comparison mercurial/hgweb/common.py @ 31793:62f9679df1f2

hgweb: extract path traversal checking into standalone function A common exploit in web applications that access paths is to insert path separator strings like ".." to try to get the server to serve up files it shouldn't. We have code for detecting this in staticfile(). A subsequent commit will need to perform this test as well. Since this is security code, let's factor the check so we don't have to reinvent the wheel.
author Gregory Szorc <gregory.szorc@gmail.com>
date Fri, 31 Mar 2017 21:47:26 -0700
parents 161a87ed456e
children d5b2beca16c0
comparison
equal deleted inserted replaced
31792:161a87ed456e 31793:62f9679df1f2
133 return os.stat(spath) 133 return os.stat(spath)
134 134
135 def get_mtime(spath): 135 def get_mtime(spath):
136 return get_stat(spath, "00changelog.i").st_mtime 136 return get_stat(spath, "00changelog.i").st_mtime
137 137
138 def ispathsafe(path):
139 """Determine if a path is safe to use for filesystem access."""
140 parts = path.split('/')
141 for part in parts:
142 if (part in ('', os.curdir, os.pardir) or
143 pycompat.ossep in part or
144 pycompat.osaltsep is not None and pycompat.osaltsep in part):
145 return False
146
147 return True
148
138 def staticfile(directory, fname, req): 149 def staticfile(directory, fname, req):
139 """return a file inside directory with guessed Content-Type header 150 """return a file inside directory with guessed Content-Type header
140 151
141 fname always uses '/' as directory separator and isn't allowed to 152 fname always uses '/' as directory separator and isn't allowed to
142 contain unusual path components. 153 contain unusual path components.
143 Content-Type is guessed using the mimetypes module. 154 Content-Type is guessed using the mimetypes module.
144 Return an empty string if fname is illegal or file not found. 155 Return an empty string if fname is illegal or file not found.
145 156
146 """ 157 """
147 parts = fname.split('/') 158 if not ispathsafe(fname):
148 for part in parts: 159 return
149 if (part in ('', os.curdir, os.pardir) or 160
150 pycompat.ossep in part or 161 fpath = os.path.join(*fname.split('/'))
151 pycompat.osaltsep is not None and pycompat.osaltsep in part):
152 return
153 fpath = os.path.join(*parts)
154 if isinstance(directory, str): 162 if isinstance(directory, str):
155 directory = [directory] 163 directory = [directory]
156 for d in directory: 164 for d in directory:
157 path = os.path.join(d, fpath) 165 path = os.path.join(d, fpath)
158 if os.path.exists(path): 166 if os.path.exists(path):