63 Commits

  • [codex] Allow socketpair in proxy-routed Linux sandbox (#26625)
    ## Summary
    
    - allow `socketpair(AF_UNIX, ...)` in the proxy-routed Linux seccomp
    mode
    - continue denying `socket(AF_UNIX, ...)` so user commands cannot create
    pathname or abstract Unix sockets
    - extend the managed-proxy integration test to verify both behaviors
    
    ## Root cause
    
    `NetworkSeccompMode::ProxyRouted` treated anonymous Unix socket pairs
    like externally addressable Unix sockets and returned `EPERM`. This
    breaks tools that use socket pairs for local child-process IPC even
    though a socket pair cannot connect outside the sandbox or bypass the
    routed proxy.
    
    `dangerously_allow_all_unix_sockets` controls Unix-socket requests
    forwarded by the managed network proxy; it does not currently configure
    the Linux seccomp filter. Socket pairs should not require that dangerous
    setting because they are unnamed, process-local IPC.
    
    Related but independent: #26553 fixes host proxy bridge socket path
    length handling.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Fix long proxy socket paths (#26553)
    ## Summary
    
    - avoid generating host proxy bridge Unix socket paths that exceed
    Linux's `sockaddr_un.sun_path` limit
    - fall back from a long `$CODEX_HOME/tmp` path to the system temp
    directory, then `/tmp`
    - add focused unit coverage for short and overlong parent paths
    
    ## Root cause
    
    With a sufficiently long `CODEX_HOME`, the generated
    `proxy-route-*.sock` path exceeds Linux's 107-byte pathname limit. The
    host bridge child exits before writing its readiness byte, so the parent
    reports the indirect error `failed to prepare host proxy routing bridge:
    failed to fill whole buffer`.
    
    ## Validation
    
    - reproduced the original error with a long `CODEX_HOME` using
    `codex-cli 0.138.0-alpha.4`
    - `cargo clippy -p codex-linux-sandbox --all-targets`
    - `just fix -p codex-linux-sandbox`
    - `just fmt`
    
    The Linux-only unit test could not execute locally: the arm64 Docker
    build was repeatedly OOM-killed by `rustc` while compiling an unrelated
    `codex-app-server-protocol` dependency, before reaching the test.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • runtime: use install context for bundled bwrap (#23634)
    ## Summary
    
    The Linux sandbox should find bundled `bwrap` through the same
    package-layout abstraction as the rest of the runtime, instead of
    maintaining a separate standalone-specific lookup path.
    
    This adds an `InstallContext` helper for bundled resources and updates
    `codex-linux-sandbox` to ask the current install context for
    `codex-resources/bwrap` before falling back to the old
    executable-relative probes. The tests cover npm-style, standalone, and
    canonical package layouts so `bwrap` lookup follows the package
    structure introduced earlier in the stack.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-linux-sandbox --lib`
    - `just fix -p codex-install-context -p codex-linux-sandbox`
    - `just bazel-lock-check`
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23634).
    * #23638
    * #23637
    * #23636
    * #23635
    * __->__ #23634
  • Make deny canonical for filesystem permission entries (#23493)
    ## Why
    Filesystem permission profiles used `none` for deny-read entries, which
    is less direct than the action the entry actually represents. This
    change makes `deny` the canonical filesystem permission spelling while
    preserving compatibility for older configs that still send `none`.
    
    ## What changed
    - rename `FileSystemAccessMode::None` to `Deny`
    - serialize and generate schemas with `deny` as the canonical value
    - retain `none` only as a legacy input alias for temporary config
    compatibility
    - update filesystem glob diagnostics and regression coverage to use the
    canonical spelling
    - refresh config and app-server schema fixtures to match the new wire
    shape
    
    ## Validation
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-core config_toml_deserializes_permission_profiles
    --lib`
    - `cargo test -p codex-core
    read_write_glob_patterns_still_reject_non_subpath_globs --lib`
    
    Earlier in the session, a broad `cargo test -p codex-core` run reached
    unrelated pre-existing failures in timing/snapshot/git-info tests under
    this environment; the targeted surfaces touched by this PR passed
    cleanly.
  • linux-sandbox: use standalone bundled bwrap (#21255)
    **Summary**
    - Add `codex-bwrap`, a standalone `bwrap` binary built from the existing
    vendored bubblewrap sources.
    - Remove the linked vendored bwrap path from `codex-linux-sandbox`;
    runtime now prefers system `bwrap` and falls back to bundled
    `codex-resources/bwrap`.
    - Add bundled SHA-256 verification with missing/all-zero digest as the
    dev-mode skip value, then exec the verified file through
    `/proc/self/fd`.
    - Keep `launcher.rs` focused on choosing and dispatching the preferred
    launcher. Bundled lookup, digest verification, and bundled exec now live
    in `linux-sandbox/src/bundled_bwrap.rs`; Bazel runfiles lookup lives in
    `linux-sandbox/src/bazel_bwrap.rs`; shared argv/fd exec helpers live in
    `linux-sandbox/src/exec_util.rs`.
    - Teach Bazel tests to surface the Bazel-built `//codex-rs/bwrap:bwrap`
    through `CARGO_BIN_EXE_bwrap`; `codex-linux-sandbox` only honors that
    fallback in debug Bazel runfiles environments so release/user runtime
    lookup stays tied to `codex-resources/bwrap`.
    - Allow `codex-exec-server` filesystem helpers to preserve just the
    Bazel bwrap/runfiles variables they need in debug Bazel builds, since
    those helpers intentionally rebuild a small environment before spawning
    `codex-linux-sandbox`.
    - Verify the Bazel bwrap target in Linux release CI with a build-only
    check. Running `bwrap --version` is too strong for GitHub runners
    because bubblewrap still attempts namespace setup there.
    
    **Verification**
    - Latest update: `cargo test -p codex-linux-sandbox`
    - Latest update: `just fix -p codex-linux-sandbox`
    - `cargo check --target x86_64-unknown-linux-gnu -p codex-linux-sandbox`
    could not run locally because this macOS machine does not have
    `x86_64-linux-gnu-gcc`; GitHub Linux Bazel CI is expected to cover the
    Linux-only modules.
    - Earlier in this PR: `cargo test -p codex-bwrap`
    - Earlier in this PR: `cargo test -p codex-exec-server`
    - Earlier in this PR: `cargo check --release -p codex-exec-server`
    - Earlier in this PR: `just fix -p codex-linux-sandbox -p
    codex-exec-server`
    - Earlier in this PR: `bazel test --nobuild
    //codex-rs/linux-sandbox:linux-sandbox-all-test
    //codex-rs/core:core-all-test
    //codex-rs/exec-server:exec-server-file_system-test
    //codex-rs/app-server:app-server-all-test` (analysis completed; Bazel
    then refuses to run tests under `--nobuild`)
    - Earlier in this PR: `bazel build --nobuild //codex-rs/bwrap:bwrap`
    - Prior to this update: `just bazel-lock-update`, `just
    bazel-lock-check`, and YAML parse check for
    `.github/workflows/bazel.yml`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/21255).
    * #21257
    * #21256
    * __->__ #21255
  • fix(linux-sandbox): isolate Linux sandbox synthetic mount registry per user for shared codex use case (#21234)
    ## Summary
    - make the Linux sandbox synthetic mount registry path unique per
    effective UID
    - keep same-user coordination intact while avoiding collisions between
    users sharing `/tmp`
    - add a regression test for the registry path contract
    
    ## Why
    Issue #21192 reports that the Linux sandbox currently uses one global
    temp path at `/tmp/codex-bwrap-synthetic-mount-targets`. If another user
    creates that directory first, later users can fail to open the shared
    lock file with `Permission denied`.
    
    ## Validation
    - `just fmt`
    - `cargo test -p codex-linux-sandbox`
    - `cargo clippy -p codex-linux-sandbox --all-targets`
    
    Fixes #21192
  • fix(linux-sandbox): avoid panic on bwrap build failures (#21127)
    ## Summary
    
    - Propagate Linux bubblewrap argument-construction failures instead of
    panicking in the helper
    - Keep mutable-symlink carveouts fail-closed while reporting them as
    ordinary sandbox build failures
    - Add regression coverage for a protected `.codex` symlink inside a
    writable workspace root
    
    ## Root cause
    
    Linux bubblewrap intentionally rejects read-only carveouts that cross a
    symlink the sandboxed process can still rewrite. That is the correct
    security behavior for protected metadata paths such as `.codex`.
    
    The bug was one layer higher: `linux_run_main` treated the expected
    build failure as impossible and panicked while constructing the
    bubblewrap argv. For issue #20716, that turned a normal fail-closed
    sandbox outcome into a noisy panic in the transcript.
    
    ## User impact
    
    Users with a project-local `.codex` symlink inside a writable workspace
    still get the conservative sandbox decision, but they no longer see a
    Rust panic for that condition. The helper now exits with the concise
    sandbox-build error so the normal denial / escalation path can handle
    it.
    
    
    Fixes #20716
  • fix(linux-sandbox): fall back when system bwrap lacks perms (#20628)
    ## Why
    
    Codex `0.128` started using `--perms` in more routine Linux sandbox
    construction when protected workspace metadata mounts landed in #19852.
    Upstream bubblewrap added `--perms` in `v0.5.0`, so system `bwrap`
    versions older than that, including the `v0.4.0` and `v0.4.1` family, do
    not support the flag. The launcher still selected those binaries as long
    as they existed on `PATH`.
    
    That means affected hosts can fail every sandboxed command up front
    with:
    
    ```text
    bwrap: Unknown option --perms
    ```
    
    The reports in #20590 and duplicate #20623 match that compatibility gap;
    #20623 explicitly shows system bubblewrap `0.4.0`.
    
    ## What changed
    
    - Replace the single `--argv0` probe with a small system-bwrap
    capability probe in `codex-rs/linux-sandbox/src/launcher.rs`.
    - Continue using the old-system `--argv0` compatibility path when
    needed, but only select a system `bwrap` if it also advertises
    `--perms`.
    - Fall back to the vendored `bwrap` when the system binary is too old
    for the flags Codex now requires.
    - Add regression coverage for the old-system-bwrap case so binaries
    without `--perms` stay on the vendored path.
    
    ## Verification
    
    - Added `falls_back_to_vendored_when_system_bwrap_lacks_perms` to cover
    the reported compatibility gap.
    - Ran `cargo test -p codex-linux-sandbox` and `cargo clippy -p
    codex-linux-sandbox --tests` locally. On macOS, the crate builds but its
    Linux-only tests are cfg-gated out, so the new regression test still
    needs Linux CI or a Linux devbox run for real execution coverage.
    
    ## Related issues
    
    - Fixes #20590
    - Duplicate report: #20623
  • Enforce workspace metadata protections in Linux sandbox (#19852)
    ## Summary
    
    Enforce FileSystemSandboxPolicy protected metadata names in the Linux
    bubblewrap adapter so `.git`, `.agents`, and `.codex` remain read only
    inside writable workspace roots unless the policy grants an explicit
    write carveout.
    
    ## Scope
    
    1. Translate protected metadata names from FileSystemSandboxPolicy into
    bubblewrap masks for existing metadata paths.
    2. Represent missing protected metadata paths as guarded mount targets
    so agents cannot create `.git`, `.agents`, or `.codex` under writable
    roots.
    3. Preserve normal git discovery for existing repos, worktrees, and
    parent repos.
    4. Keep explicit user write grants working when policy allows a
    protected metadata path directly.
    
    ## Not in scope
    
    1. No shell preflight UX.
    2. No TUI runtime profile propagation.
    3. No macOS Seatbelt changes in this PR.
    
    ## Reviewer focus
    
    1. This should be reviewed as the Linux enforcement adapter for the
    policy primitive from PR 19846.
    2. macOS enforcement already landed in PR 19847.
    3. The important invariant is that `FileSystemSandboxPolicy` is the
    source of truth for `.git`, `.agents`, and `.codex`.
    
    ## Validation
    
    1. `git diff` whitespace check passed.
    2. `cargo fmt` check passed with the existing stable rustfmt warning
    about `imports_granularity`.
    3. Full Linux sandbox Cargo test suite passed on the devbox.
    4. Devbox forty six case suite passed at head
    `012accb703c13bd28df5b40079a9bf183036336a`.
    5. Devbox summary: pass 46, fail 0.
    6. The devbox suite was run through `just c sandbox linux`.
    7. Focused repo test for Viyat parent repo case passed on the devbox.
  • linux-sandbox: switch helper plumbing to PermissionProfile (#20106)
    ## Why
    
    `PermissionProfile` is the canonical runtime permission model in the
    Rust workspace, but the Linux sandbox helper still accepted a legacy
    `SandboxPolicy` plus separate filesystem and network policy flags. That
    translation layer made the helper interface harder to reason about and
    left `linux-sandbox`-specific callers and tests coupled to the legacy
    policy representation.
    
    This change moves the helper onto `PermissionProfile` directly so the
    Linux sandbox plumbing matches the rest of the permission stack.
    
    ## What changed
    
    - changed `codex-linux-sandbox` to accept `--permission-profile` and
    derive the runtime filesystem and network policies internally
    - updated the in-process seccomp and legacy Landlock path in
    `codex-rs/linux-sandbox` to operate on `PermissionProfile`
    - updated Linux sandbox argv construction in `codex-rs/sandboxing`,
    `codex-rs/core`, and the CLI debug sandbox path to pass the canonical
    profile instead of serializing compatibility policy projections
    - simplified the Linux sandbox tests to build the exact permission
    profile under test, including the managed-proxy path and
    direct-runtime-enforcement carveout coverage
    - removed helper-local `SandboxPolicy` usage from `bwrap` tests where
    `FileSystemSandboxPolicy` is already the value being exercised
    
    ## Testing
    
    - `cargo test -p codex-sandboxing`
    - `cargo test -p codex-linux-sandbox` (on this macOS host, the crate
    compiled cleanly and its Linux-only tests were cfg-gated)
    - `cargo test -p codex-core --no-run`
    - `cargo test -p codex-cli --no-run`
  • fix(network-proxy): harden linux proxy bridge helpers (#20001)
    ## Why
    The Linux managed-proxy bridge helpers are long-lived child processes in
    the sandbox networking path. Before this change they stayed dumpable and
    the network seccomp profile did not block cross-process memory syscalls,
    so another same-user process could potentially inspect or modify bridge
    memory instead of interacting only through the intended proxy interface.
    
    ## What changed
    - reuse the shared `codex-process-hardening` helper to mark bridge
    helper children non-dumpable before they begin serving
    - deny `process_vm_readv` and `process_vm_writev` in the existing
    network seccomp filter
    
    ## Security impact
    Bridge helpers are less exposed to same-user cross-process inspection or
    memory writes, which reduces the chance that sandboxed code can
    interfere with proxy support processes outside the intended IPC path.
    
    ## Verification
    - `cargo test -p codex-process-hardening`
    - `cargo test -p codex-linux-sandbox`
    - attempted `cargo check -p codex-linux-sandbox --target
    x86_64-unknown-linux-gnu`; blocked on missing `x86_64-linux-gnu-gcc` on
    this macOS host
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [sandbox] Enforce protected workspace metadata paths (#19846)
    ## 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: #19847
    3. Shell preflight UX: #19848
    4. Runtime profile propagation: #19849
    5. Linux bubblewrap adapter: #19852
    
    ## Validation
    
    1. codex protocol permissions tests
    2. formatting for codex protocol and codex linux sandbox
    3. diff whitespace check
  • permissions: derive config defaults as profiles (#19772)
    ## Why
    
    This continues the permissions migration by making legacy config default
    resolution produce the canonical `PermissionProfile` first. The legacy
    `SandboxPolicy` projection should stay available at compatibility
    boundaries, but config loading should not create a legacy policy just to
    immediately convert it back into a profile.
    
    Specifically, when `default_permissions` is not specified in
    `config.toml`, instead of creating a `SandboxPolicy` in
    `codex-rs/core/src/config/mod.rs` and then trying to derive a
    `PermissionProfile` from it, we use `derive_permission_profile()` to
    create a more faithful `PermissionProfile` using the values of
    `ConfigToml` directly.
    
    This also keeps the existing behavior of `sandbox_workspace_write` and
    extra writable roots after #19841 replaced `:cwd` with `:project_roots`.
    Legacy workspace-write defaults are represented as symbolic
    `:project_roots` write access plus symbolic project-root metadata
    carveouts. Extra absolute writable roots are still added directly and
    continue to get concrete metadata protections for paths that exist under
    those roots.
    
    The platform sandboxes differ when a symbolic project-root subpath does
    not exist yet.
    
    * **Seatbelt** can encode literal/subpath exclusions directly, so macOS
    emits project-root metadata subpath policies even if `.git`, `.agents`,
    or `.codex` do not exist.
    * **bwrap** has to materialize bind-mount targets. Binding `/dev/null`
    to a missing `.git` can create a host-visible placeholder that changes
    Git repo discovery. Binding missing `.agents` would not affect Git
    discovery, but it would still create a host-visible project metadata
    placeholder from an automatic compatibility carveout. Linux therefore
    skips only missing automatic `.git` and `.agents` read-only metadata
    masks; missing `.codex` remains protected so first-time project config
    creation goes through the protected-path approval flow. User-authored
    `read` and `none` subpath rules keep normal bwrap behavior, and `none`
    can still mask the first missing component to prevent creation under
    writable roots.
    
    ## What Changed
    
    - Adds profile-native helpers for legacy workspace-write semantics,
    including `PermissionProfile::workspace_write_with()`,
    `FileSystemSandboxPolicy::workspace_write()`, and
    `FileSystemSandboxPolicy::with_additional_legacy_workspace_writable_roots()`.
    - Makes `FileSystemSandboxPolicy::workspace_write()` the single legacy
    workspace-write constructor so both `from_legacy_sandbox_policy()` and
    `From<&SandboxPolicy>` include the project-root metadata carveouts.
    - Removes the no-carveout `legacy_workspace_write_base_policy()` path
    and the `prune_read_entries_under_writable_roots()` cleanup that was
    only needed by that split construction.
    - Adds `ConfigToml::derive_permission_profile()` for legacy sandbox-mode
    fallback resolution; named `default_permissions` profiles continue
    through the permissions profile pipeline instead of being reconstructed
    from `sandbox_mode`.
    - Updates `Config::load()` to start from the derived profile, validate
    that it still has a legacy compatibility projection, and apply
    additional writable roots directly to managed workspace-write filesystem
    policies.
    - Updates Linux bwrap argument construction so missing automatic
    `.git`/`.agents` symbolic project-root read-only carveouts are skipped
    before emitting bind args; missing `.codex`, user-authored `read`/`none`
    subpath rules, and existing missing writable-root behavior are
    preserved.
    - Adds coverage that legacy workspace-write config produces symbolic
    project-root metadata carveouts, extra legacy workspace writable roots
    still protect existing metadata paths such as `.git`, and bwrap skips
    missing `.git`/`.agents` project-root carveouts while preserving missing
    `.codex` and user-authored missing subpath rules.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19772).
    * #19776
    * #19775
    * #19774
    * #19773
    * __->__ #19772
  • permissions: remove cwd special path (#19841)
    ## Why
    
    The experimental `PermissionProfile` API had both `:cwd` and
    `:project_roots` special filesystem paths, which made the permission
    root ambiguous. This PR removes the unstable `current_working_directory`
    special path before the permissions API is stabilized, so callers use
    `:project_roots` for symbolic project-root access.
    
    ## What changed
    
    - Removes `FileSystemSpecialPath::CurrentWorkingDirectory` from protocol
    and app-server protocol models, plus regenerated app-server
    JSON/TypeScript schemas.
    - Replaces internal `:cwd` permission entries with `:project_roots`
    entries.
    - Keeps the existing cwd-update behavior for legacy-shaped
    workspace-write profiles, while removing the deleted
    `CurrentWorkingDirectory` case from that compatibility path.
    - Keeps `PermissionProfile::workspace_write()` as the reusable symbolic
    workspace-write helper, with docs noting that `:project_roots` entries
    resolve at enforcement time.
    - Updates app-server docs/examples and approval UI labeling to stop
    advertising `:cwd` as a permission token.
    
    ## Compatibility
    
    Persisted rollout items may contain the old
    `{"kind":"current_working_directory"}` tag from earlier experimental
    `permissionProfile` snapshots. This PR keeps that tag as a
    deserialize-only alias for `ProjectRoots { subpath: None }`, while
    continuing to serialize only the new `project_roots` tag.
    
    ## Follow-up
    
    This PR intentionally does not introduce an explicit project-root set on
    `SessionConfiguration` or runtime sandbox resolution. Today, the
    resolver still uses the active cwd as the single implicit project root.
    A follow-up should model project roots separately from tool cwd so
    `:project_roots` entries can resolve against the configured project
    roots, and resolve to no entries when there are no project roots.
    
    ## Verification
    
    - `cargo test -p codex-protocol permissions:: --lib`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-sandboxing -p codex-exec-server --lib`
    - `cargo test -p codex-core session_configuration_apply_ --lib`
    - `cargo test -p codex-app-server
    command_exec_permission_profile_project_roots_use_command_cwd --test
    all`
    - `cargo test -p codex-tui
    thread_read_session_state_does_not_reuse_primary_permission_profile
    --lib`
    - `cargo test -p codex-tui
    preset_matching_accepts_workspace_write_with_extra_roots --lib`
    - `cargo test -p codex-config --lib`
  • permissions: remove legacy read-only access modes (#19449)
    ## Why
    
    `ReadOnlyAccess` was a transitional legacy shape on `SandboxPolicy`:
    `FullAccess` meant the historical read-only/workspace-write modes could
    read the full filesystem, while `Restricted` tried to carry partial
    readable roots. The partial-read model now belongs in
    `FileSystemSandboxPolicy` and `PermissionProfile`, so keeping it on
    `SandboxPolicy` makes every legacy projection reintroduce lossy
    read-root bookkeeping and creates unnecessary noise in the rest of the
    permissions migration.
    
    This PR makes the legacy policy model narrower and explicit:
    `SandboxPolicy::ReadOnly` and `SandboxPolicy::WorkspaceWrite` represent
    the old full-read sandbox modes only. Split readable roots, deny-read
    globs, and platform-default/minimal read behavior stay in the runtime
    permissions model.
    
    ## What changed
    
    - Removes `ReadOnlyAccess` from
    `codex_protocol::protocol::SandboxPolicy`, including the generated
    `access` and `readOnlyAccess` API fields.
    - Updates legacy policy/profile conversions so restricted filesystem
    reads are represented only by `FileSystemSandboxPolicy` /
    `PermissionProfile` entries.
    - Keeps app-server v2 compatible with legacy `fullAccess` read-access
    payloads by accepting and ignoring that no-op shape, while rejecting
    legacy `restricted` read-access payloads instead of silently widening
    them to full-read legacy policies.
    - Carries Windows sandbox platform-default read behavior with an
    explicit override flag instead of depending on
    `ReadOnlyAccess::Restricted`.
    - Refreshes generated app-server schema/types and updates tests/docs for
    the simplified legacy policy shape.
    
    ## Verification
    
    - `cargo check -p codex-app-server-protocol --tests`
    - `cargo check -p codex-windows-sandbox --tests`
    - `cargo test -p codex-app-server-protocol sandbox_policy_`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19449).
    * #19395
    * #19394
    * #19393
    * #19392
    * #19391
    * __->__ #19449
  • permissions: make legacy profile conversion cwd-free (#19414)
    ## Why
    
    The profile conversion path still required a `cwd` even when it was only
    translating a legacy `SandboxPolicy` into a `PermissionProfile`. That
    made profile producers invent an ambient `cwd`, which is exactly the
    anchoring we are trying to remove from permission-profile data. A legacy
    workspace-write policy can be represented symbolically instead: `:cwd =
    write` plus read-only `:project_roots` metadata subpaths.
    
    This PR creates that cwd-free base so the rest of the stack can stop
    threading cwd through profile construction. Callers that actually need a
    concrete runtime filesystem policy for a specific cwd still have an
    explicitly named cwd-bound conversion.
    
    ## What Changed
    
    - `PermissionProfile::from_legacy_sandbox_policy` now takes only
    `&SandboxPolicy`.
    - `FileSystemSandboxPolicy::from_legacy_sandbox_policy` is now the
    symbolic, cwd-free projection for profiles.
    - The old concrete projection is retained as
    `FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd` for
    runtime/boundary code that must materialize legacy cwd behavior.
    - Workspace-write profiles preserve `CurrentWorkingDirectory` and
    `ProjectRoots` special entries instead of materializing cwd into
    absolute paths.
    
    ## Verification
    
    - `cargo check -p codex-protocol -p codex-core -p
    codex-app-server-protocol -p codex-app-server -p codex-exec -p
    codex-exec-server -p codex-tui -p codex-sandboxing -p
    codex-linux-sandbox -p codex-analytics --tests`
    - `just fix -p codex-protocol -p codex-core -p codex-app-server-protocol
    -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p
    codex-sandboxing -p codex-linux-sandbox -p codex-analytics`
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19414).
    * #19395
    * #19394
    * #19393
    * #19392
    * #19391
    * __->__ #19414
  • feat(sandbox): add glob deny-read platform enforcement (#18096)
    ## Summary
    - adds macOS Seatbelt deny rules for unreadable glob patterns
    - expands unreadable glob matches on Linux and masks them in bwrap,
    including canonical symlink targets
    - keeps Linux glob expansion robust when `rg` is unavailable in minimal
    or Bazel test environments
    - adds sandbox integration coverage that runs `shell` and `exec_command`
    with a `**/*.env = none` policy and verifies the secret contents do not
    reach the model
    
    ## Linux glob expansion
    
    ```text
    Prefer:   rg --files --hidden --no-ignore --glob <pattern> -- <search-root>
    Fallback: internal globset walker when rg is not installed
    Failure:  any other rg failure aborts sandbox construction
    ```
    
    ```
    [permissions.workspace.filesystem]
    glob_scan_max_depth = 2
    
    [permissions.workspace.filesystem.":project_roots"]
    "**/*.env" = "none"
    ```
    
    
    This keeps the common path fast without making sandbox construction
    depend on an ambient `rg` binary. If `rg` is present but fails for
    another reason, the sandbox setup fails closed instead of silently
    omitting deny-read masks.
    
    ## Platform support
    - macOS: subprocess sandbox enforcement is handled by Seatbelt regex
    deny rules
    - Linux: subprocess sandbox enforcement is handled by expanding existing
    glob matches and masking them in bwrap
    - Windows: policy/config/direct-tool glob support is already on `main`
    from #15979; Windows subprocess sandbox paths continue to fail closed
    when unreadable split filesystem carveouts require runtime enforcement,
    rather than silently running unsandboxed
    
    ## Stack
    1. #15979 - merged: cross-platform glob deny-read
    policy/config/direct-tool support for macOS, Linux, and Windows
    2. This PR - macOS/Linux subprocess sandbox enforcement plus Windows
    fail-closed clarification
    3. #17740 - managed deny-read requirements
    
    ## Verification
    - Added integration coverage for `shell` and `exec_command` glob
    deny-read enforcement
    - `cargo check -p codex-sandboxing -p codex-linux-sandbox --tests`
    - `cargo check -p codex-core --test all`
    - `cargo clippy -p codex-linux-sandbox -p codex-sandboxing --tests`
    - `just bazel-lock-check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix(permissions): fix symlinked writable roots in sandbox permissions (#15981)
    ## Summary
    - preserve logical symlink paths during permission normalization and
    config cwd handling
    - bind real targets for symlinked readable/writable roots in bwrap and
    remap carveouts and unreadable roots there
    - add regressions for symlinked carveouts and nested symlink escape
    masking
    
    ## Root cause
    Permission normalization canonicalized symlinked writable roots and cwd
    to their real targets too early. That drifted policy checks away from
    the logical paths the sandboxed process can actually address, while
    bwrap still needed the real targets for mounts. The mismatch caused
    shell and apply_patch failures on symlinked writable roots.
    
    ## Impact
    Fixes #15781.
    
    Also fixes #17079:
    - #17079 is the protected symlinked carveout side: bwrap now binds the
    real symlinked writable-root target and remaps carveouts before masking.
    
    Related to #15157:
    - #15157 is the broader permission-check side of this path-identity
    problem. This PR addresses the shared logical-vs-canonical normalization
    issue, but the reported Darwin prompt behavior should be validated
    separately before auto-closing it.
    
    This should also fix #14672, #14694, #14715, and #15725:
    - #14672, #14694, and #14715 are the same Linux
    symlinked-writable-root/bwrap family as #15781.
    - #15725 is the protected symlinked workspace path variant; the PR
    preserves the protected logical path in policy space while bwrap applies
    read-only or unreadable treatment to the resolved target so
    file-vs-directory bind mismatches do not abort sandbox setup.
    
    ## Notes
    - Added Linux-only regressions for symlinked writable ancestors and
    protected symlinked directory targets, including nested symlink escape
    masking without rebinding the escape target writable.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • remove temporary ownership re-exports (#16626)
    Stacked on #16508.
    
    This removes the temporary `codex-core` / `codex-login` re-export shims
    from the ownership split and rewrites callsites to import directly from
    `codex-model-provider-info`, `codex-models-manager`, `codex-api`,
    `codex-protocol`, `codex-feedback`, and `codex-response-debug-context`.
    
    No behavior change intended; this is the mechanical import cleanup layer
    split out from the ownership move.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • ci: run Windows argument-comment-lint via native Bazel (#16120)
    ## Why
    
    Follow-up to #16106.
    
    `argument-comment-lint` already runs as a native Bazel aspect on Linux
    and macOS, but Windows is still the long pole in `rust-ci`. To move
    Windows onto the same native Bazel lane, the toolchain split has to let
    exec-side helper binaries build in an MSVC environment while still
    linting repo crates as `windows-gnullvm`.
    
    Pushing the Windows lane onto the native Bazel path exposed a second
    round of Windows-only issues in the mixed exec-toolchain plumbing after
    the initial wrapper/target fixes landed.
    
    ## What Changed
    
    - keep the Windows lint lanes on the native Bazel/aspect path in
    `rust-ci.yml` and `rust-ci-full.yml`
    - add a dedicated `local_windows_msvc` platform for exec-side helper
    binaries while keeping `local_windows` as the `windows-gnullvm` target
    platform
    - patch `rules_rust` so `repository_set(...)` preserves explicit
    exec-platform constraints for the generated toolchains, keep the
    Windows-specific bootstrap/direct-link fixes needed for the nightly lint
    driver, and expose exec-side `rustc-dev` `.rlib`s to the MSVC sysroot
    - register the custom Windows nightly toolchain set with MSVC exec
    constraints while still exposing both `x86_64-pc-windows-msvc` and
    `x86_64-pc-windows-gnullvm` targets
    - enable `dev_components` on the custom Windows nightly repository set
    so the MSVC exec helper toolchain actually downloads the
    compiler-internal crates that `clippy_utils` needs
    - teach `run-argument-comment-lint-bazel.sh` to enumerate concrete
    Windows Rust rules, normalize the resulting labels, and skip explicitly
    requested incompatible targets instead of failing before the lint run
    starts
    - patch `rules_rust` build-script env propagation so exec-side
    `windows-msvc` helper crates drop forwarded MinGW include and linker
    search paths as whole flag/path pairs instead of emitting malformed
    `CFLAGS`, `CXXFLAGS`, and `LDFLAGS`
    - export the Windows VS/MSVC SDK environment in `setup-bazel-ci` and
    pass the relevant variables through `run-bazel-ci.sh` via `--action_env`
    / `--host_action_env` so Bazel build scripts can see the MSVC and UCRT
    headers on native Windows runs
    - add inline comments to the Windows `setup-bazel-ci` MSVC environment
    export step so it is easier to audit how `vswhere`, `VsDevCmd.bat`, and
    the filtered `GITHUB_ENV` export fit together
    - patch `aws-lc-sys` to skip its standalone `memcmp` probe under Bazel
    `windows-msvc` build-script environments, which avoids a Windows-native
    toolchain mismatch that blocked the lint lane before it reached the
    aspect execution
    - patch `aws-lc-sys` to prefer its bundled `prebuilt-nasm` objects for
    Bazel `windows-msvc` build-script runs, which avoids missing
    `generated-src/win-x86_64/*.asm` runfiles in the exec-side helper
    toolchain
    - annotate the Linux test-only callsites in `codex-rs/linux-sandbox` and
    `codex-rs/core` that the wider native lint coverage surfaced
    
    ## Patches
    
    This PR introduces a large patch stack because the Windows Bazel lint
    lane currently depends on behavior that upstream dependencies do not
    provide out of the box in the mixed `windows-gnullvm` target /
    `windows-msvc` exec-toolchain setup.
    
    - Most of the `rules_rust` patches look like upstream candidates rather
    than OpenAI-only policy. Preserving explicit exec-platform constraints,
    forwarding the right MSVC/UCRT environment into exec-side build scripts,
    exposing exec-side `rustc-dev` artifacts, and keeping the Windows
    bootstrap/linker behavior coherent all look like fixes to the Bazel/Rust
    integration layer itself.
    - The two `aws-lc-sys` patches are more tactical. They special-case
    Bazel `windows-msvc` build-script environments to avoid a `memcmp` probe
    mismatch and missing NASM runfiles. Those may be harder to upstream
    as-is because they rely on Bazel-specific detection instead of a general
    Cargo/build-script contract.
    - Short term, carrying these patches in-tree is reasonable because they
    unblock a real CI lane and are still narrow enough to audit. Long term,
    the goal should not be to keep growing a permanent local fork of either
    dependency.
    - My current expectation is that the `rules_rust` patches are less
    controversial and should be broken out into focused upstream proposals,
    while the `aws-lc-sys` patches are more likely to be temporary escape
    hatches unless that crate wants a more general hook for hermetic build
    systems.
    
    Suggested follow-up plan:
    
    1. Split the `rules_rust` deltas into upstream-sized PRs or issues with
    minimized repros.
    2. Revisit the `aws-lc-sys` patches during the next dependency bump and
    see whether they can be replaced by an upstream fix, a crate upgrade, or
    a cleaner opt-in mechanism.
    3. Treat each dependency update as a chance to delete patches one by one
    so the local patch set only contains still-needed deltas.
    
    ## Verification
    
    - `./.github/scripts/run-argument-comment-lint-bazel.sh
    --config=argument-comment-lint --keep_going`
    - `RUNNER_OS=Windows
    ./.github/scripts/run-argument-comment-lint-bazel.sh --nobuild
    --config=argument-comment-lint --platforms=//:local_windows
    --keep_going`
    - `cargo test -p codex-linux-sandbox`
    - `cargo test -p codex-core shell_snapshot_tests`
    - `just argument-comment-lint`
    
    ## References
    
    - #16106
  • fix: close Bazel argument-comment-lint CI gaps (#16253)
    ## Why
    
    The Bazel-backed `argument-comment-lint` CI path had two gaps:
    
    - Bazel wildcard target expansion skipped inline unit-test crates from
    `src/` modules because the generated `*-unit-tests-bin` `rust_test`
    targets are tagged `manual`.
    - `argument-comment-mismatch` was still only a warning in the Bazel and
    packaged-wrapper entrypoints, so a typoed `/*param_name*/` comment could
    still pass CI even when the lint detected it.
    
    That left CI blind to real linux-sandbox examples, including the missing
    `/*local_port*/` comment in
    `codex-rs/linux-sandbox/src/proxy_routing.rs` and typoed argument
    comments in `codex-rs/linux-sandbox/src/landlock.rs`.
    
    ## What Changed
    
    - Added `tools/argument-comment-lint/list-bazel-targets.sh` so Bazel
    lint runs cover `//codex-rs/...` plus the manual `rust_test`
    `*-unit-tests-bin` targets.
    - Updated `just argument-comment-lint`, `rust-ci.yml`, and
    `rust-ci-full.yml` to use that helper.
    - Promoted both `argument-comment-mismatch` and
    `uncommented-anonymous-literal-argument` to errors in every strict
    entrypoint:
      - `tools/argument-comment-lint/lint_aspect.bzl`
      - `tools/argument-comment-lint/src/bin/argument-comment-lint.rs`
      - `tools/argument-comment-lint/wrapper_common.py`
    - Added wrapper/bin coverage for the stricter lint flags and documented
    the behavior in `tools/argument-comment-lint/README.md`.
    - Fixed the now-covered callsites in
    `codex-rs/linux-sandbox/src/proxy_routing.rs`,
    `codex-rs/linux-sandbox/src/landlock.rs`, and
    `codex-rs/core/src/shell_snapshot_tests.rs`.
    
    This keeps the Bazel target expansion narrow while making the Bazel and
    prebuilt-linter paths enforce the same strict lint set.
    
    ## Verification
    
    - `python3 -m unittest discover -s tools/argument-comment-lint -p
    'test_*.py'`
    - `cargo +nightly-2025-09-18 test --manifest-path
    tools/argument-comment-lint/Cargo.toml`
    - `just argument-comment-lint`
  • chore: clean up argument-comment lint and roll out all-target CI on macOS (#16054)
    ## Why
    
    `argument-comment-lint` was green in CI even though the repo still had
    many uncommented literal arguments. The main gap was target coverage:
    the repo wrapper did not force Cargo to inspect test-only call sites, so
    examples like the `latest_session_lookup_params(true, ...)` tests in
    `codex-rs/tui_app_server/src/lib.rs` never entered the blocking CI path.
    
    This change cleans up the existing backlog, makes the default repo lint
    path cover all Cargo targets, and starts rolling that stricter CI
    enforcement out on the platform where it is currently validated.
    
    ## What changed
    
    - mechanically fixed existing `argument-comment-lint` violations across
    the `codex-rs` workspace, including tests, examples, and benches
    - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` and
    `tools/argument-comment-lint/run.sh` so non-`--fix` runs default to
    `--all-targets` unless the caller explicitly narrows the target set
    - fixed both wrappers so forwarded cargo arguments after `--` are
    preserved with a single separator
    - documented the new default behavior in
    `tools/argument-comment-lint/README.md`
    - updated `rust-ci` so the macOS lint lane keeps the plain wrapper
    invocation and therefore enforces `--all-targets`, while Linux and
    Windows temporarily pass `-- --lib --bins`
    
    That temporary CI split keeps the stricter all-targets check where it is
    already cleaned up, while leaving room to finish the remaining Linux-
    and Windows-specific target-gated cleanup before enabling
    `--all-targets` on those runners. The Linux and Windows failures on the
    intermediate revision were caused by the wrapper forwarding bug, not by
    additional lint findings in those lanes.
    
    ## Validation
    
    - `bash -n tools/argument-comment-lint/run.sh`
    - `bash -n tools/argument-comment-lint/run-prebuilt-linter.sh`
    - shell-level wrapper forwarding check for `-- --lib --bins`
    - shell-level wrapper forwarding check for `-- --tests`
    - `just argument-comment-lint`
    - `cargo test` in `tools/argument-comment-lint`
    - `cargo test -p codex-terminal-detection`
    
    ## Follow-up
    
    - Clean up remaining Linux-only target-gated callsites, then switch the
    Linux lint lane back to the plain wrapper invocation.
    - Clean up remaining Windows-only target-gated callsites, then switch
    the Windows lint lane back to the plain wrapper invocation.
  • chore: move bwrap config helpers into dedicated module (#15898)
    ## Summary
    - move the bwrap PATH lookup and warning helpers out of config/mod.rs
    - move the related tests into a dedicated bwrap_tests.rs file
    
    ## Validation
    - git diff --check
    - skipped heavier local tests per request
    
    Follow-up to #15791.
  • Protect first-time project .codex creation across Linux and macOS sandboxes (#15067)
    ## Problem
    
    Codex already treated an existing top-level project `./.codex` directory
    as protected, but there was a gap on first creation.
    
    If `./.codex` did not exist yet, a turn could create files under it,
    such as `./.codex/config.toml`, without going through the same approval
    path as later modifications. That meant the initial write could bypass
    the intended protection for project-local Codex state.
    
    ## What this changes
    
    This PR closes that first-creation gap in the Unix enforcement layers:
    
    - `codex-protocol`
    - treat the top-level project `./.codex` path as a protected carveout
    even when it does not exist yet
    - avoid injecting the default carveout when the user already has an
    explicit rule for that exact path
    - macOS Seatbelt
    - deny writes to both the exact protected path and anything beneath it,
    so creating `./.codex` itself is blocked in addition to writes inside it
    - Linux bubblewrap
    - preserve the same protected-path behavior for first-time creation
    under `./.codex`
    - tests
    - add protocol regressions for missing `./.codex` and explicit-rule
    collisions
    - add Unix sandbox coverage for blocking first-time `./.codex` creation
      - tighten Seatbelt policy assertions around excluded subpaths
    
    ## Scope
    
    This change is intentionally scoped to protecting the top-level project
    `.codex` subtree from agent writes.
    
    It does not make `.codex` unreadable, and it does not change the product
    behavior around loading project skills from `.codex` when project config
    is untrusted.
    
    ## Why this shape
    
    The fix is pointed rather than broad:
    - it preserves the current model of “project `.codex` is protected from
    writes”
    - it closes the security-relevant first-write hole
    - it avoids folding a larger permissions-model redesign into this PR
    
    ## Validation
    
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-sandboxing seatbelt`
    - `cargo test -p codex-exec --test all
    sandbox_blocks_first_time_dot_codex_creation -- --nocapture`
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • fix: resolve bwrap from trusted PATH entry (#15791)
    ## Summary
    - resolve system bwrap from PATH instead of hardcoding /usr/bin/bwrap
    - skip PATH entries that resolve inside the current workspace before
    launching the sandbox helper
    - keep the vendored bubblewrap fallback when no trusted system bwrap is
    found
    
    ## Validation
    - cargo test -p codex-core bwrap --lib
    - cargo test -p codex-linux-sandbox
    - just fix -p codex-core
    - just fix -p codex-linux-sandbox
    - just fmt
    - just argument-comment-lint
    - cargo clean
  • fix: fix old system bubblewrap compatibility without falling back to vendored bwrap (#15693)
    Fixes #15283.
    
    ## Summary
    Older system bubblewrap builds reject `--argv0`, which makes our Linux
    sandbox fail before the helper can re-exec. This PR keeps using system
    `/usr/bin/bwrap` whenever it exists and only falls back to vendored
    bwrap when the system binary is missing. That matters on stricter
    AppArmor hosts, where the distro bwrap package also provides the policy
    setup needed for user namespaces.
    
    For old system bwrap, we avoid `--argv0` instead of switching binaries:
    - pass the sandbox helper a full-path `argv0`,
    - keep the existing `current_exe() + --argv0` path when the selected
    launcher supports it,
    - otherwise omit `--argv0` and re-exec through the helper's own
    `argv[0]` path, whose basename still dispatches as
    `codex-linux-sandbox`.
    
    Also updates the launcher/warning tests and docs so they match the new
    behavior: present-but-old system bwrap uses the compatibility path, and
    only absent system bwrap falls back to vendored.
    
    ### Validation
    
    1. Install Ubuntu 20.04 in a VM
    2. Compile codex and run without bubblewrap installed - see a warning
    about falling back to the vendored bwrap
    3. Install bwrap and verify version is 0.4.0 without `argv0` support
    4. run codex and use apply_patch tool without errors
    
    <img width="802" height="631" alt="Screenshot 2026-03-25 at 11 48 36 PM"
    src="https://github.com/user-attachments/assets/77248a29-aa38-4d7c-9833-496ec6a458b8"
    />
    <img width="807" height="634" alt="Screenshot 2026-03-25 at 11 47 32 PM"
    src="https://github.com/user-attachments/assets/5af8b850-a466-489b-95a6-455b76b5050f"
    />
    <img width="812" height="635" alt="Screenshot 2026-03-25 at 11 45 45 PM"
    src="https://github.com/user-attachments/assets/438074f0-8435-4274-a667-332efdd5cb57"
    />
    <img width="801" height="623" alt="Screenshot 2026-03-25 at 11 43 56 PM"
    src="https://github.com/user-attachments/assets/0dc8d3f5-e8cf-4218-b4b4-a4f7d9bf02e3"
    />
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • fix: fall back to vendored bubblewrap when system bwrap lacks --argv0 (#15338)
    ## Why
    
    Fixes [#15283](https://github.com/openai/codex/issues/15283), where
    sandboxed tool calls fail on older distro `bubblewrap` builds because
    `/usr/bin/bwrap` does not understand `--argv0`. The upstream [bubblewrap
    v0.9.0 release
    notes](https://github.com/containers/bubblewrap/releases/tag/v0.9.0)
    explicitly call out `Add --argv0`. Flipping `use_legacy_landlock`
    globally works around that compatibility bug, but it also weakens the
    default Linux sandbox and breaks proxy-routed and split-policy cases
    called out in review.
    
    The follow-up Linux CI failure was in the new launcher test rather than
    the launcher logic: the fake `bwrap` helper stayed open for writing, so
    Linux would not exec it. This update also closes the user-visibility gap
    from review by surfacing the same startup warning when `/usr/bin/bwrap`
    is present but too old for `--argv0`, not only when it is missing.
    
    ## What Changed
    
    - keep `use_legacy_landlock` default-disabled
    - teach `codex-rs/linux-sandbox/src/launcher.rs` to fall back to the
    vendored bubblewrap build when `/usr/bin/bwrap` does not advertise
    `--argv0` support
    - add launcher tests for supported, unsupported, and missing system
    `bwrap`
    - write the fake `bwrap` test helper to a closed temp path so the
    supported-path launcher test works on Linux too
    - extend the startup warning path so Codex warns when `/usr/bin/bwrap`
    is missing or too old to support `--argv0`
    - mirror the warning/fallback wording across
    `codex-rs/linux-sandbox/README.md` and `codex-rs/core/README.md`,
    including that the fallback is the vendored bubblewrap compiled into the
    binary
    - cite the upstream `bubblewrap` release that introduced `--argv0`
    
    ## Verification
    
    - `bazel test --config=remote --platforms=//:rbe
    //codex-rs/linux-sandbox:linux-sandbox-unit-tests
    --test_filter=launcher::tests::prefers_system_bwrap_when_help_lists_argv0
    --test_output=errors`
    - `cargo test -p codex-core system_bwrap_warning`
    - `cargo check -p codex-exec -p codex-tui -p codex-tui-app-server -p
    codex-app-server`
    - `just argument-comment-lint`
  • fix(linux-sandbox): prefer system /usr/bin/bwrap when available (#14963)
    ## Problem
    Ubuntu/AppArmor hosts started failing in the default Linux sandbox path
    after the switch to vendored/default bubblewrap in `0.115.0`.
    
    The clearest report is in
    [#14919](https://github.com/openai/codex/issues/14919), especially [this
    investigation
    comment](https://github.com/openai/codex/issues/14919#issuecomment-4076504751):
    on affected Ubuntu systems, `/usr/bin/bwrap` works, but a copied or
    vendored `bwrap` binary fails with errors like `bwrap: setting up uid
    map: Permission denied` or `bwrap: loopback: Failed RTM_NEWADDR:
    Operation not permitted`.
    
    The root cause is Ubuntu's `/etc/apparmor.d/bwrap-userns-restrict`
    profile, which grants `userns` access specifically to `/usr/bin/bwrap`.
    Once Codex started using a vendored/internal bubblewrap path, that path
    was no longer covered by the distro AppArmor exception, so sandbox
    namespace setup could fail even when user namespaces were otherwise
    enabled and `uidmap` was installed.
    
    ## What this PR changes
    - prefer system `/usr/bin/bwrap` whenever it is available
    - keep vendored bubblewrap as the fallback when `/usr/bin/bwrap` is
    missing
    - when `/usr/bin/bwrap` is missing, surface a Codex startup warning
    through the app-server/TUI warning path instead of printing directly
    from the sandbox helper with `eprintln!`
    - use the same launcher decision for both the main sandbox execution
    path and the `/proc` preflight path
    - document the updated Linux bubblewrap behavior in the Linux sandbox
    and core READMEs
    
    ## Why this fix
    This still fixes the Ubuntu/AppArmor regression from
    [#14919](https://github.com/openai/codex/issues/14919), but it keeps the
    runtime rule simple and platform-agnostic: if the standard system
    bubblewrap is installed, use it; otherwise fall back to the vendored
    helper.
    
    The warning now follows that same simple rule. If Codex cannot find
    `/usr/bin/bwrap`, it tells the user that it is falling back to the
    vendored helper, and it does so through the existing startup warning
    plumbing that reaches the TUI and app-server instead of low-level
    sandbox stderr.
    
    ## Testing
    - `cargo test -p codex-linux-sandbox`
    - `cargo test -p codex-app-server --lib`
    - `cargo test -p codex-tui-app-server
    tests::embedded_app_server_start_failure_is_returned`
    - `cargo clippy -p codex-linux-sandbox --all-targets`
    - `cargo clippy -p codex-app-server --all-targets`
    - `cargo clippy -p codex-tui-app-server --all-targets`
  • fix(linux-sandbox): ignore missing writable roots (#14890)
    ## Summary
    - skip nonexistent `workspace-write` writable roots in the Linux
    bubblewrap mount builder instead of aborting sandbox startup
    - keep existing writable roots mounted normally so mixed Windows/WSL
    configs continue to work
    - add unit and Linux integration regression coverage for the
    missing-root case
    
    ## Context
    This addresses regression A from #14875. Regression B will be handled in
    a separate PR.
    
    The old bubblewrap integration added `ensure_mount_targets_exist` as a
    preflight guard because bubblewrap bind targets must exist, and failing
    early let Codex return a clearer error than a lower-level mount failure.
    
    That policy turned out to be too strict once bubblewrap became the
    default Linux sandbox: shared Windows/WSL or mixed-platform configs can
    legitimately contain a well-formed writable root that does not exist on
    the current machine. This PR keeps bubblewrap's existing-target
    requirement, but changes Codex to skip missing writable roots instead of
    treating them as fatal configuration errors.
  • fix: canonicalize symlinked Linux sandbox cwd (#14849)
    ## Problem
    On Linux, Codex can be launched from a workspace path that is a symlink
    (for example, a symlinked checkout or a symlinked parent directory).
    
    Our sandbox policy intentionally canonicalizes writable/readable roots
    to the real filesystem path before building the bubblewrap mounts. That
    part is correct and needed for safety.
    
    The remaining bug was that bubblewrap could still inherit the helper
    process's logical cwd, which might be the symlinked alias instead of the
    mounted canonical path. In that case, the sandbox starts in a cwd that
    does not exist inside the sandbox namespace even though the real
    workspace is mounted. This can cause sandboxed commands to fail in
    symlinked workspaces.
    
    ## Fix
    This PR keeps the sandbox policy behavior the same, but separates two
    concepts that were previously conflated:
    
    - the canonical cwd used to define sandbox mounts and permissions
    - the caller's logical cwd used when launching the command
    
    On the Linux bubblewrap path, we now thread the logical command cwd
    through the helper explicitly and only add `--chdir <canonical path>`
    when the logical cwd differs from the mounted canonical path.
    
    That means:
    - permissions are still computed from canonical paths
    - bubblewrap starts the command from a cwd that definitely exists inside
    the sandbox
    - we do not widen filesystem access or undo the earlier symlink
    hardening
    
    ## Why This Is Safe
    This is a narrow Linux-only launch fix, not a policy change.
    
    - Writable/readable root canonicalization stays intact.
    - Protected metadata carveouts still operate on canonical roots.
    - We only override bubblewrap's inherited cwd when the logical path
    would otherwise point at a symlink alias that is not mounted in the
    sandbox.
    
    ## Tests
    - kept the existing protocol/core regression coverage for symlink
    canonicalization
    - added regression coverage for symlinked cwd handling in the Linux
    bubblewrap builder/helper path
    
    Local validation:
    - `just fmt`
    - `cargo test -p codex-protocol`
    - `cargo test -p codex-core
    normalize_additional_permissions_canonicalizes_symlinked_write_paths`
    - `cargo clippy -p codex-linux-sandbox -p codex-protocol -p codex-core
    --tests -- -D warnings`
    - `cargo build --bin codex`
    
    ## Context
    This is related to #14694. The earlier writable-root symlink fix
    addressed the mount/permission side; this PR fixes the remaining
    symlinked-cwd launch mismatch in the Linux sandbox path.
  • Apply argument comment lint across codex-rs (#14652)
    ## Why
    
    Once the repo-local lint exists, `codex-rs` needs to follow the
    checked-in convention and CI needs to keep it from drifting. This commit
    applies the fallback `/*param*/` style consistently across existing
    positional literal call sites without changing those APIs.
    
    The longer-term preference is still to avoid APIs that require comments
    by choosing clearer parameter types and call shapes. This PR is
    intentionally the mechanical follow-through for the places where the
    existing signatures stay in place.
    
    After rebasing onto newer `main`, the rollout also had to cover newly
    introduced `tui_app_server` call sites. That made it clear the first cut
    of the CI job was too expensive for the common path: it was spending
    almost as much time installing `cargo-dylint` and re-testing the lint
    crate as a representative test job spends running product tests. The CI
    update keeps the full workspace enforcement but trims that extra
    overhead from ordinary `codex-rs` PRs.
    
    ## What changed
    
    - keep a dedicated `argument_comment_lint` job in `rust-ci`
    - mechanically annotate remaining opaque positional literals across
    `codex-rs` with exact `/*param*/` comments, including the rebased
    `tui_app_server` call sites that now fall under the lint
    - keep the checked-in style aligned with the lint policy by using
    `/*param*/` and leaving string and char literals uncommented
    - cache `cargo-dylint`, `dylint-link`, and the relevant Cargo
    registry/git metadata in the lint job
    - split changed-path detection so the lint crate's own `cargo test` step
    runs only when `tools/argument-comment-lint/*` or `rust-ci.yml` changes
    - continue to run the repo wrapper over the `codex-rs` workspace, so
    product-code enforcement is unchanged
    
    Most of the code changes in this commit are intentionally mechanical
    comment rewrites or insertions driven by the lint itself.
    
    ## Verification
    
    - `./tools/argument-comment-lint/run.sh --workspace`
    - `cargo test -p codex-tui-app-server -p codex-tui`
    - parsed `.github/workflows/rust-ci.yml` locally with PyYAML
    
    ---
    
    * -> #14652
    * #14651
  • fix: reopen writable linux carveouts under denied parents (#14514)
    ## Summary
    - preserve Linux bubblewrap semantics for `write -> none -> write`
    filesystem policies by recreating masked mount targets before rebinding
    narrower writable descendants
    - add a Linux runtime regression for `/repo = write`, `/repo/a = none`,
    `/repo/a/b = write` so the nested writable child is exercised under
    bubblewrap
    - document the supported legacy Landlock fallback and the split-policy
    bubblewrap behavior for overlapping carveouts
    
    ## Example
    Given a split filesystem policy like:
    
    ```toml
    "/repo" = "write"
    "/repo/a" = "none"
    "/repo/a/b" = "write"
    ```
    
    this PR keeps `/repo` writable, masks `/repo/a`, and still reopens
    `/repo/a/b` as writable again under bubblewrap.
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-linux-sandbox`
    - `cargo clippy -p codex-linux-sandbox --tests -- -D warnings`
  • fix: preserve split filesystem semantics in linux sandbox (#14173)
    ## Stack
    
       fix: fail closed for unsupported split windows sandboxing #14172
    -> fix: preserve split filesystem semantics in linux sandbox #14173
       fix: align core approvals with split sandbox policies #14171
       refactor: centralize filesystem permissions precedence #14174
    
    ## Summary
    ## Summary
    - Preserve Linux split filesystem carveouts in bubblewrap by applying
    mount masks in the right order, so narrower rules still win under
    broader writable roots.
    - Preserve unreadable ancestors of writable roots by masking them first
    and then rebinding the narrower writable descendants.
    - Stop rejecting legacy-plus-split Linux configs that are
    sandbox-equivalent after `cwd` resolution by comparing semantics instead
    of raw legacy structs.
    - Fail closed when callers provide partial split policies, mismatched
    legacy-plus-split policies, or force `--use-legacy-landlock` for
    split-only shapes that legacy Landlock cannot enforce.
    - Add Linux regressions for overlapping writable, read-only, and denied
    paths, and document the supported split-policy enforcement path.
    
    ## Example
    Given a split filesystem policy like:
    
    ```toml
    [permissions.dev.filesystem]
    ":root" = "read"
    "/code" = "write"
    "/code/.git" = "read"
    "/code/secrets" = "none"
    "/code/secrets/tmp" = "write"
    ```
    
    this PR makes Linux enforce the intended result under bubblewrap:
    
    - `/code` stays writable
    - `/code/.git` stays read-only
    - `/code/secrets` stays denied
    - `/code/secrets/tmp` can still be reopened as writable if explicitly
    allowed
    
    Before this, Linux could lose one of those carveouts depending on mount
    order or legacy-policy fallback. This PR keeps the split-policy
    semantics intact and rejects configurations that legacy Landlock cannot
    represent safely.
  • fix: follow up on linux sandbox review nits (#14440)
    ## Summary
    - address the follow-up review nits from #13996 in a separate PR
    - make the approvals test command a raw string and keep the
    managed-network path using env proxy routing
    - inline `--apply-seccomp-then-exec` in the Linux sandbox inner command
    builder
    - remove the bubblewrap-specific sandbox metric tag path and drop the
    `use_legacy_landlock` shim from `sandbox_tag`/`TurnMetadataState::new`
    - restore the `Feature` import that `origin/main` currently still needs
    in `connectors.rs`
    
    ## Testing
    - `cargo test -p codex-linux-sandbox`
    - focused `codex-core` tests were rerun/started, but the final
    verification pass was interrupted when I pushed at request
  • refactor: make bubblewrap the default Linux sandbox (#13996)
    ## Summary
    - make bubblewrap the default Linux sandbox and keep
    `use_legacy_landlock` as the only override
    - remove `use_linux_sandbox_bwrap` from feature, config, schema, and
    docs surfaces
    - update Linux sandbox selection, CLI/config plumbing, and related
    tests/docs to match the new default
    - fold in the follow-up CI fixes for request-permissions responses and
    Linux read-only sandbox error text
  • fix(protocol): preserve legacy workspace-write semantics (#13957)
    ## Summary
    This is a fast follow to the initial `[permissions]` structure.
    
    - keep the new split-policy carveout behavior for narrower non-write
    entries under broader writable roots
    - preserve legacy `WorkspaceWrite` semantics by using a cwd-aware bridge
    that drops only redundant nested readable roots when projecting from
    `SandboxPolicy`
    - route the legacy macOS seatbelt adapter through that same legacy
    bridge so redundant nested readable roots do not become read-only
    carveouts on macOS
    - derive the legacy bridge for `command_exec` using the sandbox root cwd
    rather than the request cwd so policy derivation matches later sandbox
    enforcement
    - add regression coverage for the legacy macOS nested-readable-root case
    
    ## Examples
    ### Legacy `workspace-write` on macOS
    A legacy `workspace-write` policy can redundantly list a nested readable
    root under an already-writable workspace root.
    
    For example, legacy config can effectively mean:
    - workspace root (`.` / `cwd`) is writable
    - `docs/` is also listed in `readable_roots`
    
    The new shared split-policy helper intentionally treats a narrower
    non-write entry under a broader writable root as a carveout for real
    `[permissions]` configs. Without this fast follow, the unchanged macOS
    seatbelt legacy adapter could project that legacy shape into a
    `FileSystemSandboxPolicy` that treated `docs/` like a read-only carveout
    under the writable workspace root. In practice, legacy callers on macOS
    could unexpectedly lose write access inside `docs/`, even though that
    path was writable before the `[permissions]` migration work.
    
    This change fixes that by routing the legacy seatbelt path through the
    cwd-aware legacy bridge, so:
    - legacy `workspace-write` keeps `docs/` writable when `docs/` was only
    a redundant readable root
    - explicit `[permissions]` entries like `'.' = 'write'` and `'docs' =
    'read'` still make `docs/` read-only, which is the new intended
    split-policy behavior
    
    ### Legacy `command_exec` with a subdirectory cwd
    `command_exec` can run a command from a request cwd that is narrower
    than the sandbox root cwd.
    
    For example:
    - sandbox root cwd is `/repo`
    - request cwd is `/repo/subdir`
    - legacy policy is still `workspace-write` rooted at `/repo`
    
    Before this fast follow, `command_exec` derived the legacy bridge using
    the request cwd, but the sandbox was later built using the sandbox root
    cwd. That mismatch could miss redundant legacy readable roots during
    projection and accidentally reintroduce read-only carveouts for paths
    that should still be writable under the legacy model.
    
    This change fixes that by deriving the legacy bridge with the same
    sandbox root cwd that sandbox enforcement later uses.
    
    ## Verification
    - `just fmt`
    - `cargo test -p codex-core
    seatbelt_legacy_workspace_write_nested_readable_root_stays_writable`
    - `cargo test -p codex-core test_sandbox_config_parsing`
    - `cargo clippy -p codex-core -p codex-app-server --all-targets -- -D
    warnings`
    - `cargo clean`
  • linux-sandbox: honor split filesystem policies in bwrap (#13453)
    ## Why
    
    After `#13449`, the Linux helper could receive split filesystem and
    network policies, but the bubblewrap mount builder still reconstructed
    filesystem access from the legacy `SandboxPolicy`.
    
    That loses explicit unreadable carveouts under writable roots, and it
    also mishandles `Root` read access paired with explicit deny carveouts.
    In those cases bubblewrap could still expose paths that the split
    filesystem policy intentionally blocked.
    
    ## What changed
    
    - switched bubblewrap mount generation to consume
    `FileSystemSandboxPolicy` directly at the implementation boundary;
    legacy `SandboxPolicy` configs still flow through the existing
    `FileSystemSandboxPolicy::from(&sandbox_policy)` bridge before reaching
    bwrap
    - kept the Linux helper and preflight path on the split filesystem
    policy all the way into bwrap
    - re-applied explicit unreadable carveouts after readable and writable
    mounts so blocked subpaths still win under bubblewrap
    - masked denied directories with `--tmpfs` plus `--remount-ro` and
    denied files with `--ro-bind-data`, preserving the backing fd until exec
    - added comments in the unreadable-root masking block to explain why the
    mount order and directory/file split are intentional
    - updated Linux helper call sites and tests for the split-policy bwrap
    path
    
    ## Verification
    
    - added protocol coverage for root carveouts staying scoped
    - added core coverage that root-write plus deny carveouts still requires
    a platform sandbox
    - added bwrap unit coverage for reapplying blocked carveouts after
    writable binds
    - added Linux integration coverage for explicit split-policy carveouts
    under bubblewrap
    - validated the final branch state with `cargo test -p
    codex-linux-sandbox`, `cargo clippy -p codex-linux-sandbox --all-targets
    -- -D warnings`, and the PR CI reruns
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13453).
    * __->__ #13453
    * #13452
    * #13451
    * #13449
    * #13448
    * #13445
    * #13440
    * #13439
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • linux-sandbox: plumb split sandbox policies through helper (#13449)
    ## Why
    
    The Linux sandbox helper still only accepted the legacy `SandboxPolicy`
    payload.
    
    That meant the runtime could compute split filesystem and network
    policies, but the helper would immediately collapse them back to the
    compatibility projection before applying seccomp or staging the
    bubblewrap inner command.
    
    ## What changed
    
    - added hidden `--file-system-sandbox-policy` and
    `--network-sandbox-policy` flags alongside the legacy `--sandbox-policy`
    flag so the helper can migrate incrementally
    - updated the core-side Landlock wrapper to pass the split policies
    explicitly when launching `codex-linux-sandbox`
    - added helper-side resolution logic that accepts either the legacy
    policy alone or a complete split-policy pair and normalizes that into
    one effective configuration
    - switched Linux helper network decisions to use `NetworkSandboxPolicy`
    directly
    - added `FromStr` support for the split policy types so the helper can
    parse them from CLI JSON
    
    ## Verification
    
    - added helper coverage in `linux-sandbox/src/linux_run_main_tests.rs`
    for split-policy flags and policy resolution
    - added CLI argument coverage in `core/src/landlock.rs`
    - verified the current PR state with `just clippy`
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13449).
    * #13453
    * #13452
    * #13451
    * __->__ #13449
    * #13448
    * #13445
    * #13440
    * #13439
    
    ---------
    
    Co-authored-by: viyatb-oai <viyatb@openai.com>
  • fix(linux-sandbox): always unshare bwrap userns (#13624)
    ## Summary
    - always pass `--unshare-user` in the Linux bubblewrap argv builders
    - stop relying on bubblewrap's auto-userns behavior, which is skipped
    for `uid 0`
    - update argv expectations in tests and document the explicit user
    namespace behavior
    
    The installed Codex binary reproduced the same issue with:
    - `codex -c features.use_linux_sandbox_bwrap=true sandbox linux -- true`
    - `bwrap: Creating new namespace failed: Operation not permitted`
    
    This happens because Codex asked bubblewrap for mount/pid/network
    namespaces without explicitly asking for a user namespace. In a
    root-inside-container environment without ambient `CAP_SYS_ADMIN`, that
    fails. Adding `--unshare-user` makes bubblewrap create the user
    namespace first and then the remaining namespaces succeed.
  • Feat: Preserve network access on read-only sandbox policies (#13409)
    ## Summary
    
    `PermissionProfile.network` could not be preserved when additional or
    compiled permissions resolved to
    `SandboxPolicy::ReadOnly`, because `ReadOnly` had no network_access
    field. This change makes read-only + network
    enabled representable directly and threads that through the protocol,
    app-server v2 mirror, and permission-
      merging logic.
    
    ## What changed
    
    - Added `network_access: bool` to `SandboxPolicy::ReadOnly` in the core
    protocol and app-server v2 protocol.
    - Kept backward compatibility by defaulting the new field to false, so
    legacy read-only payloads still
        deserialize unchanged.
    - Updated `has_full_network_access()` and sandbox summaries to respect
    read-only network access.
      - Preserved PermissionProfile.network when:
          - compiling skill permission profiles into sandbox policies
          - normalizing additional permissions
          - merging additional permissions into existing sandbox policies
    - Updated the approval overlay to show network in the rendered
    permission rule when requested.
      - Regenerated app-server schema fixtures for the new v2 wire shape.
  • feat(linux-sandbox): support restricted ReadOnlyAccess in bwrap (#12369)
    ## Summary
    Implements Linux bubblewrap support for restricted `ReadOnlyAccess`
    (introduced in #11387) by honoring `readable_roots` and
    `include_platform_defaults` instead of failing closed.
    
    ## What changed
    - Added a Linux platform-default read allowlist for common
    system/runtime paths (e.g. /usr, /etc, /lib*, Nix store roots).
    - Updated the bwrap filesystem mount builder to support restricted read
    access:
      - Full-read policies still use `--ro-bind / /`
    - Restricted-read policies now start from` --tmpfs `/ and add scoped
    `--ro-bind` mounts
    - Preserved existing writable-root and protected-subpath behavior
    (`.git`, `.codex`, etc.).
    
    `ReadOnlyAccess::Restricted` was already modeled in protocol, but Linux
    bwrap still returned `UnsupportedOperation` for restricted read access.
    This closes that gap for the active Linux filesystem backend.
    
    
    ## Notes
    Legacy Linux Landlock fallback still fail-closes for restricted read
    access (unchanged).
  • feat(linux-sandbox): implement proxy-only egress via TCP-UDS-TCP bridge (#11293)
    ## Summary
    - Implement Linux proxy-only routing in `codex-rs/linux-sandbox` with a
    two-stage bridge: host namespace `loopback TCP proxy endpoint -> UDS`,
    then bwrap netns `loopback TCP listener -> host UDS`.
    - Add hidden `--proxy-route-spec` plumbing for outer-to-inner stage
    handoff.
    - Fail closed in proxy mode when no valid loopback proxy endpoints can
    be routed.
    - Introduce explicit network seccomp modes: `Restricted` (legacy
    restricted networking) and `ProxyRouted` (allow INET/INET6 for routed
    proxy access, deny `AF_UNIX` and `socketpair`).
    - Enforce that proxy bridge/routing is bwrap-only by validating
    `--apply-seccomp-then-exec` requires `--use-bwrap-sandbox`.
    - Keep landlock-only flows unchanged (no proxy bridge behavior outside
    bwrap).
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • chore: remove codex-core public protocol/shell re-exports (#12432)
    ## Why
    
    `codex-rs/core/src/lib.rs` re-exported a broad set of types and modules
    from `codex-protocol` and `codex-shell-command`. That made it easy for
    workspace crates to import those APIs through `codex-core`, which in
    turn hides dependency edges and makes it harder to reduce compile-time
    coupling over time.
    
    This change removes those public re-exports so call sites must import
    from the source crates directly. Even when a crate still depends on
    `codex-core` today, this makes dependency boundaries explicit and
    unblocks future work to drop `codex-core` dependencies where possible.
    
    ## What Changed
    
    - Removed public re-exports from `codex-rs/core/src/lib.rs` for:
    - `codex_protocol::protocol` and related protocol/model types (including
    `InitialHistory`)
      - `codex_protocol::config_types` (`protocol_config_types`)
    - `codex_shell_command::{bash, is_dangerous_command, is_safe_command,
    parse_command, powershell}`
    - Migrated workspace Rust call sites to import directly from:
      - `codex_protocol::protocol`
      - `codex_protocol::config_types`
      - `codex_protocol::models`
      - `codex_shell_command`
    - Added explicit `Cargo.toml` dependencies (`codex-protocol` /
    `codex-shell-command`) in crates that now import those crates directly.
    - Kept `codex-core` internal modules compiling by using `pub(crate)`
    aliases in `core/src/lib.rs` (internal-only, not part of the public
    API).
    - Updated the two utility crates that can already drop a `codex-core`
    dependency edge entirely:
      - `codex-utils-approval-presets`
      - `codex-utils-cli`
    
    ## Verification
    
    - `cargo test -p codex-utils-approval-presets`
    - `cargo test -p codex-utils-cli`
    - `cargo check --workspace --all-targets`
    - `just clippy`
  • fix(linux-sandbox): mount /dev in bwrap sandbox (#12081)
    ## Summary
    - Updates the Linux bubblewrap sandbox args to mount a minimal `/dev`
    using `--dev /dev` instead of only binding `/dev/null`. tools needing
    entropy (git, crypto libs, etc.) can fail.
    
    - Changed mount order so `--dev /dev` is added before writable-root
    `--bind` mounts, preserving writable `/dev/*` submounts like `/dev/shm`
    
    ## Why
    Fixes sandboxed command failures when reading `/dev/urandom` (and
    similar standard device-node access).
    
    
    Fixes https://github.com/openai/codex/issues/12056
  • build(linux-sandbox): always compile vendored bubblewrap on Linux; remove CODEX_BWRAP_ENABLE_FFI (#11498)
    ## Summary
    This PR removes the temporary `CODEX_BWRAP_ENABLE_FFI` flag and makes
    Linux builds always compile vendored bubblewrap support for
    `codex-linux-sandbox`.
    
    ## Changes
    - Removed `CODEX_BWRAP_ENABLE_FFI` gating from
    `codex-rs/linux-sandbox/build.rs`.
    - Linux builds now fail fast if vendored bubblewrap compilation fails
    (instead of warning and continuing).
    - Updated fallback/help text in
    `codex-rs/linux-sandbox/src/vendored_bwrap.rs` to remove references to
    `CODEX_BWRAP_ENABLE_FFI`.
    - Removed `CODEX_BWRAP_ENABLE_FFI` env wiring from:
      - `.github/workflows/rust-ci.yml`
      - `.github/workflows/bazel.yml`
      - `.github/workflows/rust-release.yml`
    
    ---------
    
    Co-authored-by: David Zbarsky <zbarsky@openai.com>
  • feat: make sandbox read access configurable with ReadOnlyAccess (#11387)
    `SandboxPolicy::ReadOnly` previously implied broad read access and could
    not express a narrower read surface.
    This change introduces an explicit read-access model so we can support
    user-configurable read restrictions in follow-up work, while preserving
    current behavior today.
    
    It also ensures unsupported backends fail closed for restricted-read
    policies instead of silently granting broader access than intended.
    
    ## What
    
    - Added `ReadOnlyAccess` in protocol with:
      - `Restricted { include_platform_defaults, readable_roots }`
      - `FullAccess`
    - Updated `SandboxPolicy` to carry read-access configuration:
      - `ReadOnly { access: ReadOnlyAccess }`
      - `WorkspaceWrite { ..., read_only_access: ReadOnlyAccess }`
    - Preserved existing behavior by defaulting current construction paths
    to `ReadOnlyAccess::FullAccess`.
    - Threaded the new fields through sandbox policy consumers and call
    sites across `core`, `tui`, `linux-sandbox`, `windows-sandbox`, and
    related tests.
    - Updated Seatbelt policy generation to honor restricted read roots by
    emitting scoped read rules when full read access is not granted.
    - Added fail-closed behavior on Linux and Windows backends when
    restricted read access is requested but not yet implemented there
    (`UnsupportedOperation`).
    - Regenerated app-server protocol schema and TypeScript artifacts,
    including `ReadOnlyAccess`.
    
    ## Compatibility / rollout
    
    - Runtime behavior remains unchanged by default (`FullAccess`).
    - API/schema changes are in place so future config wiring can enable
    restricted read access without another policy-shape migration.
  • feat(sandbox): enforce proxy-aware network routing in sandbox (#11113)
    ## Summary
    - expand proxy env injection to cover common tool env vars
    (`HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY`/`NO_PROXY` families +
    tool-specific variants)
    - harden macOS Seatbelt network policy generation to route through
    inferred loopback proxy endpoints and fail closed when proxy env is
    malformed
    - thread proxy-aware Linux sandbox flags and add minimal bwrap netns
    isolation hook for restricted non-proxy runs
    - add/refresh tests for proxy env wiring, Seatbelt policy generation,
    and Linux sandbox argument wiring
  • fix(linux-sandbox): block io_uring syscalls in no-network seccomp policy (#10814)
    ## Summary
    
    - Add seccomp deny rules for `io_uring` syscalls in the Linux sandbox
    network policy.
    - Specifically deny:
      - `SYS_io_uring_setup`
      - `SYS_io_uring_enter`
      - `SYS_io_uring_register`
  • feat(linux-sandbox): add bwrap support (#9938)
    ## Summary
    This PR introduces a gated Bubblewrap (bwrap) Linux sandbox path. The
    curent Linux sandbox path relies on in-process restrictions (including
    Landlock). Bubblewrap gives us a more uniform filesystem isolation
    model, especially explicit writable roots with the option to make some
    directories read-only and granular network controls.
    
    This is behind a feature flag so we can validate behavior safely before
    making it the default.
    
    - Added temporary rollout flag:
      - `features.use_linux_sandbox_bwrap`
    - Preserved existing default path when the flag is off.
    - In Bubblewrap mode:
    - Added internal retry without /proc when /proc mount is not permitted
    by the host/container.
  • feat(linux-sandbox): vendor bubblewrap and wire it with FFI (#10413)
    ## Summary
    
    Vendor Bubblewrap into the repo and add minimal build plumbing in
    `codex-linux-sandbox` to compile/link it.
    
    ## Why
    
    We want to move Linux sandboxing toward Bubblewrap, but in a safe
    two-step rollout:
    1) vendoring/build setup (this PR),  
    2) runtime integration (follow-up PR).
    
    ## Included
    
    - Add `codex-rs/vendor/bubblewrap` sources.
    - Add build-time FFI path in `codex-rs/linux-sandbox`.
    - Update `build.rs` rerun tracking for vendored files.
    - Small vendored compile warning fix (`sockaddr_nl` full init).
    
    follow up in https://github.com/openai/codex/pull/9938