Commit Graph

33 Commits

  • Prepare managed network sandbox context (#29456)
    ## Why
    
    Managed network configures commands to use local HTTP and SOCKS proxies.
    For commands delegated to the exec server, the proxy environment and the
    sandbox policy were prepared separately. On macOS, that meant a command
    could receive `HTTPS_PROXY=http://127.0.0.1:43123` while Seatbelt still
    denied access to port `43123`.
    
    ## What changed
    
    `NetworkProxy` now prepares the command environment and sandbox context
    together from the same runtime snapshot:
    
    ```text
    Prepared managed network
    ├── command environment: HTTPS_PROXY=http://127.0.0.1:43123
    └── sandbox context: allow outbound to 127.0.0.1:43123
    ```
    
    That context travels with remote exec requests. The exec server
    preserves the managed proxy and CA environment, and macOS Seatbelt
    allows only the prepared loopback proxy ports without enabling broad
    network access or local binding.
    
    The protocol field is optional and the existing enforcement flag remains
    in place, preserving compatibility with callers that do not send the new
    context.
  • Honor startup custom CA bundles with managed MITM (#29014)
    ## Why
    
    When Codex starts with a custom CA override such as
    `SSL_CERT_FILE=/path/to/corp-ca.pem codex`, `rustls-native-certs` treats
    that override as a replacement for the platform trust store. The managed
    proxy then rewrites child CA variables to its generated bundle, so the
    custom root or the ordinary platform roots can be lost. The proxy's
    upstream TLS connector must trust the same roots or private and
    corporate upstream certificates still fail after interception.
    
    ## What
    
    - load platform-native roots without consulting inherited CA override
    variables
    - append certificates from the existing curated startup CA file
    variables and `SSL_CERT_DIR`
    - share those platform and startup roots with the MITM upstream rustls
    connector
    - exclude the Codex managed MITM CA from upstream trust
    - normalize OpenSSL `TRUSTED CERTIFICATE` blocks while dropping trailing
    trust metadata
    - skip an inherited current Codex-managed bundle so nested launches do
    not duplicate it
    - append the Codex managed MITM CA to the child-facing bundle
    - copy certificate material only, so a private key or unrelated text
    colocated in a startup file is never exposed through the public bundle
    
    This is intentionally limited to CA paths present when Codex starts. It
    does not parse inline shell assignments or add per-command bundle
    materialization.
    
    This changes only `codex-network-proxy` and dependency metadata; it does
    not touch `codex-core` or sandbox orchestration.
    
    ## Validation
    
    - `just test -p codex-network-proxy`
    - includes an end-to-end upstream TLS test using a server trusted only
    by the startup custom CA
    - `just fix -p codex-network-proxy`
    - `just bazel-lock-check`
  • Scope network approvals by environment (#28899)
    Stacked on #28766.
    
    ## Why
    
    Network approvals are environment-scoped: allowing a host in one
    execution environment should not allow the same host in another
    environment.
    
    #28766 adds the inert IDs and constructor plumbing. This PR applies the
    behavior on top.
    
    ## What changed
    
    - Route managed network traffic through per-environment HTTP and SOCKS
    proxy listeners.
    - Stamp HTTP, HTTPS CONNECT, SOCKS TCP, and SOCKS UDP policy requests
    with the source environment at the proxy boundary.
    - Carry the selected execution environment through shell, unified exec,
    zsh-fork, and sandbox transform paths.
    - Include the environment in pending, approved-for-session, and
    denied-for-session network approval cache keys.
    - Include the environment in approval IDs and approval prompts.
    - Preserve legacy fallback for unattributed requests, but deny when
    active-call attribution is ambiguous.
    - Fail closed if an environment-specific proxy endpoint cannot be
    prepared.
    
    ## Validation
    
    - just fmt
    - CI will run tests and clippy
  • Wire managed MITM CA trust into child env (#22668)
    ## Stack
    1. Parent PR: #18240 uses named MITM permissions config.
    2. This PR wires managed MITM CA trust into spawned child processes.
    
    ## Why
    When Codex terminates HTTPS for limited mode or MITM hooks, child HTTPS
    clients need to trust Codex's managed MITM CA. Exporting proxy URLs
    alone is not enough, but blindly replacing user CA settings would be
    wrong: it can break custom enterprise/test roots, leak unreadable CA
    files into generated bundles, or make the child env disagree with its
    sandbox policy.
    
    ## Summary
    1. Build immutable managed CA bundles under `$CODEX_HOME/proxy` that
    include native roots, the managed MITM CA, and only inherited or
    command-scoped CA bundles the child is allowed to read.
    2. Export curated CA env vars alongside managed proxy env vars while
    preserving user CA override semantics, including nested Codex
    `SSL_CERT_FILE` precedence.
    3. Thread generated CA bundle paths into child sandbox readable roots,
    including debug sandbox execution, so the exported env vars work inside
    sandboxed commands.
    4. Remove only Codex-generated MITM CA bundle env when a child
    intentionally drops managed proxying for escalation or no-proxy retry.
    5. Document the managed CA bundle behavior and cover env injection,
    per-child bundle generation, sandbox readable roots, and no-proxy
    cleanup in tests.
    
    ## Validation
    1. Ran `just test -p codex-network-proxy`.
    2. Ran `just test -p codex-protocol`.
    3. Ran `just fix -p codex-network-proxy -p codex-protocol`.
    4. Tried focused `codex-core` validation, but the crate currently fails
    to compile in `core/tests/suite/guardian_review.rs` because an existing
    `Op::UserInput` initializer is missing `additional_context`.
    
    ---------
    
    Co-authored-by: Eva Wong <evawong@openai.com>
  • [codex] Enable Node env proxy for managed network proxy (#23905)
    ## Summary
    - set `NODE_USE_ENV_PROXY=1` when Codex applies managed network proxy
    environment overrides
    - keep the Node opt-in in the proxy environment key set used by
    shell/runtime env handling
    - cover the new env var in the focused network proxy env test
    
    ## Why
    Codex already sets HTTP proxy environment variables for child processes
    when the managed network proxy is active. Node's built-in network
    behavior needs the `NODE_USE_ENV_PROXY` opt-in to honor those env vars,
    so Node-based skill scripts can otherwise skip the managed proxy path
    and fail under restricted network access.
    
    ## Validation
    - `just fmt` in `codex-rs`
    - `cargo test -p codex-network-proxy` in `codex-rs`
  • fix(network-proxy): normalize network proxy host matching (#19995)
    ## Why
    The proxy matches allow and deny rules against normalized host strings.
    Scoped IPv6 literals can arrive in equivalent forms, such as
    `fd00::1%eth0`, `[fd00::1%eth0]`, or `[fd00::1%25eth0]`. Policy should
    canonicalize those spellings without erasing scope granularity: an
    unscoped rule like `fd00::1` should still cover scoped requests for that
    address, while a scoped rule like `fd00::1%eth0` should remain exact to
    that scope.
    
    ## What changed
    - preserve IPv6 scope IDs during host normalization and canonicalize
    `%25scope` to `%scope`
    - match policy against the exact normalized host plus the unscoped IP
    base for scoped literals
    - keep local-address explicit allow checks aligned with the same
    scoped/unscoped semantics
    - add focused coverage for scoped IPv6 normalization, scoped allow
    rules, and scoped deny rules in `network-proxy`
    
    ## Security impact
    A request cannot bypass a broad deny rule by adding an IPv6 scope
    suffix. At the same time, scoped policy remains precise:
    `deny=fd00::1%eth0` affects that scoped spelling without collapsing
    `fd00::1%eth1` onto the same key, and `allow=fe80::1%eth0` does not
    implicitly allow other scopes.
    
    ## Verification
    - `just fmt`
    - `cargo test -p codex-network-proxy`
    - `just fix -p codex-network-proxy`
    - `git diff --check`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
    Co-authored-by: evawong-oai <evawong@openai.com>
  • fix(network-proxy): tighten network proxy bypass defaults (#20002)
    ## Why
    Managed sessions use `NO_PROXY` to keep a small set of destinations on
    the direct path by default. The old default also bypassed all IPv4
    link-local addresses in `169.254.0.0/16`, which includes metadata
    endpoints such as `169.254.169.254`. Because `NO_PROXY` is evaluated by
    the client before the request reaches the managed proxy, requests to
    that range could skip proxy-side allowlist and local-binding checks
    entirely. On hosts where a link-local metadata service is reachable,
    that creates a path to sensitive environment metadata or credentials
    outside the intended enforcement point.
    
    ## What changed
    - remove the default IPv4 link-local `169.254.0.0/16` bypass from the
    managed proxy environment
    - keep the existing loopback and private-network defaults unchanged
    - update the regression assertion to lock in the narrower default
    
    ## Security impact
    Link-local requests now stay on the managed-proxy path by default, so
    the proxy can apply configured policy before they reach metadata-style
    endpoints or other link-local services.
    
    ## Verification
    - `cargo test -p codex-network-proxy`
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: fix stale proxy env restoration after shell snapshots (#17271)
    ## Summary
    
    This fixes a stale-environment path in shell snapshot restoration. A
    sandboxed command can source a shell snapshot that was captured while an
    older proxy process was running. If that proxy has died and come back on
    a different port, the snapshot can otherwise put old proxy values back
    into the command environment, which is how tools like `pip` end up
    talking to a dead proxy.
    
    The wrapper now captures the live process environment before sourcing
    the snapshot and then restores or clears every proxy env var from the
    proxy crate's canonical list. That makes proxy state after shell
    snapshot restoration match the current command environment, rather than
    whatever proxy values happened to be present in the snapshot. On macOS,
    the Codex-generated `GIT_SSH_COMMAND` is refreshed when the SOCKS
    listener changes, while custom SSH wrappers are still left alone.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: unblock private DNS in macOS sandbox (#17370)
    ## Summary
    - keep hostname targets proxied by default by removing hostname suffixes
    from the managed `NO_PROXY` value while preserving private/link-local
    CIDRs
    - make the macOS `allow_local_binding` sandbox rules match the local
    socket shape used by DNS tools by allowing wildcard local binds
    - allow raw DNS egress to remote port 53 only when `allow_local_binding`
    is enabled, without opening blanket outbound network access
    
    ## Root cause
    Raw DNS tools do not honor `HTTP_PROXY` or `ALL_PROXY`, so the
    proxy-only Seatbelt policy blocked their resolver traffic before it
    could reach host DNS. In the affected managed config,
    `allow_local_binding = true`, but the existing rule only allowed
    `localhost:*` binds; `dig`/BIND can bind sockets in a way that needs
    wildcard local binding. Separately, hostname suffixes in `NO_PROXY`
    could force internal hostnames to resolve locally instead of through the
    proxy path.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: refresh network proxy settings when sandbox mode changes (#17040)
    ## Summary
    
    Fix network proxy sessions so changing sandbox mode recomputes the
    effective managed network policy and applies it to the already-running
    per-session proxy.
    
    ## Root Cause
    
    `danger_full_access_denylist_only` injects `"*"` only while building the
    proxy spec for Full Access. Sessions built that spec once at startup, so
    a later permission switch to Full Access left the live proxy in its
    original restricted policy. Switching back needed the same recompute
    path to remove the synthetic wildcard again.
    
    ## What Changed
    
    - Preserve the original managed network proxy config/requirements so the
    effective spec can be recomputed for a new sandbox policy.
    - Refresh the current session proxy when sandbox settings change, then
    reapply exec-policy network overlays.
    - Add an in-place proxy state update path while rejecting
    listener/port/SOCKS changes that cannot be hot-reloaded.
    - Keep runtime proxy settings cheap to snapshot and update.
    - Add regression coverage for workspace-write -> Full Access ->
    workspace-write.
  • fix: clean up remaining Windows argument-comment-lint violations (#16071)
    ## Why
    
    The initial `argument-comment-lint` rollout left Windows on
    default-target coverage because there were still Windows-only callsites
    failing under `--all-targets`. This follow-up cleans up those remaining
    Windows-specific violations so the Windows CI lane can enforce the same
    stricter coverage, leaving Linux as the remaining platform-specific
    follow-up.
    
    ## What changed
    
    - switched the Windows `rust-ci` argument-comment-lint step back to the
    default wrapper invocation so it runs full-target coverage again
    - added the required `/*param_name*/` annotations at Windows-gated
    literal callsites in:
      - `codex-rs/windows-sandbox-rs/src/lib.rs`
      - `codex-rs/windows-sandbox-rs/src/elevated_impl.rs`
      - `codex-rs/tui_app_server/src/multi_agents.rs`
      - `codex-rs/network-proxy/src/proxy.rs`
    
    ## Validation
    
    - Windows `argument comment lint` CI on this PR
  • 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: refactor network permissions to use explicit domain and unix socket rule maps (#15120)
    ## Summary
    
    This PR replaces the legacy network allow/deny list model with explicit
    rule maps for domains and unix sockets across managed requirements,
    permissions profiles, the network proxy config, and the app server
    protocol.
    
    Concretely, it:
    
    - introduces typed domain (`allow` / `deny`) and unix socket permission
    (`allow` / `none`) entries instead of separate `allowed_domains`,
    `denied_domains`, and `allow_unix_sockets` lists
    - updates config loading, managed requirements merging, and exec-policy
    overlays to read and upsert rule entries consistently
    - exposes the new shape through protocol/schema outputs, debug surfaces,
    and app-server config APIs
    - rejects the legacy list-based keys and updates docs/tests to reflect
    the new config format
    
    ## Why
    
    The previous representation split related network policy across multiple
    parallel lists, which made merging and overriding rules harder to reason
    about. Moving to explicit keyed permission maps gives us a single source
    of truth per host/socket entry, makes allow/deny precedence clearer, and
    gives protocol consumers access to the full rule state instead of
    derived projections only.
    
    ## Backward Compatibility
    
    ### Backward compatible
    
    - Managed requirements still accept the legacy
    `experimental_network.allowed_domains`,
    `experimental_network.denied_domains`, and
    `experimental_network.allow_unix_sockets` fields. They are normalized
    into the new canonical `domains` and `unix_sockets` maps internally.
    - App-server v2 still deserializes legacy `allowedDomains`,
    `deniedDomains`, and `allowUnixSockets` payloads, so older clients can
    continue reading managed network requirements.
    - App-server v2 responses still populate `allowedDomains`,
    `deniedDomains`, and `allowUnixSockets` as legacy compatibility views
    derived from the canonical maps.
    - `managed_allowed_domains_only` keeps the same behavior after
    normalization. Legacy managed allowlists still participate in the same
    enforcement path as canonical `domains` entries.
    
    ### Not backward compatible
    
    - Permissions profiles under `[permissions.<profile>.network]` no longer
    accept the legacy list-based keys. Those configs must use the canonical
    `[domains]` and `[unix_sockets]` tables instead of `allowed_domains`,
    `denied_domains`, or `allow_unix_sockets`.
    - Managed `experimental_network` config cannot mix canonical and legacy
    forms in the same block. For example, `domains` cannot be combined with
    `allowed_domains` or `denied_domains`, and `unix_sockets` cannot be
    combined with `allow_unix_sockets`.
    - The canonical format can express explicit `"none"` entries for unix
    sockets, but those entries do not round-trip through the legacy
    compatibility fields because the legacy fields only represent allow/deny
    lists.
    ## Testing
    `/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl
    https://www.example.com' ` gives 200 with config
    ```
    [permissions.workspace.network.domains]
    "www.example.com" = "allow"
    ```
    and fails when set to deny: `curl: (56) CONNECT tunnel failed, response
    403`.
    
    Also tested backward compatibility path by verifying that adding the
    following to `/etc/codex/requirements.toml` works:
    ```
    [experimental_network]
    allowed_domains = ["www.example.com"]
    ```
  • 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>
  • fix(network-proxy): serve HTTP proxy listener as HTTP/1 (#14395)
    ## Summary
    - switch the local HTTP proxy listener from Rama's auto server to
    explicit HTTP/1 so CONNECT clients skip the version-sniffing pre-read
    path
    - move rustls crypto-provider bootstrap into the HTTP proxy runner so
    direct callers do not need hidden global init
    - add a regression test that exercises a plain HTTP/1 CONNECT request
    against a live loopback listener
  • Add guardian approval MVP (#13692)
    ## Summary
    - add the guardian reviewer flow for `on-request` approvals in command,
    patch, sandbox-retry, and managed-network approval paths
    - keep guardian behind `features.guardian_approval` instead of exposing
    a public `approval_policy = guardian` mode
    - route ordinary `OnRequest` approvals to the guardian subagent when the
    feature is enabled, without changing the public approval-mode surface
    
    ## Public model
    - public approval modes stay unchanged
    - guardian is enabled via `features.guardian_approval`
    - when that feature is on, `approval_policy = on-request` keeps the same
    approval boundaries but sends those approval requests to the guardian
    reviewer instead of the user
    - `/experimental` only persists the feature flag; it does not rewrite
    `approval_policy`
    - CLI and app-server no longer expose a separate `guardian` approval
    mode in this PR
    
    ## Guardian reviewer
    - the reviewer runs as a normal subagent and reuses the existing
    subagent/thread machinery
    - it is locked to a read-only sandbox and `approval_policy = never`
    - it does not inherit user/project exec-policy rules
    - it prefers `gpt-5.4` when the current provider exposes it, otherwise
    falls back to the parent turn's active model
    - it fail-closes on timeout, startup failure, malformed output, or any
    other review error
    - it currently auto-approves only when `risk_score < 80`
    
    ## Review context and policy
    - guardian mirrors `OnRequest` approval semantics rather than
    introducing a separate approval policy
    - explicit `require_escalated` requests follow the same approval surface
    as `OnRequest`; the difference is only who reviews them
    - managed-network allowlist misses that enter the approval flow are also
    reviewed by guardian
    - the review prompt includes bounded recent transcript history plus
    recent tool call/result evidence
    - transcript entries and planned-action strings are truncated with
    explicit `<guardian_truncated ... />` markers so large payloads stay
    bounded
    - apply-patch reviews include the full patch content (without
    duplicating the structured `changes` payload)
    - the guardian request layout is snapshot-tested using the same
    model-visible Responses request formatter used elsewhere in core
    
    ## Guardian network behavior
    - the guardian subagent inherits the parent session's managed-network
    allowlist when one exists, so it can use the same approved network
    surface while reviewing
    - exact session-scoped network approvals are copied into the guardian
    session with protocol/port scope preserved
    - those copied approvals are now seeded before the guardian's first turn
    is submitted, so inherited approvals are available during any immediate
    review-time checks
    
    ## Out of scope / follow-ups
    - the sandbox-permission validation split was pulled into a separate PR
    and is not part of this diff
    - a future follow-up can enable `serde_json` preserve-order in
    `codex-core` and then simplify the guardian action rendering further
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • refactor: remove proxy admin endpoint (#13687)
    ## Summary
    - delete the network proxy admin server and its runtime listener/task
    plumbing
    - remove the admin endpoint config, runtime, requirement, protocol,
    schema, and debug-surface fields
    - update proxy docs to reflect the remaining HTTP and SOCKS listeners
    only
  • feat(core): persist network approvals in execpolicy (#12357)
    ## Summary
    Persist network approval allow/deny decisions as `network_rule(...)`
    entries in execpolicy (not proxy config)
    
    It adds `network_rule` parsing + append support in `codex-execpolicy`,
    including `decision="prompt"` (parse-only; not compiled into proxy
    allow/deny lists)
    - compile execpolicy network rules into proxy allow/deny lists and
    update the live proxy state on approval
    - preserve requirements execpolicy `network_rule(...)` entries when
    merging with file-based execpolicy
    - reject broad wildcard hosts (for example `*`) for persisted
    `network_rule(...)`
  • fix(network-proxy): add unix socket allow-all and update seatbelt rules (#11368)
    ## Summary
    Adds support for a Unix socket escape hatch so we can bypass socket
    allowlisting when explicitly enabled.
    
    ## Description
    * added a new flag, `network.dangerously_allow_all_unix_sockets` as an
    explicit escape hatch
    * In codex-network-proxy, enabling that flag now allows any absolute
    Unix socket path from x-unix-socket instead of requiring each path to be
    explicitly allowlisted. Relative paths are still rejected.
    * updated the macOS seatbelt path in core so it enforces the same Unix
    socket behavior:
      * allowlisted sockets generate explicit network* subpath rules
      * allow-all generates a broad network* (subpath "/") rule
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • Refactor network approvals to host/protocol/port scope (#12140)
    ## Summary
    Simplify network approvals by removing per-attempt proxy correlation and
    moving to session-level approval dedupe keyed by (host, protocol, port).
    Instead of encoding attempt IDs into proxy credentials/URLs, we now
    treat approvals as a destination policy decision.
    
    - Concurrent calls to the same destination share one approval prompt.
    - Different destinations (or same host on different ports) get separate
    prompts.
    - Allow once approves the current queued request group only.
    - Allow for session caches that (host, protocol, port) and auto-allows
    future matching requests.
    - Never policy continues to deny without prompting.
    
    Example:
    - 3 calls: 
      - a.com (line 443)
      - b.com (line 443)
      - a.com (line 443)
    => 2 prompts total (a, b), second a waits on the first decision.
    - a.com:80 is treated separately from a.com line 443
    
    ## Testing
    - `just fmt` (in `codex-rs`)
    - `cargo test -p codex-core tools::network_approval::tests`
    - `cargo test -p codex-core` (unit tests pass; existing
    integration-suite failures remain in this environment)
  • feat(network-proxy): add websocket proxy env support (#11784)
    ## Summary
    - add managed proxy env wiring for websocket-specific variables
    (`WS_PROXY`/`WSS_PROXY`, including lowercase)
    - keep websocket proxy vars aligned with the existing managed HTTP proxy
    endpoint
    - add CONNECT regression tests to cover allowlist and denylist decisions
    (websocket tunnel path)
    - document websocket proxy usage and CONNECT policy behavior in the
    network proxy README
    
    ## Testing
    - just fmt
    - cargo test -p codex-network-proxy
    - cargo clippy -p codex-network-proxy
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • feat(core): add structured network approval plumbing and policy decision model (#11672)
    ### Description
    #### Summary
    Introduces the core plumbing required for structured network approvals
    
    #### What changed
    - Added structured network policy decision modeling in core.
    - Added approval payload/context types needed for network approval
    semantics.
    - Wired shell/unified-exec runtime plumbing to consume structured
    decisions.
    - Updated related core error/event surfaces for structured handling.
    - Updated protocol plumbing used by core approval flow.
    - Included small CLI debug sandbox compatibility updates needed by this
    layer.
    
    #### Why
    establishes the minimal backend foundation for network approvals without
    yet changing high-level orchestration or TUI behavior.
    
    #### Notes
    - Behavior remains constrained by existing requirements/config gating.
    - Follow-up PRs in the stack handle orchestration, UX, and app-server
    integration.
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • feat(network-proxy): structured policy signaling and attempt correlation to core (#11662)
    ## Summary
    When network requests were blocked, downstream code often had to infer
    ask vs deny from free-form response text. That was brittle and led to
    incorrect approval behavior.
    This PR fixes the proxy side so blocked decisions are structured and
    request metadata survives reliably.
    
    ## Description
    - Blocked proxy responses now carry consistent structured policy
    decision data.
    - Request attempt metadata is preserved across proxy env paths
    (including ALL_PROXY flows).
    - Header stripping was tightened so we still remove unsafe forwarding
    headers, but keep metadata needed for policy handling.
    - Block messages were clarified (for example, allowlist miss vs explicit
    deny).
    - Added unified violation log entries so policy failures can be
    inspected in one place.
    - Added/updated tests for these behaviors.
    
    ---------
    
    Co-authored-by: Codex <199175422+chatgpt-codex-connector[bot]@users.noreply.github.com>
  • Enable SOCKS defaults for common local network proxy use cases (#11362)
    ## Summary
    - enable local-use defaults in network proxy settings: SOCKS5 on, SOCKS5
    UDP on, upstream proxying on, and local binding on
    - add a regression test that asserts the full
    `NetworkProxySettings::default()` baseline
    - Fixed managed listener reservation behavior.
    Before: we always reserved a loopback SOCKS listener, even when
    enable_socks5 = false.
    Now: SOCKS listener is only reserved when SOCKS is enabled.
    - Fixed /debug-config env output for SOCKS-disabled sessions.
    ALL_PROXY now shows the HTTP proxy URL when SOCKS is disabled (instead
    of incorrectly showing socks5h://...).
    
    
    ## Validation
    - just fmt
    - cargo test -p codex-network-proxy
    - cargo clippy -p codex-network-proxy --all-targets
  • feat: retain NetworkProxy, when appropriate (#11207)
    As of this PR, `SessionServices` retains a
    `Option<StartedNetworkProxy>`, if appropriate.
    
    Now the `network` field on `Config` is `Option<NetworkProxySpec>`
    instead of `Option<NetworkProxy>`.
    
    Over in `Session::new()`, we invoke `NetworkProxySpec::start_proxy()` to
    create the `StartedNetworkProxy`, which is a new struct that retains the
    `NetworkProxy` as well as the `NetworkProxyHandle`. (Note that `Drop` is
    implemented for `NetworkProxyHandle` to ensure the proxies are shutdown
    when it is dropped.)
    
    The `NetworkProxy` from the `StartedNetworkProxy` is threaded through to
    the appropriate places.
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/11207).
    * #11285
    * __->__ #11207
  • chore: put crypto provider logic in a shared crate (#11294)
    Ensures a process-wide rustls crypto provider is installed.
    
    Both the `codex-network-proxy` and `codex-api` crates need this.
  • 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
  • feat: reserve loopback ephemeral listeners for managed proxy (#11269)
    Codex may run many per-thread proxy instances, so hardcoded proxy ports
    are brittle and conflict-prone. The previous "ephemeral" approach still
    had a race: `build()` read `local_addr()` from temporary listeners and
    dropped them before `run()` rebound the ports. That left a
    [TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
    window where the OS (or another process) could reuse the same port,
    causing intermittent `EADDRINUSE` and partial proxy startup.
    
    Change the managed proxy path to reserve real listener sockets up front
    and keep them alive until startup:
    
    - add `ReservedListeners` on `NetworkProxy` to hold HTTP/SOCKS/admin std
    listeners allocated during `build()`
    - in managed mode, bind `127.0.0.1:0` for each listener and carry those
    bound sockets into `run()` instead of rebinding by address later
    - add `run_*_with_std_listener` entry points for HTTP, SOCKS5, and admin
    servers so `run()` can start services from already-reserved sockets
    - keep static/configured ports only when `managed_by_codex(false)`,
    including explicit `socks_addr` override support
    - remove fallback synthetic port allocation and add tests for managed
    ephemeral loopback binding and unmanaged configured-port behavior
    
    This makes managed startup deterministic, avoids port collisions, and
    preserves the intended distinction between Codex-managed ephemeral ports
    and externally managed fixed ports.
  • feat: include NetworkConfig through ExecParams (#11105)
    This PR adds the following field to `Config`:
    
    ```rust
    pub network: Option<NetworkProxy>,
    ```
    
    Though for the moment, it will always be initialized as `None` (this
    will be addressed in a subsequent PR).
    
    This PR does the work to thread `network` through to `execute_exec_env()`, `process_exec_tool_call()`, and `UnifiedExecRuntime.run()` to ensure it is available whenever we span a process.
  • refactor(network-proxy): flatten network config under [network] (#10965)
    Summary:
    - Rename config table from network_proxy to network.
    - Flatten allowed_domains, denied_domains, allow_unix_sockets, and
    allow_local_binding onto NetworkProxySettings.
    - Update runtime, state constraints, tests, and README to the new config
    shape.
  • feat(network-proxy): add a SOCKS5 proxy with policy enforcement (#9803)
    ### Summary
    - Adds an optional SOCKS5 listener via `rama-socks5`
    - SOCKS5 is disabled by default and gated by config
    - Reuses existing policy enforcement and blocked-request recording
    - Blocks SOCKS5 in limited mode to prevent method-policy bypass
    - Applies bind clamping to the SOCKS5 listener
    
    ### Config
    New/used fields under `network_proxy`:
    - `enable_socks5`
    - `socks_url`
    - `enable_socks5_udp`
    
    ### Scope
    - Changes limited to `codex-rs/network-proxy` (+ `codex-rs/Cargo.lock`)
    
    ### Testing
    ```bash
    cd codex-rs
    just fmt
    cargo test -p codex-network-proxy --offline
  • feat: introducing a network sandbox proxy (#8442)
    This add a new crate, `codex-network-proxy`, a local network proxy
    service used by Codex to enforce fine-grained network policy (domain
    allow/deny) and to surface blocked network events for interactive
    approvals.
    
    - New crate: `codex-rs/network-proxy/` (`codex-network-proxy` binary +
    library)
    - Core capabilities:
      - HTTP proxy support (including CONNECT tunneling)
      - SOCKS5 proxy support (in the later PR)
    - policy evaluation (allowed/denied domain lists; denylist wins;
    wildcard support)
      - small admin API for polling/reload/mode changes
    - optional MITM support for HTTPS CONNECT to enforce “limited mode”
    method restrictions (later PR)
    
    Will follow up integration with codex in subsequent PRs.
    
    ## Testing
    
    - `cd codex-rs && cargo build -p codex-network-proxy`
    - `cd codex-rs && cargo run -p codex-network-proxy -- proxy`