Skip to content

dirfs: reject paths that escape the root via ".."#2047

Open
sahvx655-wq wants to merge 2 commits into
fsspec:masterfrom
sahvx655-wq:dirfs-join-escape-root
Open

dirfs: reject paths that escape the root via ".."#2047
sahvx655-wq wants to merge 2 commits into
fsspec:masterfrom
sahvx655-wq:dirfs-join-escape-root

Conversation

@sahvx655-wq

Copy link
Copy Markdown
Contributor
  1. DirFileSystem is meant to keep every operation under its configured path, but _join only concatenates the prefix with the caller-supplied path and never inspects ".." segments.
  2. a relative path such as "../secret" therefore resolves above the root, and since reads and writes both funnel through _join an untrusted name escapes the directory entirely (confirmed by reading and writing a file outside the root over LocalFileSystem).

Reject in _join when the path would climb above the root; ".." that stays within the prefix is still allowed, so existing in-tree paths are unaffected.

@sahvx655-wq

Copy link
Copy Markdown
Contributor Author

gentle ping

@martindurant

Copy link
Copy Markdown
Member

Thanks, I totally missed this.

The trouble is, that ".." might be a totally legitimate path part on many filesystems - it only has special meaning on LocalFileSystem.

@sahvx655-wq

Copy link
Copy Markdown
Contributor Author

Good point. ".." really is just a literal key component on object stores, and _strip_protocol does not collapse it anyway, so a path only ever climbs out of the root when the backend resolves ".." against a real directory tree at access time. That is LocalFileSystem (and anything sitting on a real tree). My first cut rejected it unconditionally, which would have wrongly refused legitimate keys like foo/../name on the likes of memory or s3.

I have narrowed the guard so it only fires when the wrapped fs is a LocalFileSystem; every other backend keeps the old behaviour and ".." passes straight through _join. Confirmed on MemoryFileSystem that ../secret and foo/../name still round-trip as plain keys, while the local case still blocks cat("../secret.txt"). Split the tests to match: a local reject case, a local internal-".." allowed case, and a non-local case asserting ".." is left untouched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants