Skip to content

ci: pin workspace root in apply_patch move-traversal test for Bazel hermetic sandbox#282

Merged
andrei-hasna merged 1 commit into
mainfrom
ci/fix-apply-patch-bwrap
Jul 20, 2026
Merged

ci: pin workspace root in apply_patch move-traversal test for Bazel hermetic sandbox#282
andrei-hasna merged 1 commit into
mainfrom
ci/fix-apply-patch-bwrap

Conversation

@andrei-hasna

Copy link
Copy Markdown
Contributor

Root cause

suite::apply_patch_cli::apply_patch_cli_rejects_move_path_traversal_outside_workspace
fails under the Bazel hermetic CI sandbox with:

apply_patch verification failed: Failed to read file to update /tmp/.tmpXXXX/stay.txt:
fs sandbox helper failed with status exit status: 1:
bwrap: Can't mount tmpfs on /newroot/home/runner/work/codewith/codewith/codex-rs/.git:
No such file or directory

This is an environment-specific test-setup bug, not a defect in the
path-traversal guard:

  • Unlike the sibling Add File traversal test (which the guard rejects before
    touching the filesystem), this case does an Update File on a file that lives
    inside the workspace, so the Linux fs sandbox reads it through bubblewrap
    before the traversal guard rejects the outside Move to.
  • Before that read, the sandbox masks the workspace .git metadata directory
    read-only (an empty read-only tmpfs when .git is missing).
  • The shared harness prepare_config overrides config.cwd to a fresh temp dir
    but leaves config.workspace_roots pointing at the process's build-time
    working directory
    (resolved_cwd = std::env::current_dir() at config build).
    So the permission profile materializes :workspace_roots against that
    directory, and the .git mask is applied to <process-cwd>/.git.
  • On a developer machine <process-cwd> is the repo checkout with a real .git,
    so the mask binds an existing path and the bug is hidden. Under the Bazel
    hermetic sandbox, <process-cwd> = /home/runner/work/codewith/codewith/codex-rs
    has no .git (VCS metadata is stripped) and no ancestor with git metadata,
    so should_leave_missing_git_for_parent_repo_discovery does not skip it and
    bwrap is told to --tmpfs <cwd>/.git, which it cannot set up in the hermetic
    new-root → the in-workspace read aborts before the guard runs.

Fix

Pin the test's workspace root to the harness cwd (a writable temp dir) so the
.git mask always targets an existing, writable location regardless of the
process working directory:

let harness = apply_patch_harness_with(|builder| {
    builder.with_config(|config| {
        let workspace_roots = vec![config.cwd.clone()];
        config.workspace_roots = workspace_roots.clone();
        config.permissions.set_workspace_roots(workspace_roots);
    })
})
.await?;

This mirrors the established harness pattern (see
core/tests/suite/permissions_messages.rs) and is scoped to the test only. The
real path-traversal protection is unchanged: the test still reads stay.txt,
still attempts the Move to: ../escape-move.txt, and still asserts the
"writing outside of the project; rejected by user approval settings" rejection
plus that the escape file is never created.

Scope / coordination

  • Test-only change (codex-rs/core/tests/suite/apply_patch_cli.rs); no
    production apply_patch / worktree-guard / bwrap logic touched, to avoid
    conflicts with the in-flight OCW worktree-guard fixes (OPE80-00009/10/11) on
    fix/ocw-worktree-guard.
  • Latent harness bug noted: prepare_config leaks the build-time process CWD
    into config.workspace_roots; a follow-up could sync it centrally so future
    sandboxed read tests do not need the per-test workaround.

Verification

E2B is a different (non-hermetic) sandbox than Bazel's bwrap-hermetic sandbox,
so it does not reproduce the original failure; the remote build confirms
codex-core (incl. this test) compiles and the crate's tests build/run. Final
green can only be confirmed by the real Bazel CI.

@andrei-hasna

Copy link
Copy Markdown
Contributor Author

E2B verification (E2B is a different, non-hermetic sandbox than Bazel's bwrap-hermetic sandbox, so it does not reproduce the original .git tmpfs failure):

  • Compile-only check on a fresh box: PASS (cargo check finished dev profile in 6m49s, exit 0) — the edited test crate type-checks cleanly.
  • An earlier full cargo test --no-run run reached the final link step with 0 rustc errors (all crates incl. libcore_test_support/libcodex_core compiled); it only failed with No space left on device + ld ... signal 7 [Bus error] — shared-box disk exhaustion from concurrent fleet builds, not a code error.

Net: the change compiles. Final green can only be confirmed by the real Bazel CI.

@andrei-hasna
andrei-hasna force-pushed the ci/fix-apply-patch-bwrap branch from ffcee40 to 35d1889 Compare July 20, 2026 13:46
…andbox

The apply_patch move-traversal test reads an in-workspace file through the
Linux fs sandbox (bubblewrap) before the traversal guard rejects the outside
move. Before reading, the sandbox masks the workspace `.git` metadata dir
read-only. The shared test harness (`prepare_config`) overrides `config.cwd`
to a temp dir but leaves `config.workspace_roots` pointing at the process's
build-time working directory. On a dev machine that is the repo checkout with
a real `.git`, so the mask binds an existing path; under the Bazel hermetic
CI sandbox that directory has no `.git`, so bwrap fails to set up the `.git`
tmpfs mask ("Can't mount tmpfs on .../.git: No such file or directory") and
the read aborts before the guard runs.

Pin the test's workspace root to the harness cwd (a writable temp dir) so the
`.git` mask always targets an existing, writable location regardless of the
process working directory. This keeps the real path-traversal protection
intact and only fixes the environment-specific test setup.
@andrei-hasna
andrei-hasna force-pushed the ci/fix-apply-patch-bwrap branch from 35d1889 to 633d484 Compare July 20, 2026 14:10
@andrei-hasna
andrei-hasna merged commit c098fcd into main Jul 20, 2026
21 of 26 checks passed
@andrei-hasna
andrei-hasna deleted the ci/fix-apply-patch-bwrap branch July 20, 2026 14:19
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 20, 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.

1 participant