Skip to content

fix(sandbox): add deny-first Seatbelt rules for well-known secret paths#3103

Merged
bug-ops merged 1 commit intomainfrom
3086-sandbox-deny-secret-paths
Apr 17, 2026
Merged

fix(sandbox): add deny-first Seatbelt rules for well-known secret paths#3103
bug-ops merged 1 commit intomainfrom
3086-sandbox-deny-secret-paths

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented Apr 17, 2026

Summary

  • PR fix(sandbox): fix Seatbelt workspace profile and add wizard/migrate support #3085 replaced per-path file-read rules with a global (allow file-read*) to fix dylib loading from the macOS DYLD shared cache. This inadvertently made credential files readable by sandboxed bash.
  • Since Seatbelt applies the last matching rule, adding deny rules after the global allow closes the gap without breaking dylib loading.
  • Adds 37 deny rules across SECRET_DIRS (26 entries, subpath) and SECRET_FILES (11 entries, literal), covering SSH, AWS, GCP, Azure, GnuPG, Docker, Kubernetes, git credentials, AI agent caches (.claude, .anthropic, .codex), Zeph age vault (.config/zeph), shell histories, npm/pypi/cargo credentials, HashiCorp vault token, and more.

Changes

  • crates/zeph-tools/src/sandbox/macos.rs: SECRET_DIRS/SECRET_FILES consts, push_secret_deny_rules_for_home(), refactored generate_sb_profile_for_home() inner function, fail-closed on home_dir() == None, deny rules applied to all non-Off profiles, 11 new unit tests
  • specs/010-security/spec.md: new "Deny-First Secret Paths (sandbox/macos: add deny-first rules for well-known secret paths #3086)" subsection with mechanism, path table, non-goals, key invariants
  • CHANGELOG.md: entry under [Unreleased] ### Security

Test plan

  • cargo nextest run -p zeph-tools --lib — 1029 tests pass
  • test_deny_ordering — deny rules appear after (allow file-read*) in generated profile
  • test_readonly_has_deny_rules / test_network_allow_all_has_deny_rules — all non-Off profiles covered
  • home_path_with_quotes_is_escaped — Seatbelt injection via hostile HOME path prevented
  • all_37_deny_rules_emitted — full deny list count verified
  • Manual: sandbox-exec -f <workspace.sb> /bin/bash -c "cat ~/.ssh/id_rsa" → denied
  • Manual: sandbox-exec -f <workspace.sb> /bin/bash -c "echo hello" → succeeds

Follow-up

Security audit identified additional long-tail paths (browser profiles, IaC tokens, cloud CLIs) deferred to a separate issue.

Closes #3086

@github-actions github-actions Bot added bug Something isn't working size/L Large PR (201-500 lines) documentation Improvements or additions to documentation rust Rust code changes and removed bug Something isn't working size/L Large PR (201-500 lines) labels Apr 17, 2026
@bug-ops bug-ops enabled auto-merge (squash) April 17, 2026 12:41
PR #3085 replaced per-path file-read rules with a global (allow file-read*)
to fix dylib loading. This inadvertently allowed sandboxed bash to read
credential files. Since Seatbelt applies the last matching rule, adding
deny rules after the global allow closes the gap.

Changes:
- Add SECRET_DIRS (26 entries, subpath) and SECRET_FILES (11 entries, literal)
  covering SSH, AWS, GCP, Azure, GnuPG, Docker, Kubernetes, git credentials,
  AI agent caches (.claude, .anthropic, .codex), Zeph age vault (.config/zeph),
  shell histories, npm/pypi/cargo credentials, HashiCorp vault token, and more
- Extract generate_sb_profile_for_home(policy, home) as pure inner function;
  generate_sb_profile() fails closed (SandboxError::Policy + warn!) when
  dirs::home_dir() returns None
- Apply deny rules to all non-Off profiles (ReadOnly gap fixed)
- Add 11 unit tests covering ordering invariant, all profile types,
  user allow_read override, quote escaping, and 37-rule count assertion
- Document mechanism, non-goals, and key invariants in specs/010-security/spec.md
- Add CHANGELOG entry under [Unreleased] ### Security

Closes #3086
@bug-ops bug-ops force-pushed the 3086-sandbox-deny-secret-paths branch from 5aa327a to 226f855 Compare April 17, 2026 12:41
@github-actions github-actions Bot added bug Something isn't working size/L Large PR (201-500 lines) labels Apr 17, 2026
@bug-ops bug-ops merged commit 079d608 into main Apr 17, 2026
32 checks passed
@bug-ops bug-ops deleted the 3086-sandbox-deny-secret-paths branch April 17, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation rust Rust code changes size/L Large PR (201-500 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sandbox/macos: add deny-first rules for well-known secret paths

1 participant