`auto_review.policy` silently replaces the catalog security policy despite being described as additional instructions
What version of Codex CLI is running?
codex-cli 0.147.0
Source reproduction: commit e9be887f57c8e2d4f606c36890b692d03f328726.
What subscription do you have?
Pro Lite (5x)
Which model were you using?
gpt-5.6-sol; approval review uses the codex-auto-review catalog entry.
What platform is your computer?
Darwin 25.6.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
Ghostty through tmux 3.6a.
Codex doctor report
{
"codexVersion": "0.147.0",
"relevantChecks": {
"config.load": "ok",
"sandbox.helpers": "ok"
},
"note": "Full report omitted because the reproduction is independent of auth, network, and local state."
}
What issue are you seeing?
The Rust config type and generated JSON schema describe local [auto_review].policy as:
Additional policy instructions inserted into the guardian prompt.
The public configuration reference calls it “Local Markdown policy instructions for automatic review,” but does not warn that setting it removes the model catalog policy.
The implementation treats any nonblank local policy as a complete replacement:
ConfigToml.auto_review.policyis normalized intoConfig.guardian_policy_config.- Guardian prompt construction selects that value or the catalog policy:
let tenant_policy_config = parent_config
.guardian_policy_config
.as_deref()
.or_else(|| catalog_auto_review.and_then(|messages| messages.policy.as_deref()))
.unwrap_or(BUNDLED_GUARDIAN_POLICY);
Consequently, adding one narrow local rule can silently remove the catalog policy's rules for sensitive-data egress, credential probing, persistent security weakening, and destructive actions. The policy template still defaults low- and medium-risk actions to allow, so an attempted tightening can weaken unrelated review categories.
Managed guardian_policy_config may intentionally be a complete organization-policy override. The issue is that local [auto_review].policy is collapsed into the same override field, losing the distinction between managed replacement and user-supplied “additional” instructions.
What steps can reproduce the bug?
From the codex-rs workspace at the commit above:
- Confirm the schema wording:
$ rg -n -C 2 'Additional policy instructions' codex-rs/core/config.schema.json
347- "properties": {
348- "policy": {
349: "description": "Additional policy instructions inserted into the guardian prompt.",
350- "type": "string"
- Confirm local policy is loaded into the complete-override field:
$ cargo test -p codex-core load_config_uses_auto_review_guardian_policy_config -- --nocapture
test config::tests::load_config_uses_auto_review_guardian_policy_config ... ok
- Confirm that field displaces the catalog policy in the final prompt:
$ cargo test -p codex-core guardian_review_session_config_prefers_managed_policy_and_uses_catalog_template -- --nocapture
test guardian::review_session::tests::guardian_review_session_config_prefers_managed_policy_and_uses_catalog_template ... ok
Together, these existing tests deterministically prove replacement. A direct regression test can inject LOCAL_POLICY_SENTINEL and CATALOG_POLICY_SENTINEL and assert that both appear in base_instructions; the catalog assertion fails today.
What is the expected behavior?
Local customization should not silently remove the catalog security policy.
Preferably:
- managed
guardian_policy_configremains an explicit complete override; - local
[auto_review].policyis appended to the effective catalog/bundled policy; - prompt construction preserves whether a policy came from managed requirements or local additions;
- a regression test asserts that both local and catalog sentinels appear.
If complete replacement is intended locally, the field and documentation should say so explicitly, for example policy_override, with a prominent warning that it removes catalog safeguards. A separate safe additional_policy setting would still be useful.
Additional information
I searched open/closed issues and pull requests for auto_review.policy, guardian_policy_config, “Additional policy instructions,” custom/catalog Guardian policy, and Guardian prompt overrides. No existing issue covers this contract.
Related but distinct:
- #30598: per-prefix human reviewer routing under global auto-review.
- #37930: Guardian treating user transcript messages as untrusted.
- #25570: canonical reviewer-name serialization.
- PR #18959: introduced user-customizable auto-review policy config.
- PR #32875: preserved
guardian_policy_configprecedence when adding catalog policies, but did not address collapsing local policy into that complete-override field.
Official configuration reference: https://developers.openai.com/codex/config-reference
1 Comment
Confirmed still present on
main@ 1f41cc5d92 — the selection is now factored intoConfig::resolve_guardian_policy, same replacement semantics:https://github.com/openai/codex/blob/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1/codex-rs/core/src/config/mod.rs#L1454-L1466
guardian_policy_config → catalog policy → bundled policyis a first-match chain, so any nonblank local[auto_review].policyfully shadows the catalog's sensitive-egress/credential/destructive-action rules — while the schema doc string promises "additional policy instructions." Your key point stands: this makes an attempted tightening a silent loosening of everything the catalog covers, which is the worst failure direction for a security reviewer config.Fix shape: keep two distinct inputs — managed
guardian_policy_configstays a full override (that's its contract), but local[auto_review].policyshould be appended to whichever base wins the managed/catalog/bundled selection, matching its documented semantics. One function change plus a doc line; a test asserting the catalog rules survive alongside a local addition would pin it. Same docs-say-safe-but-isn't family as #39085, for whoever triages both.