Skip to content

permissions: make SessionConfigured profile-only#19774

Open
bolinfest wants to merge 1 commit intopr19773from
pr19774
Open

permissions: make SessionConfigured profile-only#19774
bolinfest wants to merge 1 commit intopr19773from
pr19774

Conversation

@bolinfest
Copy link
Copy Markdown
Collaborator

@bolinfest bolinfest commented Apr 27, 2026

Why

SessionConfiguredEvent is the internal event that tells clients what permissions are active for a session. Emitting both sandbox_policy and permission_profile leaves two possible authorities and forces every consumer to decide which one to honor. At this point in the migration, the profile is expressive enough to represent managed, disabled, and external sandbox enforcement, so the internal event can be profile-only.

The wire compatibility concern is older serialized events or rollout data that only contain sandbox_policy; those still need to deserialize.

What Changed

  • Removes sandbox_policy from SessionConfiguredEvent and makes permission_profile required.
  • Adds custom deserialization so old payloads with only sandbox_policy are upgraded to a cwd-anchored PermissionProfile.
  • Updates core event emission and TUI session handling to sync permissions from the profile directly.
  • Updates app-server response construction to derive the legacy sandbox response field from the active thread snapshot instead of from SessionConfiguredEvent.
  • Updates yolo-mode display logic to treat both PermissionProfile::Disabled and managed unrestricted filesystem plus enabled network as full-access, while still preserving the distinction between no sandbox and external sandboxing.

Verification

  • cargo test -p codex-protocol session_configured_event --lib
  • cargo test -p codex-protocol serialize_event --lib
  • cargo test -p codex-exec session_configured --lib
  • cargo test -p codex-app-server thread_response_permission_profile_preserves_enforcement --lib
  • cargo test -p codex-core session_configured_reports_permission_profile_for_external_sandbox --lib
  • cargo test -p codex-tui session_configured --lib
  • cargo test -p codex-tui yolo_mode_includes_managed_full_access_profiles --lib

Stack created with Sapling. Best reviewed with ReviewStack.

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23dc42cae4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/tui/src/history_cell.rs Outdated
Comment on lines +1324 to +1325
approval_policy == AskForApproval::Never
&& matches!(permission_profile, PermissionProfile::Disabled)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recognize managed full-access profiles as YOLO

has_yolo_permissions now only treats PermissionProfile::Disabled as full-access, but managed profiles with unrestricted filesystem and enabled network are also projected as danger-full-access in the rest of the UI. In sessions with approval_policy = Never and a custom managed-unrestricted profile, this change drops the YOLO indicator in the session header even though permissions are still effectively full access, creating inconsistent and misleading permission state display.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in cac068aaea. has_yolo_permissions now treats both PermissionProfile::Disabled and managed unrestricted filesystem plus enabled network as YOLO, while preserving the distinction for external sandboxing. Covered by yolo_mode_includes_managed_full_access_profiles.

@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from f1f1786 to c247fed Compare April 27, 2026 06:09
@bolinfest bolinfest force-pushed the pr19773 branch 2 times, most recently from 3e7f657 to dbcbf3b Compare April 27, 2026 06:15
@bolinfest bolinfest force-pushed the pr19774 branch 3 times, most recently from 926d327 to f1a729b Compare April 27, 2026 06:35
@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from 72111af to cac068a Compare April 27, 2026 07:14
@bolinfest bolinfest force-pushed the pr19773 branch 2 times, most recently from c543c8d to 0519638 Compare April 27, 2026 16:30
@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from 347c6f7 to cb0186b Compare April 27, 2026 16:46
@bolinfest bolinfest force-pushed the pr19773 branch 2 times, most recently from 86504e2 to 536ab2d Compare April 27, 2026 20:42
@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from 68fb758 to bbec1f1 Compare April 27, 2026 21:46
@bolinfest bolinfest force-pushed the pr19773 branch 2 times, most recently from 333c2c3 to 95aae38 Compare April 27, 2026 22:03
@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from 4797158 to ea8ca7d Compare April 27, 2026 22:30
@bolinfest bolinfest force-pushed the pr19773 branch 2 times, most recently from 030cb28 to 5b19acc Compare April 27, 2026 22:49
@bolinfest bolinfest force-pushed the pr19774 branch 2 times, most recently from 53baea0 to 4b9080c Compare April 27, 2026 23:02
bolinfest added a commit that referenced this pull request Apr 27, 2026
## 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant