Commit Graph

19 Commits

  • Include legacy deny paths in elevated Windows sandbox setup (#17365)
    ## Summary
    
    This updates the Windows elevated sandbox setup/refresh path to include
    the legacy `compute_allow_paths(...).deny` protected children in the
    same deny-write payload pipe added for split filesystem carveouts.
    
    Concretely, elevated setup and elevated refresh now both build
    deny-write payload paths from:
    
    - explicit split-policy deny-write paths, preserving missing paths so
    setup can materialize them before applying ACLs
    - legacy `compute_allow_paths(...).deny`, which includes existing
    `.git`, `.codex`, and `.agents` children under writable roots
    
    This lets the elevated backend protect `.git` consistently with the
    unelevated/restricted-token path, and removes the old janky hard-coded
    `.codex` / `.agents` elevated setup helpers in favor of the shared
    payload path.
    
    ## Root Cause
    
    The landed split-carveout PR threaded a `deny_write_paths` pipe through
    elevated setup/refresh, but the legacy workspace-write deny set from
    `compute_allow_paths(...).deny` was not included in that payload. As a
    result, elevated workspace-write did not apply the intended deny-write
    ACLs for existing protected children like `<cwd>/.git`.
    
    ## Notes
    
    The legacy protected children still only enter the deny set if they
    already exist, because `compute_allow_paths` filters `.git`, `.codex`,
    and `.agents` with `exists()`. Missing explicit split-policy deny paths
    are preserved separately because setup intentionally materializes those
    before applying ACLs.
    
    ## Validation
    
    - `cargo fmt --check -p codex-windows-sandbox`
    - `cargo test -p codex-windows-sandbox`
    - `cargo build -p codex-cli -p codex-windows-sandbox --bins`
    - Elevated `codex exec` smoke with `windows.sandbox='elevated'`: fresh
    git repo, attempted append to `.git/config`, observed `Access is
    denied`, marker not written, Deny ACE present on `.git`
    - Unelevated `codex exec` smoke with `windows.sandbox='unelevated'`:
    fresh git repo, attempted append to `.git/config`, observed `Access is
    denied`, marker not written, Deny ACE present on `.git`
  • fix: support split carveouts in windows elevated sandbox (#14568)
    ## Summary
    - preserve legacy Windows elevated sandbox behavior for existing
    policies
    - add elevated-only support for split filesystem policies that can be
    represented as readable-root overrides, writable-root overrides, and
    extra deny-write carveouts
    - resolve those elevated filesystem overrides during sandbox transform
    and thread them through setup and policy refresh
    - keep failing closed for explicit unreadable (`none`) carveouts and
    reopened writable descendants under read-only carveouts
    - for explicit read-only-under-writable-root carveouts, materialize
    missing carveout directories during elevated setup before applying the
    deny-write ACL
    - document the elevated vs restricted-token support split in the core
    README
    
    ## Example
    Given a split filesystem policy like:
    
    ```toml
    ":root" = "read"
    ":cwd" = "write"
    "./docs" = "read"
    "C:/scratch" = "write"
    ```
    
    the elevated backend now provisions the readable-root overrides,
    writable-root overrides, and extra deny-write carveouts during setup and
    refresh instead of collapsing back to the legacy workspace-only shape.
    
    If a read-only carveout under a writable root is missing at setup time,
    elevated setup creates that carveout as an empty directory before
    applying its deny-write ACE; otherwise the sandboxed command could
    create it later and bypass the carveout. This is only for explicit
    policy carveouts. Best-effort workspace protections like `.codex/` and
    `.agents/` still skip missing directories.
    
    A policy like:
    
    ```toml
    "/workspace" = "write"
    "/workspace/docs" = "read"
    "/workspace/docs/tmp" = "write"
    ```
    
    still fails closed, because the elevated backend does not reopen
    writable descendants under read-only carveouts yet.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • ci: sync Bazel clippy lints and fix uncovered violations (#16351)
    ## Why
    
    Follow-up to #16345, the Bazel clippy rollout in #15955, and the cleanup
    pass in #16353.
    
    `cargo clippy` was enforcing the workspace deny-list from
    `codex-rs/Cargo.toml` because the member crates opt into `[lints]
    workspace = true`, but Bazel clippy was only using `rules_rust` plus
    `clippy.toml`. That left the Bazel lane vulnerable to drift:
    `clippy.toml` can tune lint behavior, but it cannot set
    allow/warn/deny/forbid levels.
    
    This PR now closes both sides of the follow-up. It keeps `.bazelrc` in
    sync with `[workspace.lints.clippy]`, and it fixes the real clippy
    violations that the newly-synced Windows Bazel lane surfaced once that
    deny-list started matching Cargo.
    
    ## What Changed
    
    - added `.github/scripts/verify_bazel_clippy_lints.py`, a Python check
    that parses `codex-rs/Cargo.toml` with `tomllib`, reads the Bazel
    `build:clippy` `clippy_flag` entries from `.bazelrc`, and reports
    missing, extra, or mismatched lint levels
    - ran that verifier from the lightweight `ci.yml` workflow so the sync
    check does not depend on a Rust toolchain being installed first
    - expanded the `.bazelrc` comment to explain the Cargo `workspace =
    true` linkage and why Bazel needs the deny-list duplicated explicitly
    - fixed the Windows-only `codex-windows-sandbox` violations that Bazel
    clippy reported after the sync, using the same style as #16353: inline
    `format!` args, method references instead of trivial closures, removed
    redundant clones, and replaced SID conversion `unwrap` and `expect`
    calls with proper errors
    - cleaned up the remaining cross-platform violations the Bazel lane
    exposed in `codex-backend-client` and `core_test_support`
    
    ## Testing
    
    Key new test introduced by this PR:
    
    `python3 .github/scripts/verify_bazel_clippy_lints.py`
  • chore: move pty and windows sandbox to Rust 2024 (#15954)
    ## Why
    
    `codex-utils-pty` and `codex-windows-sandbox` were the remaining crates
    in `codex-rs` that still overrode the workspace's Rust 2024 edition.
    Moving them forward in a separate PR keeps the baseline edition update
    isolated from the follow-on Bazel clippy workflow in #15955, while
    making linting and formatting behavior consistent with the rest of the
    workspace.
    
    This PR also needs Cargo and Bazel to agree on the edition for
    `codex-windows-sandbox`. Without the Bazel-side sync, the experimental
    Bazel app-server builds fail once they compile `windows-sandbox-rs`.
    
    ## What changed
    
    - switch `codex-rs/utils/pty` and `codex-rs/windows-sandbox-rs` to
    `edition = "2024"`
    - update `codex-utils-pty` callsites and tests to use the collapsed `if
    let` form that Clippy expects under the new edition
    - fix the Rust 2024 fallout in `windows-sandbox-rs`, including the
    reserved `gen` identifier, `unsafe extern` requirements, and new Clippy
    findings that surfaced under the edition bump
    - keep the edition bump separate from a larger unsafe cleanup by
    temporarily allowing `unsafe_op_in_unsafe_fn` in the Windows entrypoint
    modules that now report it under Rust 2024
    - update `codex-rs/windows-sandbox-rs/BUILD.bazel` to `crate_edition =
    "2024"` so Bazel compiles the crate with the same edition as Cargo
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/15954).
    * #15976
    * #15955
    * __->__ #15954
  • feat(windows-sandbox): add network proxy support (#12220)
    ## Summary
    
    This PR makes Windows sandbox proxying enforceable by routing proxy-only
    runs through the existing `offline` sandbox user and reserving direct
    network access for the existing `online` sandbox user.
    
    In brief:
    
    - if a Windows sandbox run should be proxy-enforced, we run it as the
    `offline` user
    - the `offline` user gets firewall rules that block direct outbound
    traffic and only permit the configured localhost proxy path
    - if a Windows sandbox run should have true direct network access, we
    run it as the `online` user
    - no new sandbox identity is introduced
    
    This brings Windows in line with the intended model: proxy use is not
    just env-based, it is backed by OS-level egress controls. Windows
    already has two sandbox identities:
    
    - `offline`: intended to have no direct network egress
    - `online`: intended to have full network access
    
    This PR makes proxy-enforced runs use that model directly.
    
    ### Proxy-enforced runs
    
    When proxy enforcement is active:
    
    - the run is assigned to the `offline` identity
    - setup extracts the loopback proxy ports from the sandbox env
    - Windows setup programs firewall rules for the `offline` user that:
      - block all non-loopback outbound traffic
      - block loopback UDP
      - block loopback TCP except for the configured proxy ports
    - optionally allow broader localhost access when `allow_local_binding=1`
    
    So the sandboxed process can only talk to the local proxy. It cannot
    open direct outbound sockets or do local UDP-based DNS on its own.The
    proxy then performs the real outbound network access outside that
    restricted sandbox identity.
    
    ### Direct-network runs
    
    When proxy enforcement is not active and full network access is allowed:
    
    - the run is assigned to the `online` identity
    - no proxy-only firewall restrictions are applied
    - the process gets normal direct network access
    
    ### Unelevated vs elevated
    
    The restricted-token / unelevated path cannot enforce per-identity
    firewall policy by itself.
    
    So for Windows proxy-enforced runs, we transparently use the logon-user
    sandbox path under the hood, even if the caller started from the
    unelevated mode. That keeps enforcement real instead of best-effort.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Use released DotSlash package for argument-comment lint (#15199)
    ## Why
    The argument-comment lint now has a packaged DotSlash artifact from
    [#15198](https://github.com/openai/codex/pull/15198), so the normal repo
    lint path should use that released payload instead of rebuilding the
    lint from source every time.
    
    That keeps `just clippy` and CI aligned with the shipped artifact while
    preserving a separate source-build path for people actively hacking on
    the lint crate.
    
    The current alpha package also exposed two integration wrinkles that the
    repo-side prebuilt wrapper needs to smooth over:
    - the bundled Dylint library filename includes the host triple, for
    example `@nightly-2025-09-18-aarch64-apple-darwin`, and Dylint derives
    `RUSTUP_TOOLCHAIN` from that filename
    - on Windows, Dylint's driver path also expects `RUSTUP_HOME` to be
    present in the environment
    
    Without those adjustments, the prebuilt CI jobs fail during `cargo
    metadata` or driver setup. This change makes the checked-in prebuilt
    wrapper normalize the packaged library name to the plain
    `nightly-2025-09-18` channel before invoking `cargo-dylint`, and it
    teaches both the wrapper and the packaged runner source to infer
    `RUSTUP_HOME` from `rustup show home` when the environment does not
    already provide it.
    
    After the prebuilt Windows lint job started running successfully, it
    also surfaced a handful of existing anonymous literal callsites in
    `windows-sandbox-rs`. This PR now annotates those callsites so the new
    cross-platform lint job is green on the current tree.
    
    ## What Changed
    - checked in the current
    `tools/argument-comment-lint/argument-comment-lint` DotSlash manifest
    - kept `tools/argument-comment-lint/run.sh` as the source-build wrapper
    for lint development
    - added `tools/argument-comment-lint/run-prebuilt-linter.sh` as the
    normal enforcement path, using the checked-in DotSlash package and
    bundled `cargo-dylint`
    - updated `just clippy` and `just argument-comment-lint` to use the
    prebuilt wrapper
    - split `.github/workflows/rust-ci.yml` so source-package checks live in
    a dedicated `argument_comment_lint_package` job, while the released lint
    runs in an `argument_comment_lint_prebuilt` matrix on Linux, macOS, and
    Windows
    - kept the pinned `nightly-2025-09-18` toolchain install in the prebuilt
    CI matrix, since the prebuilt package still relies on rustup-provided
    toolchain components
    - updated `tools/argument-comment-lint/run-prebuilt-linter.sh` to
    normalize host-qualified nightly library filenames, keep the `rustup`
    shim directory ahead of direct toolchain `cargo` binaries, and export
    `RUSTUP_HOME` when needed for Windows Dylint driver setup
    - updated `tools/argument-comment-lint/src/bin/argument-comment-lint.rs`
    so future published DotSlash artifacts apply the same nightly-filename
    normalization and `RUSTUP_HOME` inference internally
    - fixed the remaining Windows lint violations in
    `codex-rs/windows-sandbox-rs` by adding the required `/*param*/`
    comments at the reported callsites
    - documented the checked-in DotSlash file, wrapper split, archive
    layout, nightly prerequisite, and Windows `RUSTUP_HOME` requirement in
    `tools/argument-comment-lint/README.md`
  • copy command-runner to CODEX_HOME so sandbox users can always execute it (#13413)
    • Keep Windows sandbox runner launches working from packaged installs by
    running the helper from a user-owned runtime location.
    
    On some Windows installs, the packaged helper location is difficult to
    use reliably for sandboxed runner launches even though the binaries are
    present. This change works around that by copying codex-
    command-runner.exe into CODEX_HOME/.sandbox-bin/, reusing that copy
    across launches, and falling back to the existing packaged-path lookup
    if anything goes wrong.
    
    The runtime copy lives in a dedicated directory with tighter ACLs than
    .sandbox: sandbox users can read and execute the runner there, but they
    cannot modify it. This keeps the workaround focused on the
    command runner, leaves the setup helper on its trusted packaged path,
    and adds logging so it is clear which runner path was selected at
    launch.
  • Protect workspace .agents directory in Windows sandbox (#11970)
    The Mac and Linux implementations of the sandbox recently added write
    protections for `.codex` and `.agents` subdirectories in all writable
    roots. When adding documentation for this, I noticed that this change
    was never made for the Windows sandbox.
    
    Summary
    - make compute_allow_paths treat .codex/.agents as protected alongside
    .git, and cover their behavior in new tests
    - wire protect_workspace_agents_dir through the sandbox lib and setup
    path to apply deny ACEs when `.agents` exists
    - factor shared ACL logic for workspace subdirectories
  • implement per-workspace capability SIDs for workspace specific ACLs (#10189)
    Today, there is a single capability SID that allows the sandbox to write
    to
    * workspace (cwd)
    * tmp directories if enabled
    * additional writable roots
    
    This change splits those up, so that each workspace has its own
    capability SID, while tmp and additional roots, which are
    installation-wide, are still governed by the "generic" capability SID
    
    This isolates workspaces from each other in terms of sandbox write
    access.
    Also allows us to protect <cwd>/.codex when codex runs in a specific
    <cwd>
  • use machine scope instead of user scope for dpapi. (#9713)
    This fixes a bug where the elevated sandbox setup encrypts sandbox user
    passwords as an admin user, but normal command execution attempts to
    decrypt them as a different user.
    
    Machine scope allows all users to encyrpt/decrypt
    
    this PR also moves the encrypted file to a different location
    .codex/.sandbox-secrets which the sandbox users cannot read.
  • chore: upgrade to Rust 1.92.0 (#8860)
    **Summary**
    - Upgrade Rust toolchain used by CI to 1.92.0.
    - Address new clippy `derivable_impls` warnings by deriving `Default`
    for enums across protocol, core, backend openapi models, and
    windows-sandbox setup.
    - Tidy up related test/config behavior (originator header handling, env
    override cleanup) and remove a now-unused assignment in TUI/TUI2 render
    layout.
    
    **Testing**
    - `just fmt`
    - `just fix -p codex-tui`
    - `just fix -p codex-tui2`
    - `just fix -p codex-windows-sandbox`
    - `cargo test -p codex-tui`
    - `cargo test -p codex-tui2`
    - `cargo test -p codex-windows-sandbox`
    - `cargo test -p codex-core --test all`
    - `cargo test -p codex-app-server --test all`
    - `cargo test -p codex-mcp-server --test all`
    - `cargo test --all-features`
  • best effort to "hide" Sandbox users (#8492)
    The elevated sandbox creates two new Windows users - CodexSandboxOffline
    and CodexSandboxOnline. This is necessary, so this PR does all that it
    can to "hide" those users. It uses the registry plus directory flags (on
    their home directories) to get them to show up as little as possible.
  • better idempotency for creating/updating firewall rules during setup. (#8686)
    make sure if the Sandbox has to re-initialize with different Sandbox
    user SID, it still finds/updates the firewall rule instead of creating a
    new one.
  • use a SandboxUsers group for ACLs instead of granting to each sandbox user separately (#8483)
    This is more future-proof if we ever decide to add additional Sandbox
    Users for new functionality
    
    This also moves some more user-related code into a new file for code
    cleanliness
  • use a junction for the cwd while read ACLs are being applied (#8444)
    The elevated setup synchronously applies read/write ACLs to any
    workspace roots.
    
    However, until we apply *read* permission to the full path, powershell
    cannot use some roots as a cwd as it needs access to all parts of the
    path in order to apply it as the working directory for a command.
    
    The solution is, while the async read-ACL part of setup is running, use
    a "junction" that lives in C:\Users\CodexSandbox{Offline|Online} that
    points to the cwd.
    
    Once the read ACLs are applied, we stop using the junction.
    
    -----
    
    this PR also removes some dead code and overly-verbose logging, and has
    some light refactoring to the ACL-related functions
  • speed and reliability improvements for setting reads ACLs (#8216)
    - Batch read ACL creation for online/offline sandbox user
    - creates a new ACL helper process that is long-lived and runs in the
    background
    - uses a mutex so that only one helper process is running at a time.
  • bug fixes and perf improvements for elevated sandbox setup (#8094)
    a few fixes based on testing feedback:
    * ensure cap_sid file is always written by elevated setup.
    * always log to same file whether using elevated sandbox or not
    * process potentially slow ACE write operations in parallel
    * dedupe write roots so we don't double process any
    * don't try to create read/write ACEs on the same directories, due to
    race condition
  • Elevated Sandbox 2 (#7792)
    - DPAPI helpers for storing Sandbox user passwords securely
    - creation of Offline/Online sandbox users
    - ACL setup for sandbox users
    - firewall rule setup