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): |