Skip to content

Reserve missing preserved paths in Linux sandbox policy#18446

Open
evawong-oai wants to merge 5 commits intomainfrom
codex/bugb15632_missing_protected_paths_v2
Open

Reserve missing preserved paths in Linux sandbox policy#18446
evawong-oai wants to merge 5 commits intomainfrom
codex/bugb15632_missing_protected_paths_v2

Conversation

@evawong-oai
Copy link
Copy Markdown
Contributor

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

Summary

This PR protects preserved workspace metadata paths under workspace write sandboxes without breaking normal Git discovery. The preserved paths are .git, .codex, and .agents.

BUGB 15632 tracks the Bugcrowd submission.

Root Cause

The previous policy only treated .git and .agents as protected after they already existed. That left a first run gap where sandboxed code in a new workspace could create .git/config.

The report used git init followed by a Git config value that could persist on the host. A later host side Git command could then load attacker controlled repo metadata.

The hard part is that missing .git cannot always be handled the same way as .codex and .agents. If Codex runs inside a subdirectory of an existing Git repo or worktree, materializing a child .git changes Git discovery and hides the valid parent repo. In that case the child .git must stay absent, and creation must be blocked before the sandboxed command runs.

Design

  1. The protocol layer centralizes preserved workspace metadata names and derives default read only carveouts for writable roots.
  2. Existing .git, .codex, and .agents paths stay read only under default writable roots.
  3. Missing .codex and .agents are reserved under writable roots so first creation is blocked.
  4. Missing .git is reserved only when doing so does not break Git discovery.
  5. Missing child .git under a valid parent repo or worktree stays absent so normal git status and git rev parse keep discovering the parent.
  6. Preserved path write detection lives in the shell command crate and is reused by both codex sandbox and normal Codex shell tool execution.
  7. That shared guard blocks preserved path writes that cannot be represented safely as sandbox mounts. This covers git init, common file commands, and literal shell redirection targets.
  8. Linux bwrap setup masks representable missing preserved paths with /dev/null, treats transient empty preserved files as synthetic when another active sandbox owns them, and cleans synthetic placeholders after bwrap exits.
  9. The Linux launcher keeps a parent process only when cleanup is needed, forwards termination signals to the bwrap child process group, clears the child pid after wait, and then runs cleanup.
  10. The bwrap mount helper state is held in one local context so preserved files and synthetic cleanup targets stay in sync.

Reviewer Collaboration

  1. Viyat found that child .git materialization broke parent repo discovery. The PR now leaves child .git absent under a valid ancestor repo or worktree and blocks creation through the command guard.
  2. Viyat found that overlapping Linux sandboxes could leave zero byte .git, .codex, and .agents placeholders. The PR now treats empty preserved paths with active synthetic ownership as synthetic and removes them when the last owner exits, while preserving real prior empty files.
  3. Michael pointed out that the codex sandbox entrypoint had too much preserved path policy logic. The latest head moves that logic into shared shell command code and keeps the debug sandbox entrypoint as a caller of the shared guard.

Validation

Current PR head: f8f8584923d3e1b9d9e19ffb1d67cb60de96a876.

  1. GitHub Actions is green on the current head, including Rust CI, Bazel, Blob size policy, Codespell, cargo deny, SDK, and CLA.
  2. Earlier local validation passed formatting, diff whitespace, the focused protocol permission tests with Cargo and Bazel, the full protocol crate test suite with Cargo, and the full Bazel protocol unit target.
  3. Latest Linux devbox validation passed native cases 1 through 45 on the current head.
  4. Latest Linux devbox validation also passed Viyat's parent repo subdirectory workflow when run as the real split flow: parent Git discovery works, git init and mkdir .codex are blocked, normal JSONL viewer coding works, and no .git, .codex, or .agents placeholders remain afterward.
  5. The latest push only moves preserved path command detection out of codex sandbox and into shared shell command code, then wires the CLI and core shell paths through the same guard.

@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 6 times, most recently from 30deabb to e4249a5 Compare April 20, 2026 16:55
@evawong-oai evawong-oai changed the title [Linux] Reserve missing top level .git at runtime Reserve missing top level .git in sandbox policy Apr 20, 2026
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from e4249a5 to f85a396 Compare April 20, 2026 17:11
@evawong-oai
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown
Contributor

Codex Review: Didn't find any major issues. Nice work!

ℹ️ 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".

@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 3 times, most recently from 30d621d to 042c0ba Compare April 20, 2026 21:01
@evawong-oai evawong-oai changed the title Reserve missing top level .git in sandbox policy Reserve missing project metadata in sandbox policy Apr 20, 2026
@evawong-oai
Copy link
Copy Markdown
Contributor Author

evawong-oai commented Apr 20, 2026

Reviewer evidence update.

I extended the same preserved path rule to missing .agents. The existing .agents directory was already protected when present. The missing case now follows .git and .codex for the active workspace root, while explicit user write rules still win.

I also reran linked worktree validation on a Linux devbox with system bwrap installed at /usr/bin/bwrap, version 0.9.0. The .git worktree pointer case passed. Normal writes inside the worktree succeeded. Overwriting .git, replacing .git, and writing through the resolved gitdir were blocked. The pointer file stayed unchanged and no .git directory was created.

I fixed the devbox validation gap where blocked missing .codex and .agents attempts left zero byte host files. The Linux bwrap path now tracks synthetic missing mount targets, runs bwrap in a child only when cleanup is needed, removes only zero byte synthetic targets after preflight and the real run, then preserves the bwrap exit status.

The rerun passed on the Linux devbox with system bwrap at /usr/bin/bwrap, version 0.9.0, against commit 87f8ccb2bc22abb0e2c403da30866ae014941ae7. Normal checkout and linked worktree cases passed for .codex, .git, and .agents. Missing .codex and .agents writes were blocked and left no host side file or directory immediately after the sandbox command returned. Normal writes still succeeded, the linked .git pointer stayed unchanged, and resolved gitdir writes stayed blocked. The validation transcript did not contain the previous proc mount setup error.

I also locally passed sandboxing crate tests, protocol crate tests, format check, and whitespace diff check.

The branch remains one commit, now 87f8ccb2bc22abb0e2c403da30866ae014941ae7.

@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from 042c0ba to 18885a0 Compare April 20, 2026 21:10
@evawong-oai evawong-oai changed the title Reserve missing project metadata in sandbox policy Reserve missing preserved paths in sandbox policy Apr 20, 2026
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 3 times, most recently from 2daf148 to bd1520f Compare April 20, 2026 21:34
@evawong-oai evawong-oai changed the title Reserve missing preserved paths in sandbox policy Reserve missing preserved paths in Linux sandbox policy Apr 20, 2026
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 6 times, most recently from fb6fcdf to e76236e Compare April 20, 2026 23:40
@evawong-oai evawong-oai changed the title Reserve missing preserved paths in Linux sandbox policy Reserve missing preserved paths in Linux/Mac sandbox policy Apr 21, 2026
@evawong-oai evawong-oai changed the title Reserve missing preserved paths in Linux/Mac sandbox policy Reserve missing preserved paths in Linux sandbox policy Apr 21, 2026
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from be03010 to 0cbf816 Compare April 21, 2026 19:02
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be03010efb

ℹ️ 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/linux-sandbox/src/linux_run_main.rs
Comment thread codex-rs/protocol/src/permissions.rs Outdated
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 6 times, most recently from 68debc4 to 260e53d Compare April 22, 2026 01:29
Comment thread codex-rs/linux-sandbox/src/linux_run_main.rs
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 4 times, most recently from 13dff2c to 16eeec0 Compare April 23, 2026 15:39
Comment thread codex-rs/protocol/src/permissions.rs Outdated
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch 2 times, most recently from 06ad689 to d4502ef Compare April 23, 2026 18:52
@evawong-oai evawong-oai requested a review from a team as a code owner April 23, 2026 18:52
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from d4502ef to 7681996 Compare April 23, 2026 19:39
@viyatb-oai viyatb-oai enabled auto-merge (squash) April 23, 2026 21:40
evawong-oai and others added 3 commits April 24, 2026 08:11
Represent missing .agents and .codex preserved path masks as read-only empty directories instead of empty files so Git ignores them while cleanup still removes the host mount targets after bwrap exits.

Co-authored-by: Codex <noreply@openai.com>
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from 374d917 to 10fa02e Compare April 24, 2026 15:32
Co-authored-by: Codex <noreply@openai.com>
@evawong-oai evawong-oai force-pushed the codex/bugb15632_missing_protected_paths_v2 branch from 10fa02e to fa603bb Compare April 24, 2026 15:53
Comment thread codex-rs/cli/src/debug_sandbox.rs Outdated
handle_exit_status(status);
}

fn preserved_path_write_forbidden_reason(
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.

Something feels off: this the logic that powers codex sandbox. This file should be small, as it should be calling into existing library code. This seems off.

Comment thread codex-rs/cli/src/debug_sandbox.rs Outdated
Comment on lines +338 to +339
"git" => git_init_preserved_path_write(command, cwd, file_system_sandbox_policy),
"touch" | "mkdir" | "rm" | "rmdir" | "ln" | "mv" | "cp" | "install" => command
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.

Why are we special-casing things?

Evyatar108 pushed a commit to Evyatar108/codex-patched that referenced this pull request Apr 24, 2026
…factors

F-1: SANDBOX PATCH send_add_credits_nudge_email_inner -> early auth-required
error. Upstream openai#18446 added a new /backend-api/add_credits_nudge_email call;
copilot-api does not use it. backend_credit_type kept #[allow(dead_code)].

F-3: Reconcile type-level drift against new codex-model-provider crate:

- codex-api/src/auth.rs: add on_unauthorized(&self) default no-op method on
  AuthProvider trait so Copilot retry path in core/src/client.rs can trigger
  invalidation through dyn AuthProvider.
- codex-api/src/api_bridge.rs: switch CoreAuthProvider to impl
  crate::auth::AuthProvider (the upstream trait), and move on_unauthorized
  into the trait impl.
- codex-api/src/lib.rs: pub use CoreAuthProvider + AuthProvider as
  ApiAuthProvider (alias for pre-refactor call sites).
- core/src/client.rs:
  - ModelClientState.provider: ModelProviderInfo -> SharedModelProvider so
    .auth(), .api_provider(), .api_auth(), .auth_manager() trait methods
    resolve through create_model_provider(...).
  - ModelClient::new stores the wrapped provider (fixes stray 'provider,'
    shorthand).
  - is_copilot() uses .info().is_copilot().
  - current_client_setup: Copilot branch re-inlined — build CopilotHeaderSource
    from the session-scoped OnceLock<Arc<CopilotAuth>> and wrap in
    CoreAuthProvider.with_copilot. Non-Copilot branch unchanged.
- core/src/copilot_transport.rs: build_copilot_client now takes
  SharedAuthProvider and returns ResponsesClient<T> (upstream collapsed the
  auth generic on ResponsesClient).
- core/src/compact_remote.rs: turn_context.provider now SharedModelProvider;
  use .info().is_copilot().
- cli/src/responses_cmd.rs: wrap CoreAuthProvider in Arc<dyn AuthProvider> via
  SharedAuthProvider alias to match the new build_copilot_client signature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants