Commit Graph

11 Commits

  • 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
  • permissions: make profiles represent enforcement (#19231)
    ## Why
    
    `PermissionProfile` is becoming the canonical permissions abstraction,
    but the old shape only carried optional filesystem and network fields.
    It could describe allowed access, but not who is responsible for
    enforcing it. That made `DangerFullAccess` and `ExternalSandbox` lossy
    when profiles were exported, cached, or round-tripped through app-server
    APIs.
    
    The important model change is that active permissions are now a disjoint
    union over the enforcement mode. Conceptually:
    
    ```rust
    pub enum PermissionProfile {
        Managed {
            file_system: FileSystemSandboxPolicy,
            network: NetworkSandboxPolicy,
        },
        Disabled,
        External {
            network: NetworkSandboxPolicy,
        },
    }
    ```
    
    This distinction matters because `Disabled` means Codex should apply no
    outer sandbox at all, while `External` means filesystem isolation is
    owned by an outside caller. Those are not equivalent to a broad managed
    sandbox. For example, macOS cannot nest Seatbelt inside Seatbelt, so an
    inner sandbox may require the outer Codex layer to use no sandbox rather
    than a permissive one.
    
    ## How Existing Modeling Maps
    
    Legacy `SandboxPolicy` remains a boundary projection, but it now maps
    into the higher-fidelity profile model:
    
    - `ReadOnly` and `WorkspaceWrite` map to `PermissionProfile::Managed`
    with restricted filesystem entries plus the corresponding network
    policy.
    - `DangerFullAccess` maps to `PermissionProfile::Disabled`, preserving
    the “no outer sandbox” intent instead of treating it as a lax managed
    sandbox.
    - `ExternalSandbox { network_access }` maps to
    `PermissionProfile::External { network }`, preserving external
    filesystem enforcement while still carrying the active network policy.
    - Split runtime policies that legacy `SandboxPolicy` cannot faithfully
    express, such as managed unrestricted filesystem plus restricted
    network, stay `Managed` instead of being collapsed into
    `ExternalSandbox`.
    - Per-command/session/turn grants remain partial overlays via
    `AdditionalPermissionProfile`; full `PermissionProfile` is reserved for
    complete active runtime permissions.
    
    ## What Changed
    
    - Change active `PermissionProfile` into a tagged union: `managed`,
    `disabled`, and `external`.
    - Keep partial permission grants separate with
    `AdditionalPermissionProfile` for command/session/turn overlays.
    - Represent managed filesystem permissions as either `restricted`
    entries or `unrestricted`; `glob_scan_max_depth` is non-zero when
    present.
    - Preserve old rollout compatibility by accepting the pre-tagged `{
    network, file_system }` profile shape during deserialization.
    - Preserve fidelity for important edge cases: `DangerFullAccess`
    round-trips as `disabled`, `ExternalSandbox` round-trips as `external`,
    and managed unrestricted filesystem + restricted network stays managed
    instead of being mistaken for external enforcement.
    - Preserve configured deny-read entries and bounded glob scan depth when
    full profiles are projected back into runtime policies, including
    unrestricted replacements that now become `:root = write` plus deny
    entries.
    - Regenerate the experimental app-server v2 JSON/TypeScript schema and
    update the `command/exec` README example for the tagged
    `permissionProfile` shape.
    
    ## Compatibility
    
    Legacy `SandboxPolicy` remains available at config/API boundaries as the
    compatibility projection. Existing rollout lines with the old
    `PermissionProfile` shape continue to load. The app-server
    `permissionProfile` field is experimental, so its v2 wire shape is
    intentionally updated to match the higher-fidelity model.
    
    ## Verification
    
    - `just write-app-server-schema`
    - `cargo check --tests`
    - `cargo test -p codex-protocol permission_profile`
    - `cargo test -p codex-protocol
    preserving_deny_entries_keeps_unrestricted_policy_enforceable`
    - `cargo test -p codex-app-server-protocol
    permission_profile_file_system_permissions`
    - `cargo test -p codex-app-server-protocol serialize_client_response`
    - `cargo test -p codex-core
    session_configured_reports_permission_profile_for_external_sandbox`
    - `just fix`
    - `just fix -p codex-protocol`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-core`
    - `just fix -p codex-app-server`
  • exec-server: require explicit filesystem sandbox cwd (#19046)
    ## Why
    
    This is a cleanup PR for the `PermissionProfile` migration stack. #19016
    fixed remote exec-server sandbox contexts so Docker-backed filesystem
    requests use a request/container `cwd` instead of leaking the local test
    runner `cwd`. That exposed the broader API problem:
    `FileSystemSandboxContext::new(SandboxPolicy)` could still reconstruct
    filesystem permissions by reading the exec-server process cwd with
    `AbsolutePathBuf::current_dir()`.
    
    That made `cwd`-dependent legacy entries, such as `:cwd`,
    `:project_roots`, and relative deny globs, depend on ambient process
    state instead of the request sandbox `cwd`. As later PRs make
    `PermissionProfile` the primary permissions abstraction, sandbox
    contexts should be explicit about whether they carry a request `cwd` or
    are profile-only. Removing the implicit constructor prevents new call
    sites from accidentally rebuilding permissions against the wrong `cwd`.
    
    ## What changed
    
    - Removed `FileSystemSandboxContext::new(SandboxPolicy)`.
    - Kept production callers on explicit constructors:
    `from_legacy_sandbox_policy(..., cwd)`, `from_permission_profile(...)`,
    and `from_permission_profile_with_cwd(...)`.
    - Updated exec-server test helpers to construct `PermissionProfile`
    values directly instead of routing through legacy `SandboxPolicy`
    projections.
    - Updated the environment regression test to use an explicit restricted
    profile with no synthetic `cwd`.
    
    ## Verification
    
    - `cargo test -p codex-exec-server`
    - `just fix -p codex-exec-server`
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19046).
    * #18288
    * #18287
    * #18286
    * #18285
    * #18284
    * #18283
    * #18282
    * #18281
    * #18280
    * __->__ #19046
  • exec-server: expose arg0 alias root to fs sandbox (#19016)
    ## Why
    
    The post-merge `rust-ci-full` run for #18999 still failed the Ubuntu
    remote `suite::remote_env` sandboxed filesystem tests. That run checked
    out merge commit `ddde50c611e4800cb805f243ed3c50bbafe7d011`, so the arg0
    guard lifetime fix was present.
    
    The Docker-backed failure had two remaining pieces:
    
    - The sandboxed filesystem helper needs to execute Codex through the
    `codex-linux-sandbox` arg0 alias path. The helper sandbox was only
    granting read access to the real Codex executable parent, so the alias
    parent also has to be visible inside the helper sandbox.
    - The remote-env tests were building sandbox contexts with
    `FileSystemSandboxContext::new()`, which captures the local test runner
    cwd. In the Docker remote exec-server, that host checkout path does not
    exist, so spawning the filesystem helper failed with `No such file or
    directory` before the helper could process the request.
    
    ## What Changed
    
    - Track all helper runtime read roots instead of a single root.
    - Add both the real Codex executable parent and the
    `codex-linux-sandbox` alias parent to sandbox readable roots.
    - Avoid sending an unused local cwd in remote filesystem sandbox
    contexts when the permission profile has no cwd-dependent entries.
    - Build the Docker remote-env test sandbox contexts with a cwd path that
    exists inside the container.
    - Add unit coverage for the alias-parent root and remote sandbox cwd
    handling.
    
    ## Verification
    
    - `cargo test -p codex-exec-server`
    - `cargo test -p codex-core
    remote_test_env_sandboxed_read_allows_readable_root`
    - `just fix -p codex-exec-server`
    - `just fix -p codex-core`
  • exec-server: carry filesystem sandbox profiles (#18276)
    ## Why
    
    The exec-server still needs platform sandbox inputs, but the migration
    should preserve the `PermissionProfile` that produced them. Keeping only
    the derived legacy sandbox map would keep `SandboxPolicy` as the
    effective abstraction and would make full-disk vs. restricted profiles
    harder to preserve as the permissions stack starts round-tripping
    profiles.
    
    `PermissionProfile` entries can also be cwd-sensitive (`:cwd`,
    `:project_roots`, relative globs), so the exec-server must carry the
    request sandbox cwd instead of resolving those entries against the
    long-lived exec-server process cwd.
    
    ## What changed
    
    `FileSystemSandboxContext` now carries `permissions: PermissionProfile`
    plus an optional `cwd`:
    
    - removed `sandboxPolicy`, `sandboxPolicyCwd`,
    `fileSystemSandboxPolicy`, and `additionalPermissions`
    - added `permissions` and `cwd`
    - kept the platform knobs `windowsSandboxLevel`,
    `windowsSandboxPrivateDesktop`, and `useLegacyLandlock`
    
    Core turn and apply-patch paths populate the context from the active
    runtime permissions and request cwd. Exec-server derives platform
    `SandboxPolicy`/`FileSystemSandboxPolicy` at the filesystem boundary,
    adds helper runtime reads there, and rejects cwd-dependent profiles that
    arrive without a cwd.
    
    The legacy `FileSystemSandboxContext::new(SandboxPolicy)` constructor
    now preserves the old workspace-write conversion semantics for
    compatibility tests/callers.
    
    ## Verification
    
    - `cargo test -p codex-exec-server`
    - `cargo test -p codex-exec-server sandbox_cwd -- --nocapture`
    - `cargo test -p codex-exec-server
    sandbox_context_new_preserves_legacy_workspace_write_read_only_subpaths
    -- --nocapture`
    - `cargo test -p codex-core --lib
    file_system_sandbox_context_uses_active_attempt -- --nocapture`
  • fix: fix fs sandbox helper for apply_patch (#18296)
    ## Summary
    
    - pass split filesystem sandbox policy/cwd through apply_patch contexts,
    while omitting legacy-equivalent policies to keep payloads small
    - keep the fs helper compatible with legacy Landlock by avoiding helper
    read-root permission expansion in that mode and disabling helper network
    access
    
    ## Root Cause
    
    `d626dc38950fb40a1a5ad0a8ffab2485e3348c53` routed exec-server filesystem
    operations through a sandboxed helper. That path forwarded legacy
    Landlock into a helper policy shape that could require direct
    split-policy enforcement. Sandboxed `apply_patch` hit that edge through
    the filesystem abstraction.
    
    The same 0.121 edit-regression path is consistent with #18354: normal
    writes route through the `apply_patch` filesystem helper, fail under
    sandbox, and then surface the generic retry-without-sandbox prompt.
    
    Fixes #18069
    Fixes #18354
    
    ## Validation
    
    - `cd codex-rs && just fmt`
    - earlier branch validation before merging current `origin/main` and
    dropping the now-separate PATH fix:
      - `cd codex-rs && cargo test -p codex-exec-server`
    - `cd codex-rs && cargo test -p codex-core file_system_sandbox_context`
      - `cd codex-rs && just fix -p codex-exec-server`
      - `cd codex-rs && just fix -p codex-core`
      - `git diff --check`
      - `cd codex-rs && cargo clean`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Add symlink flag to fs metadata (#17719)
    Add `is_symlink` to FsMetadata struct.
  • Run exec-server fs operations through sandbox helper (#17294)
    ## Summary
    - run exec-server filesystem RPCs requiring sandboxing through a
    `codex-fs` arg0 helper over stdin/stdout
    - keep direct local filesystem execution for `DangerFullAccess` and
    external sandbox policies
    - remove the standalone exec-server binary path in favor of top-level
    arg0 dispatch/runtime paths
    - add sandbox escape regression coverage for local and remote filesystem
    paths
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - remote devbox: `cd codex-rs && bazel test --bes_backend=
    --bes_results_url= //codex-rs/exec-server:all` (6/6 passed)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add sandbox support to filesystem APIs (#16751)
    ## Summary
    - add optional `sandboxPolicy` support to the app-server filesystem
    request surface
    - thread sandbox-aware filesystem options through app-server and
    exec-server adapters
    - enforce sandboxed read/write access in the filesystem abstraction with
    focused local and remote coverage
    
    ## Validation
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-exec-server file_system`
    - `cargo test -p codex-app-server suite::v2::fs`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Migrate apply_patch to executor filesystem (#17027)
    - Migrate apply-patch verification and application internals to use the
    async `ExecutorFileSystem` abstraction from `exec-server`.
    - Convert apply-patch `cwd` handling to `AbsolutePathBuf` through the
    verifier/parser/handler boundary.
    
    Doesn't change how the tool itself works.
  • Refactor ExecServer filesystem split between local and remote (#15232)
    For each feature we have:
    1. Trait exposed on environment
    2. **Local Implementation** of the trait
    3. Remote implementation that uses the client to proxy via network
    4. Handler implementation that handles PRC requests and calls into
    **Local Implementation**