Skip to content

Restrict snaphsot recovery to snapshots directory, hide file contents#8341

Merged
timvisee merged 7 commits into
devfrom
restrict-snapshot-recovery
Mar 10, 2026
Merged

Restrict snaphsot recovery to snapshots directory, hide file contents#8341
timvisee merged 7 commits into
devfrom
restrict-snapshot-recovery

Conversation

@generall

@generall generall commented Mar 9, 2026

Copy link
Copy Markdown
Member

Only allow file://... snapshots to be recoverred from snapshot directly, for security reasons.

Copilot AI 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.

Pull request overview

This PR restricts snapshot recovery from file:// URLs by validating that the referenced file path is within the configured snapshots directory. This prevents potential path traversal attacks where a privileged user could trick the system into reading arbitrary files on the filesystem via the snapshot recovery API.

Changes:

  • Added validate_snapshot_path() helper in download.rs that canonicalizes both the target path and the allowed directory and uses component-wise starts_with to enforce containment.
  • Updated the download_snapshot() signature to accept a snapshots_path parameter used for file:// URL validation.
  • Updated both callers (recover.rs and snapshots.rs) to pass the appropriate snapshots directory path.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
lib/storage/src/content_manager/snapshots/download.rs Core change: adds validate_snapshot_path and updates download_snapshot signature to accept snapshots_path for file:// URL validation
lib/storage/src/content_manager/snapshots/recover.rs Passes toc.snapshots_path() (root snapshot dir) as the snapshots_path argument to download_snapshot
src/common/snapshots.rs Passes collection.snapshots_path() (collection-scoped snapshot dir) as the snapshots_path argument to download_snapshot

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +58 to +76
fn validate_snapshot_path(path: &Path, allowed_dir: &Path) -> Result<PathBuf, StorageError> {
let canonical_path = path.canonicalize().map_err(|err| {
StorageError::bad_request(format!("Failed to resolve snapshot path {path:?}: {err}"))
})?;

let canonical_allowed = allowed_dir.canonicalize().map_err(|err| {
StorageError::service_error(format!(
"Failed to resolve snapshots directory {allowed_dir:?}: {err}"
))
})?;

if !canonical_path.starts_with(&canonical_allowed) {
return Err(StorageError::bad_request(format!(
"Snapshot file path must be inside the snapshots directory {canonical_allowed:?}",
)));
}

Ok(canonical_path)
}

Copilot AI Mar 9, 2026

Copy link

Choose a reason for hiding this comment

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

The new validate_snapshot_path function implements a security-critical path traversal prevention mechanism. The adjacent download_tar.rs file has unit tests for its public function, but download.rs has none. Given that this function is the core of the security feature being introduced, it should have unit tests covering at minimum: (1) a path inside the allowed directory (should succeed), (2) a path outside the allowed directory (should fail with bad_request), (3) a path that uses .. components to escape the allowed directory (e.g., /snapshots/../etc/passwd, should fail), and (4) a non-existent allowed directory (should fail with service_error).

Copilot uses AI. Check for mistakes.
coderabbitai[bot]

This comment was marked as resolved.

@qdrant qdrant deleted a comment from coderabbitai Bot Mar 10, 2026

@timvisee timvisee left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've added some tests from #4572. Other than that this PR looks complete.

Comment thread lib/storage/src/content_manager/snapshots/download.rs Outdated
Comment thread tests/openapi/test_snapshot.py Outdated
coderabbitai[bot]

This comment was marked as resolved.

@qdrant qdrant deleted a comment from coderabbitai Bot Mar 10, 2026
Comment on lines +29 to +33
// Must hide error in release builds to not leak contents of potentially sensitive files
#[cfg(not(debug_assertions))]
format!("Malformed tar archive, unable to read entries"),
#[cfg(debug_assertions)]
format!("Malformed tar archive, unable to read entries: {err}"),

@timvisee timvisee Mar 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A core problem was that we expose the tar error message, which could leak file contents.

This now hides the tar message in release builds. It is kept in debug builds as it's still useful for development.

Alternatively we can hide the message for both build types. Any opinions?

cc @generall

@timvisee timvisee changed the title restrict snapshot recovery Restrict snaphsot recovery to snapshots directory, hide file contents Mar 10, 2026
@qdrant qdrant deleted a comment from coderabbitai Bot Mar 10, 2026
…lection_snapshot

The recover from snapshot API now requires file:// paths to be inside the
configured snapshots directory. Write the test snapshot into the peer's
snapshots directory instead of a temp file so the request is allowed.

Made-with: Cursor
coderabbitai[bot]

This comment was marked as resolved.

@qdrant qdrant deleted a comment from coderabbitai Bot Mar 10, 2026
@timvisee timvisee requested review from agourlay, coszio and xzfc March 10, 2026 15:21
@timvisee timvisee merged commit b14e0be into dev Mar 10, 2026
21 of 22 checks passed
@timvisee timvisee deleted the restrict-snapshot-recovery branch March 10, 2026 16:13
generall added a commit that referenced this pull request Mar 26, 2026
…#8341)

* restrict recovery from snapshot folder

* fmt

* clippy

* Extend test, path must be inside snapshots directory

* In release builds, hide detailed tar error message to not leak contents

* Change from bad request to forbidden

* fix(auth_tests): place snapshot in peer snapshots dir for recover_collection_snapshot

The recover from snapshot API now requires file:// paths to be inside the
configured snapshots directory. Write the test snapshot into the peer's
snapshots directory instead of a temp file so the request is allowed.

Made-with: Cursor

---------

Co-authored-by: timvisee <[email protected]>
Co-authored-by: Tim Visée <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
KShivendu pushed a commit that referenced this pull request Mar 30, 2026
…#8341)

* restrict recovery from snapshot folder

* fmt

* clippy

* Extend test, path must be inside snapshots directory

* In release builds, hide detailed tar error message to not leak contents

* Change from bad request to forbidden

* fix(auth_tests): place snapshot in peer snapshots dir for recover_collection_snapshot

The recover from snapshot API now requires file:// paths to be inside the
configured snapshots directory. Write the test snapshot into the peer's
snapshots directory instead of a temp file so the request is allowed.

Made-with: Cursor

---------

Co-authored-by: timvisee <[email protected]>
Co-authored-by: Tim Visée <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants