Commit 03936e1d by btimby

Use explicit exception to detect invalid path (BackReferenceError), see comments on r773

parent 34c78ce2
...@@ -188,6 +188,11 @@ class NoMMapError(ResourceError): ...@@ -188,6 +188,11 @@ class NoMMapError(ResourceError):
default_message = "Can't get mmap for %(path)s" default_message = "Can't get mmap for %(path)s"
class BackReferenceError(FSError, ValueError):
"""Exception raised when too many backrefs exist in a path (ex: '/..', '/docs/../..')."""
default_message = "Too many backrefs in '%(path)s'"
def convert_fs_errors(func): def convert_fs_errors(func):
"""Function wrapper to convert FSError instances into OSError.""" """Function wrapper to convert FSError instances into OSError."""
@wraps(func) @wraps(func)
......
...@@ -185,12 +185,10 @@ class SFTPServerInterface(paramiko.SFTPServerInterface): ...@@ -185,12 +185,10 @@ class SFTPServerInterface(paramiko.SFTPServerInterface):
def canonicalize(self, path): def canonicalize(self, path):
try: try:
return abspath(normpath(path)).encode(self.encoding) return abspath(normpath(path)).encode(self.encoding)
except ValueError, e: except BackReferenceError:
# If the client tries to use backrefs to escape root, gently # If the client tries to use backrefs to escape root, gently
# nudge them back to /. # nudge them back to /.
if 'too many backrefs' in e.args[0]: return '/'
return '/'
raise
@report_sftp_errors @report_sftp_errors
def chattr(self, path, attr): def chattr(self, path, attr):
......
...@@ -33,31 +33,34 @@ def normpath(path): ...@@ -33,31 +33,34 @@ def normpath(path):
>>> normpath("foo/../../bar") >>> normpath("foo/../../bar")
Traceback (most recent call last) Traceback (most recent call last)
... ...
ValueError: too many backrefs in path 'foo/../../bar' BackReferenceError: Too many backrefs in 'foo/../../bar'
""" """
if path in ('', '/'): if path in ('', '/'):
return path return path
path = path.replace('\\', '/') path = path.replace('\\', '/')
# An early out if there is no need to normalize this path # An early out if there is no need to normalize this path
if not _requires_normalization(path): if not _requires_normalization(path):
return path.rstrip('/') return path.rstrip('/')
components = [] components = []
append = components.append append = components.append
special = ('..', '.', '').__contains__ special = ('..', '.', '').__contains__
try: try:
for component in path.split('/'): for component in path.split('/'):
if special(component): if special(component):
if component == '..': if component == '..':
components.pop() components.pop()
else: else:
append(component) append(component)
except IndexError: except IndexError:
raise ValueError("too many backrefs in path '%s'" % path) # Imported here because errors imports this module (path),
# causing a circular import.
from fs.errors import BackReferenceError
BackReferenceError(details={ 'path': path })
if path[0] == '/': if path[0] == '/':
return '/%s' % '/'.join(components) return '/%s' % '/'.join(components)
return '/'.join(components) return '/'.join(components)
...@@ -77,7 +80,8 @@ def iteratepath(path, numsplits=None): ...@@ -77,7 +80,8 @@ def iteratepath(path, numsplits=None):
return path.split('/') return path.split('/')
else: else:
return path.split('/', numsplits) return path.split('/', numsplits)
def recursepath(path, reverse=False): def recursepath(path, reverse=False):
"""Returns intermediate paths from the root to the given path """Returns intermediate paths from the root to the given path
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment