fix(codexcli): restore sandbox baseline fields lost on permissions round-trip - #1801
Merged
Conversation
network.enabled, network.mode, network.unix_sockets, extends, and description are not representable in Rulesync's canonical permissions model, but were silently dropped on every regeneration. Now toCodexProfile() reads these fields and mergeWithExistingProfile() carries them forward so that an existing .codex/config.toml is not mutated beyond the managed filesystem/network.domains surface.
…s in docs - Add structural TOML parse assertion to verify preserved fields land in the correct table hierarchy (addresses reviewer suggestion) - Clarify in docs that filesystem and network.domains are always overwritten by Rulesync (canonical source), contrasting with the passthrough fields
…ebfetch rules
Round-trip idempotency fix:
- emit extends = ":workspace" when any edit/write rule is present so
Codex workspace-write baseline protections are always inherited
- emit network.enabled = true alongside network.domains when any
webfetch rule is present so domain rules actually take effect
- import network.enabled=true without domains as webfetch: { "*": "allow" }
so Codex-only configs round-trip correctly through Rulesync
Previously, a Codex config.toml containing these sandbox-level fields
would lose them after import + generate through any other tool (Claude
Code, Cursor, etc.) because Rulesync only modelled network as domains
and had no concept of extends or network.enabled.
…tion Address reviewer findings: - extends emitted only when actual write-access rules exist (deny-only or empty edit/write no longer triggers workspace baseline) - network.enabled=false suppresses domain import (avoids disabled→enabled upgrade on next generate) - unknown domain values no longer fall back to wildcard allow - domainsHadUnknown internal flag prevents wildcard inflation after unrecognized entries are stripped - warn when extends is replaced, unix_sockets preserved, or unknown domain entries are skipped - remove non-null assertion on domains; strip internal flag before TOML serialization - add tests covering deny-only edge cases and each new guard
Owner
|
@saitota Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix #1733 — Codex CLI permission profiles lose sandbox baseline fields on round-trip.
Background
Codex CLI v0.131.0 renamed
:project_roots→:workspace_rootsand removed legacy parsing (#22624). v0.133.0 addedextendsinheritance resolution (#22270) and madedenythe canonical filesystem value (#23493). v0.134.0 removed legacy profile v1 resolution entirely (#24051, #24055).After these changes, a functional Codex profile requires:
extends = ":workspace"— inherits workspace-write baseline protections; without it, writes inside workspace roots are not covered (Codex permissions docs)network.enabled = true— defaults tofalse;network.domainsentries are silently ignored unless this is set (Codex permissions docs)filesystem.":workspace_roots"."." = "write"— scopes filesystem write rules to workspace rootsRulesync modelled
networkasdomainsonly and had no concept ofextendsornetwork.enabled, so these fields were lost on every import/generate cycle regardless of the source tool.Changes
extends = ":workspace"when anyedit/writerule is presentnetwork.enabled = truealongsidenetwork.domainswhen anywebfetchrule is presentnetwork.enabled = true(no domains) aswebfetch: { "*": "allow" }description,network.mode,network.unix_socketsfrom existing config on regenerationThe converter infers these values from existing rule semantics rather than extending
permissions.jsonwith Codex-specific fields. This keeps the schema tool-agnostic and guarantees idempotency: any round-trip (Claude Code → Rulesync → Codex, Cursor → Rulesync → Codex, etc.) produces the same result.