Skip to content

[sandbox] Enforce protected workspace metadata paths#19846

Merged
evawong-oai merged 1 commit into
mainfrom
codex/bugb15632-policy-primitive
Apr 28, 2026
Merged

[sandbox] Enforce protected workspace metadata paths#19846
evawong-oai merged 1 commit into
mainfrom
codex/bugb15632-policy-primitive

Conversation

@evawong-oai

@evawong-oai evawong-oai commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Make FileSystemSandboxPolicy the semantic source of truth for project root metadata protection. Under writable roots, .git, .codex, and .agents stay protected unless user policy grants an explicit write rule for that metadata path.

Scope

  1. Add protected_metadata_names to WritableRoot.
  2. Teach FileSystemSandboxPolicy::can_write_path_with_cwd to reject protected metadata writes under writable roots unless explicitly allowed.
  3. Default workspace write profiles to protect .git, .codex, and .agents.
  4. Add the Linux fallback setup needed before Linux enforcement lands later in the stack.

Reviewer Focus

  1. The policy decision belongs in FileSystemSandboxPolicy, not shell command parsing.
  2. Legacy SandboxPolicy remains a compatibility projection, not the source of the new rule.
  3. Explicit user write rules can still opt into these metadata paths.

Stack

  1. Policy primitive: this PR
  2. macOS Seatbelt adapter: Enforce workspace metadata protections in Seatbelt #19847
  3. Shell preflight UX: Add workspace metadata shell preflight #19848
  4. Runtime profile propagation: Propagate runtime permission profiles #19849
  5. Linux bubblewrap adapter: Enforce workspace metadata protections in Linux sandbox #19852

Validation

  1. codex protocol permissions tests
  2. formatting for codex protocol and codex linux sandbox
  3. diff whitespace check

@evawong-oai evawong-oai force-pushed the codex/bugb15632-policy-primitive branch 5 times, most recently from a99da8c to 5b59ee3 Compare April 27, 2026 19:48
@evawong-oai evawong-oai changed the base branch from main to codex/bugb15632-windows-cmd-test-skip April 27, 2026 19:50
@evawong-oai evawong-oai force-pushed the codex/bugb15632-policy-primitive branch 2 times, most recently from 01771db to 11fa852 Compare April 27, 2026 21:14
@evawong-oai evawong-oai force-pushed the codex/bugb15632-windows-cmd-test-skip branch from 5b8dd2e to 0f697bc Compare April 27, 2026 21:14
@evawong-oai evawong-oai force-pushed the codex/bugb15632-policy-primitive branch from 11fa852 to ab4b378 Compare April 27, 2026 22:21
@evawong-oai evawong-oai changed the base branch from codex/bugb15632-windows-cmd-test-skip to main April 27, 2026 22:24
@evawong-oai evawong-oai marked this pull request as ready for review April 27, 2026 23:02

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

https://github.com/openai/codex/blob/ab4b378d678768326e770272994017d56c3b7c31/codex-rs/protocol/src/permissions.rs#L726-L728
P1 Badge Include preserved-write semantics in enforcement bridging check

needs_direct_runtime_enforcement decides compatibility by comparing semantic signatures, but preserved-name write denials are enforced in can_write_path_with_cwd and are not represented in the bridged legacy model. This can return false and skip direct enforcement even when .git/.agents/.codex writes should be blocked.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/protocol/src/permissions.rs Outdated
Comment on lines +1513 to +1519
fn first_preserved_component(path: &Path) -> Option<(AbsolutePathBuf, &'static str)> {
let mut candidate = PathBuf::new();
for component in path.components() {
candidate.push(component.as_os_str());
if let Some(preserved_name) = preserved_path_name(component.as_os_str()) {
let absolute = AbsolutePathBuf::from_absolute_path(candidate).ok()?;
return Some((absolute, preserved_name));

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.

P1 Badge Restrict preserved-name blocking to root-level metadata

can_write_path_with_cwd calls first_preserved_component, which scans all path components and denies writes whenever any component is .git, .agents, or .codex unless there is a deeper explicit rule. This blocks legitimate nested repo writes (e.g. /tmp/repo/.git/...) under otherwise writable roots, so common flows like cloning in /tmp regress.

Useful? React with 👍 / 👎.

@evawong-oai evawong-oai force-pushed the codex/bugb15632-policy-primitive branch 2 times, most recently from 8e60a50 to 0b263d0 Compare April 28, 2026 01:10
@evawong-oai evawong-oai changed the title Add preserved path policy primitive Add workspace metadata protection policy primitive Apr 28, 2026
@evawong-oai evawong-oai force-pushed the codex/bugb15632-policy-primitive branch from 0b263d0 to 3ed97f5 Compare April 28, 2026 03:15
@evawong-oai

Copy link
Copy Markdown
Contributor Author

Fixed in 3ed97f5.

The active contract is now represented on WritableRoot.protected_metadata_names. get_writable_roots_with_cwd() fills that field for .git, .agents, and .codex, and explicit writable rules for those paths remove the matching name from the parent root.

needs_direct_runtime_enforcement() now checks that field before the legacy semantic comparison. If legacy SandboxPolicy cannot express the metadata protection contract, we keep the direct enforcement path. That removes the hidden dependency on can_write_path_with_cwd() for the bridge decision.

The stale inline nested repo concern is also covered by the current contract. This only protects the first component under each writable root, so normal nested work below a regular directory is not blocked by this field.

Validation passed locally, and the devbox 46 case suite passed with just c sandbox linux at 8cf759200dd2355df3a76fbfcb0dc9f8619f33f5.

Comment thread codex-rs/linux-sandbox/src/bwrap.rs

@viyatb-oai viyatb-oai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can you preserve comments from the old code?

@viyatb-oai viyatb-oai changed the title Add workspace metadata protection policy primitive [sandbox] Enforce protected workspace metadata paths Apr 28, 2026
@evawong-oai evawong-oai merged commit 0156b1e into main Apr 28, 2026
25 checks passed
@evawong-oai evawong-oai deleted the codex/bugb15632-policy-primitive branch April 28, 2026 16:10
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants