Skip to content

sec(policy): add symlink boundary check to load_policy_file()#1893

Merged
bug-ops merged 4 commits intomainfrom
1872-policy-symlink-check
Mar 15, 2026
Merged

sec(policy): add symlink boundary check to load_policy_file()#1893
bug-ops merged 4 commits intomainfrom
1872-policy-symlink-check

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Mar 15, 2026

Summary

  • Canonicalize the policy file path before reading, eliminating a TOCTOU race window
  • Verify the canonical path stays within the process working directory (same pattern as load_instructions())
  • Add PolicyCompileError::FileEscapesRoot error variant
  • Add two unit tests: happy path (file within cwd) and symlink escape rejection

Motivation

load_policy_file() in crates/zeph-tools/src/policy.rs opened the file via the raw path, allowing a symlink inside the project to point outside the project root. load_instructions() already performs this check; this PR brings parity.

Risk: Low — policy_file is admin-controlled config. Defense in depth.

Closes #1872

Test plan

  • cargo nextest run -p zeph-tools --features policy-enforcer --lib — 728 passed
  • cargo clippy --workspace --features full -- -D warnings — clean
  • cargo +nightly fmt --check — clean
  • New tests policy_file_loaded_from_cwd_subdir and policy_file_symlink_escaping_project_root_is_rejected pass

bug-ops added 3 commits March 15, 2026 23:55
Canonicalize the policy file path before reading and verify it stays
within the process working directory, matching the same pattern used
in load_instructions(). This eliminates a TOCTOU window and prevents
symlinks inside the project from pointing to files outside the root.

Adds FileEscapesRoot error variant and two unit tests covering the
happy path and the symlink escape rejection.

Closes #1872
Both test sections coexist: symlink boundary check (this PR) and
tool alias resolution (main).
@github-actions github-actions bot added size/M Medium PR (51-200 lines) documentation Improvements or additions to documentation rust Rust code changes and removed size/M Medium PR (51-200 lines) labels Mar 15, 2026
@bug-ops bug-ops enabled auto-merge (squash) March 15, 2026 23:00
@github-actions github-actions bot added the size/M Medium PR (51-200 lines) label Mar 15, 2026
@bug-ops bug-ops merged commit 5715e50 into main Mar 15, 2026
20 checks passed
@bug-ops bug-ops deleted the 1872-policy-symlink-check branch March 15, 2026 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation rust Rust code changes size/M Medium PR (51-200 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sec(policy): load_policy_file() lacks symlink boundary check

1 participant