Skip to content

security: resolve_via_ancestors does not normalize .. in suffix, enabling sandbox bypass #2125

@bug-ops

Description

@bug-ops

Summary

resolve_via_ancestors in crates/zeph-tools/src/file.rs:587-610 does not normalize .. components in the path suffix when intermediate directories do not exist on the filesystem. This allows an attacker-controlled path to pass the starts_with(allowed_path) check at the string level, while the OS resolves it outside the sandbox boundary at open time.

Reproduction

allowed_paths = ["/tmp/sandbox"]
path = "/tmp/sandbox/nonexistent_dir/../../etc/passwd"
  1. resolve_via_ancestors walks ancestors of /tmp/sandbox/nonexistent_dir/../../etc/passwd to find the longest existing prefix
  2. It finds /tmp/sandbox exists, constructs base = /tmp/sandbox + suffix = nonexistent_dir/../../etc/passwd
  3. base.join(suffix) = /tmp/sandbox/nonexistent_dir/../../etc/passwd
  4. starts_with("/tmp/sandbox")true (string comparison on unnormalized path)
  5. OS call to open("/tmp/sandbox/nonexistent_dir/../../etc/passwd") resolves .. and opens /etc/passwd

Impact

All file operations routed through FileExecutor::execute_file_tool (read, write, edit, delete, move, copy, list, grep, find) are affected when allowed_paths is non-empty. The bypass requires that the fake intermediate directory does not exist on disk.

Location

crates/zeph-tools/src/file.rs:587-610resolve_via_ancestors function

Fix Approach

After constructing the candidate path, apply lexical normalization (collapse .. components without touching the filesystem) before the starts_with check. The path-clean crate or a manual implementation can do this.

Alternatively, call canonicalize on the fully joined path — but that requires the path to exist, which won't work for write operations on non-existent files.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-related issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions