Root cause (surfaced by PR #377 review loop)
plugins/autonomy/skills/setup/scripts/check-security-binding.mjs validates two open-ended input classes with static structural recognition and no configured allowlist:
- Credential probe paths (
isCredentialTarget, ~L100-186) — must recognize whether a probed path is a real host credential location.
- Egress targets (
isNonExternalEgressHost, ~L631-678) — must recognize whether a probed host is a provably external address (vs. a non-globally-routable / special-use address whose failed connection proves nothing).
Neither can be decided statically. A static checker with no config source can only pattern-match structure, so for each class the reviewer (chatgpt-codex-connector) can always construct a fresh input that is structurally plausible but semantically meaningless:
- Credential side: an invented home-base path such as
/home/definitely-not-a-host-user/.ssh passes (home is a home-base segment) while the real host credentials remain elsewhere and readable.
- Egress side: each IANA/RFC special-use range must be denied one at a time. The bot has walked the registry across ~29 review cycles (loopback encodings,
::/96, IPv4-compat, 192.88.99.0/24 6to4, fec0::/10 site-local, 192.0.0.0/24 protocol-assignment, and now 100::/64 discard-only per RFC 6666), and unenumerated blocks remain (e.g. 64:ff9b::/96 NAT64, 3fff::/20 documentation).
This is not point-fixable. Each landed denial predictably spawns the next variant in the same class — an adversarial enumeration of an open-ended space, not a converging bug list. 42 findings across 28+ Codex review cycles on PR #377 are all instances of these two root causes.
Convergent fix (needs a human architecture decision)
Replace open-ended structural recognition with deny-by-default + configured allowlists, which the code comments and prior review replies already name as the deferred seam ("org-specific names belong to the future configured allow-list"):
- Credential roots: accept a probe path as evidence only if it resolves under an operator-configured trusted credential root (e.g. a
--credential-roots / config list). No configured root then no static acceptance.
- Egress targets: accept a target as external-egress evidence only if it is on an operator-configured
--egress-hosts allowlist (deny-by-default), rather than trying to enumerate every non-global range to reject.
Under a configured allowlist, both point-denial enumerations become superseded/throwaway, and the validator converges by construction.
Why this is filed rather than fixed inline
PR #377's babysit worker hit the non-convergence fix-round cap (~29 cycles, no realistic path to convergence via point-fixes). The two current-head P1 threads (commit 931771d, check-security-binding.mjs L184 credential path and L659 IPv6 discard-only) are left open and unresolved — they are valid but resolve to this single architectural decision, which a human must make. This issue captures that decision so the threads/PR are not point-fixed into a 30th cycle.
References
Reference: Boris Cherny, "Steps of AI Adoption" (Google Doc)
Root cause (surfaced by PR #377 review loop)
plugins/autonomy/skills/setup/scripts/check-security-binding.mjsvalidates two open-ended input classes with static structural recognition and no configured allowlist:isCredentialTarget, ~L100-186) — must recognize whether a probed path is a real host credential location.isNonExternalEgressHost, ~L631-678) — must recognize whether a probed host is a provably external address (vs. a non-globally-routable / special-use address whose failed connection proves nothing).Neither can be decided statically. A static checker with no config source can only pattern-match structure, so for each class the reviewer (chatgpt-codex-connector) can always construct a fresh input that is structurally plausible but semantically meaningless:
/home/definitely-not-a-host-user/.sshpasses (homeis a home-base segment) while the real host credentials remain elsewhere and readable.::/96, IPv4-compat,192.88.99.0/246to4,fec0::/10site-local,192.0.0.0/24protocol-assignment, and now100::/64discard-only per RFC 6666), and unenumerated blocks remain (e.g.64:ff9b::/96NAT64,3fff::/20documentation).This is not point-fixable. Each landed denial predictably spawns the next variant in the same class — an adversarial enumeration of an open-ended space, not a converging bug list. 42 findings across 28+ Codex review cycles on PR #377 are all instances of these two root causes.
Convergent fix (needs a human architecture decision)
Replace open-ended structural recognition with deny-by-default + configured allowlists, which the code comments and prior review replies already name as the deferred seam ("org-specific names belong to the future configured allow-list"):
--credential-roots/ config list). No configured root then no static acceptance.--egress-hostsallowlist (deny-by-default), rather than trying to enumerate every non-global range to reject.Under a configured allowlist, both point-denial enumerations become superseded/throwaway, and the validator converges by construction.
Why this is filed rather than fixed inline
PR #377's babysit worker hit the non-convergence fix-round cap (~29 cycles, no realistic path to convergence via point-fixes). The two current-head P1 threads (commit
931771d,check-security-binding.mjsL184 credential path and L659 IPv6 discard-only) are left open and unresolved — they are valid but resolve to this single architectural decision, which a human must make. This issue captures that decision so the threads/PR are not point-fixed into a 30th cycle.References
feat/autonomy-guardrails)931771d, two P1 findings (L184, L659)Reference: Boris Cherny, "Steps of AI Adoption" (Google Doc)