Skip to content

storage: keep recursive downloads inside the destination folder - #920

Merged
pierre-emmanuelJ merged 5 commits into
masterfrom
pej/sc-175532/exo-cli-path-traversal-unsanitized-object-names-upon-recursive-bucket-download-ei0162
Oct 5, 2026
Merged

pierre-emmanuelJ merged 5 commits into
masterfrom
pej/sc-175532/exo-cli-path-traversal-unsanitized-object-names-upon-recursive-bucket-download-ei0162

Conversation

@pierre-emmanuelJ

Copy link
Copy Markdown
Member

Description

exo storage download -r could still write outside of the destination folder: an object named public/../file is a valid in-bucket path, so it passed the check added in #823, but once the public/ prefix is stripped it landed one level above the destination.

  • The check is now done on the local path the object is written to, not on the object key.
  • Such objects are skipped with a warning (same behaviour as the AWS CLI); the rest of the download goes on.
  • No impact on other downloads: single-object downloads and keys that stay inside the destination behave as before.

[sc-175532]

Checklist

(For exoscale contributors)

  • Changelog updated (under Unreleased block, and add the Pull Request #number for each bit you add to the CHANGELOG.md)
  • Testing

Testing

Unit tests, plus a new e2e scenario (storage_download_path_traversal.txtar) run against a real bucket.


Note

AI assistance: code, tests, PR description.

🤖 Generated with Claude Code

pierre-emmanuelJ and others added 2 commits October 2, 2026 10:14
An object such as "public/../file" is a valid in-bucket path, but once
the "public/" prefix is stripped by a recursive download it was written
one level above the destination. Check the final local path instead of
the object key, and skip the objects that would leave the destination.

AI-assisted: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI-assisted: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pierre-emmanuelJ
pierre-emmanuelJ requested a review from a team October 2, 2026 10:15
@pierre-emmanuelJ

Copy link
Copy Markdown
Member Author

Note on os.Root, which was suggested as a safeguard: I left it out of this PR on purpose.

The check here is lexical (the final local path must stay under the destination), which is enough for the reported case. os.Root would be a stronger backstop, but it also refuses symlinks inside the destination that point outside of it, so a download into a folder with e.g. a subfolder symlinked to another disk would start failing. It also means reworking DownloadFile, which opens files by plain path today.

Happy to do it as a follow-up if we think the stricter behaviour is worth it.

…li-path-traversal-unsanitized-object-names-upon-recursive-bucket-download-ei0162

# Conflicts:
#	CHANGELOG.md

@quentinalbertone quentinalbertone left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should delete IsTraversalPath this fonctoin doesn't work and it will confuse us in the future why two check: one on the key (which doesn't work every time) the other on on the write path

pierre-emmanuelJ and others added 2 commits October 5, 2026 13:01
…li-path-traversal-unsanitized-object-names-upon-recursive-bucket-download-ei0162

# Conflicts:
#	CHANGELOG.md
The check on the local path the object is written to covers every case
the key check did, and the key check missed some. Keeping both was
confusing.

AI-assisted: true
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pierre-emmanuelJ

Copy link
Copy Markdown
Member Author

@quentinalbertone agreed, IsTraversalPath is removed along with its filter in storage download. The check on the local write path covers what it did (a unit case for ../file from the bucket root replaces its test). Unit tests and the storage_download_path_traversal e2e scenario pass. Also merged master to fix the changelog conflict.

@pierre-emmanuelJ
pierre-emmanuelJ merged commit 2857b89 into master Oct 5, 2026
7 checks passed
@pierre-emmanuelJ
pierre-emmanuelJ deleted the pej/sc-175532/exo-cli-path-traversal-unsanitized-object-names-upon-recursive-bucket-download-ei0162 branch October 5, 2026 13:47
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