Skip to content

fix(codexcli): scope :workspace extends to workspace-wide writes and import extends-only profiles - #1810

Merged
dyoshikawa merged 2 commits into
mainfrom
resolve-scrap-issue-1809-codexcli-permissions
Jun 10, 2026
Merged

fix(codexcli): scope :workspace extends to workspace-wide writes and import extends-only profiles#1810
dyoshikawa merged 2 commits into
mainfrom
resolve-scrap-issue-1809-codexcli-permissions

Conversation

@dyoshikawa

Copy link
Copy Markdown
Owner

Background

Issue #1809 collected the mid/low review findings from PR #1801 (codexcli permissions round-trip, #1733) so they would not be lost after merge. The findings were re-validated against the upstream Codex CLI source and docs before this fix:

  • Codex's :workspace baseline grants read access to the whole filesystem and write access to the entire workspace root plus /tmp/$TMPDIR, and filesystem entries grant access on their own without extends (verified against openai/codex PermissionProfile::workspace_write() / FileSystemSandboxPolicy::workspace_write). Emitting extends = ":workspace" for any write rule therefore over-grants.
  • Profiles granting write solely via extends = ":workspace" were dropped on import, degrading to default-deny on regeneration (remaining form of Codex CLI permissions export can lose network and filesystem sandbox baselines #1733).
  • The global * wildcard in network.domains is rejected at Codex config load time in both allow and deny positions (network-proxy README), so the previous round-trip shape enabled = truewebfetch: { "*": "allow" }domains."*" = "allow" generated a config Codex refuses to start with.
  • One premise in finding 8 of the issue turned out to be incorrect: unlisted domains are denied (allowlist-first), not allowed. The fix still emits enabled = true only when an allow rule exists, which is behavior-preserving and keeps deny-only sets round-trippable.

Changes

  • Emit extends = ":workspace" only for workspace-wide write rules (., ./, **, ./**); narrow or workspace-external writes are expressed purely as filesystem entries (finding 1)
  • Import extends = ":workspace" as edit: { ".": "allow" } with a round-trip test so extends-only profiles converge instead of being lost (finding 2)
  • Return domainsHadUnknown out-of-band from profile parsing instead of carrying it inside the serialized CodexNetwork type; removes the eslint-disable workaround (finding 3)
  • Document the enabled !== false import rationale (finding 4)
  • Simplify the description merge and use an object argument for mergeWithExistingProfile (findings 5, 6)
  • Preserve unix_sockets values verbatim instead of silently filtering unknown values (finding 7)
  • Emit network.enabled = true only when at least one allow rule exists; deny-only domain sets are emitted without enabled and still re-import (finding 8, with corrected premise)
  • Warn when preserving an existing network.mode (finding 9)
  • Guard global wildcard domain entries: webfetch: { "*": "allow" } becomes enabled = true with no domain entry (lossless round-trip with the existing import path), webfetch: { "*": "deny" } is skipped with a warning (finding 10)
  • Sync docs/reference/file-formats.md and skills/rulesync/file-formats.md with the new behavior

pnpm cicheck passes locally (the content check passes once the synced skills file is committed).

Closes #1809

🤖 Generated with Claude Code

cm-dyoshikawa and others added 2 commits June 9, 2026 21:22
…import extends-only profiles

- Emit extends = ":workspace" only for workspace-wide write rules (., ./, **, ./**)
  instead of any write rule, since Codex filesystem entries grant access on their
  own and :workspace widens write access to the whole workspace plus /tmp/$TMPDIR
- Import extends = ":workspace" as edit: { ".": "allow" } so extends-only
  profiles round-trip instead of degrading to default-deny
- Move the domainsHadUnknown flag out of the serialized CodexNetwork type into
  an out-of-band parse result, removing the eslint-disable workaround
- Emit network.enabled = true only when an allow rule exists; deny-only domain
  sets are emitted without enabled (Codex defaults to restricted) and still
  round-trip back into rulesync rules
- Skip global wildcard domain entries that Codex rejects at config load time:
  webfetch "*" allow becomes enabled-only, "*" deny is skipped with a warning
- Preserve unix_sockets values verbatim (pass-through) instead of silently
  filtering unknown values, and warn when preserving existing network.mode
- Simplify description merge, use object argument for mergeWithExistingProfile,
  and document the enabled !== false import rationale
- Update docs/reference/file-formats.md and synced skills/rulesync copy

Closes #1809

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-domain import on enabled

Address review findings on PR #1810:
- Codex rejects the global wildcard only in DENIED domains (openai/codex#15549
  allows "*" in allowed_domains for denylist-only setups). Emit
  webfetch "*" allow as a regular "*" = "allow" domain entry (Codex-valid
  and round-trips), keep skipping "*" deny with a warning. "**" is not a
  global wildcard in Codex, so no longer special-cased.
- On import, take deny entries always but import allow entries only when
  network.enabled = true is explicit, since Codex treats a missing enabled as
  restricted; importing allow from a disabled profile would activate a grant
  Codex never had (privilege escalation on round-trip).
- Update e2e import fixture to set enabled = true so allow domains import.
- Correct docs/reference/file-formats.md and synced skills copy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dyoshikawa
dyoshikawa merged commit 583074c into main Jun 10, 2026
8 checks passed
@dyoshikawa

Copy link
Copy Markdown
Owner Author

@dyoshikawa Thank you!

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.

Review findings for PR #1801 (codexcli permissions round-trip): extends over-grant, extends-only import loss, and related cleanups

2 participants