From 03e08eb0a8f4f9499d246fe29ec6e4cd0b7190fe Mon Sep 17 00:00:00 2001 From: tomaioo Date: Wed, 27 May 2026 11:28:19 -0700 Subject: [PATCH] fix(security): path traversal in rooteddirfilesystem._join The `RootedDirFileSystem._join` method attempts to prevent path traversal by checking if the resolved path starts with the root path. However, the check uses `os.path.normpath` and `make_path_posix` which may not handle all path traversal techniques. Specifically, the `startswith` check is vulnerable if the root path is not properly normalized or if an attacker can craft a path that passes the check but still escapes the root. More critically, the `startswith` check with `root_posix` can be bypassed if the root path doesn't end with a separator and the path contains the root as a prefix. For example, if root is `/data/storage`, a path like `/data/storage_extra` would pass the startswith check but is outside the intended directory. Additionally, the method doesn't handle symlink attacks or race conditions. Signed-off-by: tomaioo <203048277+tomaioo@users.noreply.github.com> --- fs_storage/rooted_dir_file_system.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs_storage/rooted_dir_file_system.py b/fs_storage/rooted_dir_file_system.py index 91bae52200..0da5edd36f 100644 --- a/fs_storage/rooted_dir_file_system.py +++ b/fs_storage/rooted_dir_file_system.py @@ -27,7 +27,9 @@ def _join(self, path): # we need to normalize the path separator. path_posix = os.path.normpath(make_path_posix(path)) root_posix = os.path.normpath(make_path_posix(self.path)) - if not path_posix.startswith(root_posix): + if not root_posix.endswith("/"): + root_posix = root_posix + "/" + if path_posix != root_posix.rstrip("/") and not path_posix.startswith(root_posix): raise PermissionError( f"Path {path} is not a subpath of the root path {self.path}" )