Skip to content

feat(permissions): pass Claude Code settings keys and Codex CLI [tui] through the permission override - #2720

Merged
dyoshikawa merged 8 commits into
mainfrom
resolve-issue-2715-claudecode-settings-passthrough
Aug 23, 2026
Merged

feat(permissions): pass Claude Code settings keys and Codex CLI [tui] through the permission override#2720
dyoshikawa merged 8 commits into
mainfrom
resolve-issue-2715-claudecode-settings-passthrough

Conversation

@dyoshikawa

Copy link
Copy Markdown
Owner

Summary

.rulesync/permissions.jsonc's claudecode override block validated any key (its schema is a looseObject), but claudecode-permissions.ts only ever copied permissions and sandbox. Every other authored key — editorMode, and since then emojiCompletionEnabled, workflowSizeGuideline, keybindingFlavor, spellcheck, env, model, ... — validated cleanly, produced no warning, and was then dropped. Codex CLI's [tui] table hit the same wall from the other side: it was rejected by the CODEXCLI_OVERRIDE_KEYS allowlist with a warning.

Per the maintainer's re-check comment on the issue, the fix is widened from "add editorMode to the schema" to a general top-level passthrough, because enumerating keys one at a time keeps losing the race (2.1.217 → 2.1.239 alone added five, plus aliases).

Claude Code — generic top-level passthrough

  • Generate: every key in the claudecode override other than permission, permissions, sandbox, hooks and $schema is deep-merged into the top level of .claude/settings.json. Deep-merged for the same reason sandbox is: setting one key under env must not drop the variables beside it. The exclusions keep feature ownership intact — hooks belongs to the hooks feature, permission/permissions/sandbox have their own branches, and $schema is an editor pointer rather than a setting.
  • Import: every remaining top-level settings key is lifted back into the claudecode override, so an imported settings.json survives the next generate instead of being narrowed to the keys this feature happens to model.
  • Scope filtering (new, mirroring the existing CLAUDECODE_GLOBAL_ONLY_SANDBOX_PATHS behavior). The settings reference documents a Scope per key, and rulesync now skips a key the file it is writing cannot honor, warning once per key:
    • User or managed / User, local, or managed (spellcheck, autoMode, vimInsertModeRemaps, pluginConfigs, ...) — skipped at project scope, emitted under --global.
    • Managed (allowManagedHooksOnly, requiredMinimumVersion, ...) and Global config (diffTool, autoConnectIde, ...) — skipped in both scopes, since neither .claude/settings.json nor ~/.claude/settings.json is the file that reads them.
    • As with the sandbox filtering, only the override copy is filtered; a value already in the target file is left untouched, and the warning says so.
  • Prototype-pollution keys are skipped, consistent with deepMergeRecords.

Codex CLI — [tui]

  • tui joins CODEXCLI_OVERRIDE_KEYS, so it is written verbatim as a top-level config.toml table (shallow-merged with existing siblings, like apps) and re-extracted on import. tui: z.optional(z.looseObject({})) is added to CodexcliPermissionsOverrideSchema so the intent is documented, while staying loose for the tui.keymap.* bindings.
  • Adding it to CODEXCLI_OVERRIDE_KEYS also extends the permissions feature's SHARED_CONFIG_OWNERSHIP declaration for .codex/config.toml, which is the single source both the writer and the gateway read.

Docs

docs/reference/file-formats.md: the Claude Code override callout gains the passthrough rule, the exclusion list and the scope caveat; the Codex CLI callout lists tui.

Validation evidence

Test plan

  • New unit tests in claudecode-permissions.test.ts: unmodeled key passthrough, deep merge preserving siblings, refusal to write hooks/$schema, project-scope drop + warning for a user-scope key, --global emission of the same key, both-scope drop + warning for managed / ~/.claude.json keys, and an import → generate round trip.
  • New unit test in codexcli-permissions.test.ts: [tui] vim_mode_default is written with no "not managed" warning and round-trips on import.
  • The pre-existing assertion that a top-level key is not swept into the override was deliberately inverted — that behavior is the bug this issue reports.
  • pnpm cicheck passes: 375 test files / 8778 tests, plus fmt / oxlint / typecheck / docs-content / gitignore / supported-tools / cspell / secretlint.

Closes #2715

cm-dyoshikawa and others added 8 commits August 23, 2026 07:32
… through the permission override

The claudecode override block validated any key (looseObject) but the writer only copied permissions and sandbox, so editorMode, emojiCompletionEnabled, workflowSizeGuideline, keybindingFlavor and every future top-level settings key was accepted and then silently dropped. Enumerating them one at a time keeps losing the race, so everything in the block other than permission/permissions/sandbox/hooks/$schema is now deep-merged into the top level of settings.json and lifted back on import. Keys the target file cannot honor are skipped with a warning, derived from the per-key Scope column of the settings reference: User-or-managed keys at project scope, Managed and ~/.claude.json keys in both scopes. For Codex CLI, tui joins the override allowlist so [tui] vim_mode_default and the keymap bindings beside it are authorable.
The claudecode passthrough copied any top-level key into .claude/settings.json. Because .rulesync/permissions.jsonc is fetchable, that let a third-party file author apiKeyHelper, statusLine and friends, whose values Claude Code executes. Refuse those keys in both directions and both scopes, warn on the keys that widen trust (env, enabledPlugins, ...), resolve marketplace aliases before the scope check, and harden the key lookups against prototype-chain hits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…Claude Code override

The first refusal missed fileSuggestion, gcpAuthRefresh, subagentStatusLine and policyHelper, and the sandbox branch never reached the top-level filter at all, so sandbox.ripgrep/bwrapPath/socatPath still named an executable a fetched permissions file could pick. Refuse all of them, warn when the override starts sessions in bypassPermissions, say plainly that env can run code, and share one control-character stripper across doctor, fetch and here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add httpHookAllowedEnvVars, allowedHttpHookUrls, permissions.additionalDirectories and the sandbox network proxy ports to the warn-then-write set. Share one traversal helper between the sandbox path filters, drop command-executing sandbox paths on import too, and remove the dead policyHelper scope entry.
Cover the sandbox paths that let commands out of the sandbox or weaken its isolation, the MCP/agent/output-style keys that redirect what a session trusts, and the acceptEdits and auto permission modes. Warn on the filtered sandbox so a path project scope drops is never reported as written, share one traversal helper across the sandbox filters, and strip the plain LRM/RLM marks from logged names again.
…only widen

Claude Code merges a sandbox list across settings scopes rather than replacing it, so filesystem.allowRead, filesystem.allowWrite and network.allowedDomains authored in a project file can only add to the policy. Warn on them, and on disableSkillShellExecution, which re-opens shell execution a user setting closed. Filter prototype keys out of the imported permissions fields too.
…e warn set

sandbox.ignoreViolations maps a command substring to the violations to hide, so the boolean predicate never fired. Warn on allowedMcpServers, whose entries merge into an allowlist deployed elsewhere, on network.allowMachLookup, and on autoMode and the two skip-prompt keys in the scope that honors them.
…sens

The key restricts at true and re-opens inline shell execution at false, so warning on every value misdirected. Add a small predicate table for keys whose loosening value is the absence of a restriction, restore the alphabetical order of the trust table, and correct why the sandbox deny lists are absent from the warn set.
@dyoshikawa
dyoshikawa merged commit bd09686 into main Aug 23, 2026
9 checks passed
@dyoshikawa
dyoshikawa deleted the resolve-issue-2715-claudecode-settings-passthrough branch August 23, 2026 17:08
@dyoshikawa

Copy link
Copy Markdown
Owner Author

@dyoshikawa Thank you!

rudironsoni pushed a commit to rudironsoni/rulesync that referenced this pull request Aug 25, 2026
…trust warnings

Works through the five follow-ups deferred from dyoshikawa#2720.

1. `sandbox.filesystem.allowManagedReadPathsOnly` and
   `sandbox.network.allowManagedDomainsOnly` are documented `Managed`, so
   neither file rulesync writes reads them. They are now dropped in both
   scopes with a warning, the same treatment the `Managed` top-level keys
   already get, instead of being committed as a lock that never applies.

2. The per-key trust warnings from the `permissions` fields, the `sandbox`
   paths and the top-level passthrough are collected and emitted as one
   summary line per file. The reasons are what differ between them; the
   "review this as you would a hook" advice only needs saying once.

3. `allowUnsandboxedCommands` and `autoAllowBashIfSandboxed` keep warning on
   their documented `true` default, now with the reason recorded: a project
   settings file outranks the user file, so an explicit `true` there re-opens
   what a user`s `false` closed.

4. Adds the six candidate keys, each checked against the settings reference:
   `claudeMdExcludes`, `modelOverrides`, `skipWebFetchPreflight`,
   `remoteControlAtStartup`, `prUrlTemplate` and `companyAnnouncements` are
   all scoped `Any file`, so all are written and warned about at the value
   that widens. `remoteControlAtStartup` is the exception: Claude Code
   honors a `false` from a project file but ignores a `true`, so a `true` is
   skipped at project scope and emitted only under --global.

5. Guards the `CLAUDECODE_TRUST_KEY_WIDENING_VALUES` lookup with
   `Object.hasOwn`, so its safety no longer depends on evaluation order.
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.

Claude Code editorMode + Codex CLI tui.vim_mode_default unauthorable in permissions override

2 participants