diff --git a/CHANGES b/CHANGES index bc554652..2bedbd15 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,8 @@ Version 0.12 - the cli command now responds to `--version`. - Mimetype guessing for ``send_file`` has been removed, as per issue ``#104``. See pull request ``#1849``. +- Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` + (pull request ``#1730``). Version 0.11.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index e42a6a3c..ff660d72 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -563,8 +563,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, return rv -def safe_join(directory, filename): - """Safely join `directory` and `filename`. +def safe_join(directory, *pathnames): + """Safely join `directory` and zero or more untrusted `pathnames` + components. Example usage:: @@ -574,20 +575,23 @@ def safe_join(directory, filename): with open(filename, 'rb') as fd: content = fd.read() # Read and process the file content... - :param directory: the base directory. - :param filename: the untrusted filename relative to that directory. - :raises: :class:`~werkzeug.exceptions.NotFound` if the resulting path - would fall out of `directory`. + :param directory: the trusted base directory. + :param pathnames: the untrusted pathnames relative to that directory. + :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed + paths fall out of its boundaries. """ - filename = posixpath.normpath(filename) - for sep in _os_alt_seps: - if sep in filename: + for filename in pathnames: + if filename != '': + filename = posixpath.normpath(filename) + for sep in _os_alt_seps: + if sep in filename: + raise NotFound() + if os.path.isabs(filename) or \ + filename == '..' or \ + filename.startswith('../'): raise NotFound() - if os.path.isabs(filename) or \ - filename == '..' or \ - filename.startswith('../'): - raise NotFound() - return os.path.join(directory, filename) + directory = os.path.join(directory, filename) + return directory def send_from_directory(directory, filename, **options):