Repository navigation
fix(integrations): guard automatic prompt capture and preparation - #2174
Conversation
Apply a documented best-effort credential policy across seven prompt adapters. Skip recognized capture inputs as whole events and sanitize only the automatic read-only query. Preserve accepted Source identity and existing capture controls. Add shared synthetic fixtures, hook/HTTP regressions and bilingual guidance. Constraint: No stored evidence rewrite, Source classifier at the Server, event deduplication, or change to automatic Memory evolution. Scope-risk: Detection is best effort and can conservatively skip examples; real-host credential acceptance remains open in issue 2165. Tested: Linux Python plugin/install suites (457 passed, 4 skipped); Windows isolated hooks (42 passed); Kimi/ZCode hook HTTP (63 passed); Pi (121 passed); OpenCode (88 passed); TypeScript/build; manifest (50 passed); full Linux Ty; changed-file pre-commit/Ruff.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
hidb4ai
left a comment
There was a problem hiding this comment.
Recognized credentials still reach automatic preparation requests. Please address the P1 issue below before merging.
| r"""|(?<![\w-])["']?\b(?:api[_ -]?key|access[_ -]?(?:key|token)|client[_ -]?secret""" | ||
| r"""|secret(?:[_ -]?key)?|private[_ -]?key|password|passwd|passphrase|token|authorization|cookie)\b""" | ||
| r"""["']?\s*[:=]\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|`[^`\r\n]*`|[^\s,;}\]]+)""" | ||
| r"|(?<![\w-])bearer\s+[A-Za-z0-9._~+/=-]{8,}(?![\w-])" |
There was a problem hiding this comment.
[P1] Redact complete Authorization and Cookie header values
Authorization: Bearer synthetic-value-2084 becomes [REDACTED] synthetic-value-2084: the assignment branch consumes Bearer before the bearer rule can match its value. Likewise, Cookie: theme=light; session=synthetic-value-2084 becomes [REDACTED]; session=synthetic-value-2084. Capture is skipped, but the remaining credential is still sent in the automatic /v1/context/prepare query, including when capture is disabled.
I reproduced both cases with hook subprocesses and a local HTTP fixture for Claude Code, Codex, WorkBuddy, Kimi and ZCode; direct helper calls confirm the same behavior in Pi and OpenCode. These are adapter-entrypoint checks, not real host-process acceptance.
Please remove the complete recognized credential values before dispatch and add these standard header cases to the shared corpus. The corresponding implementations also need the fix: Codex, WorkBuddy, Kimi, ZCode, Pi, OpenCode.
There was a problem hiding this comment.
Fixed in b7d3c7b. All seven adapters now consume complete Authorization/Cookie header values before single-value assignments, including Proxy-Authorization, Set-Cookie and folded continuation lines. OpenCode bundles were regenerated normally.
The shared corpus includes these headers. The Python hook/HTTP regressions reproduce 30 failures before the fix and pass with capture both enabled and disabled after it. Kimi/ZCode exercise the same enabled/disabled cases through subprocesses and HTTP; Pi/OpenCode share the corpus. Whole-event capture skipping and accepted Source identity/content are preserved; only the automatic query is scrubbed.
Consume Authorization and Cookie headers before single-token assignments, including proxy/set-cookie variants and folded continuations. Add shared header cases and hook/HTTP regressions with capture both enabled and disabled. Constraint: keep automatic capture whole-event skipping and preserve accepted Source identity and contents. Scope-risk: conservative header recognition removes remaining prose on the same line only from automatic read-only queries. Tested: 30 header regressions fail before the fix; Linux hook/usage suite 104 passed; Pi 126 passed; OpenCode 93 passed; Kimi/ZCode 83 passed; both TypeScript checks, OpenCode build, full Linux Ty and changed-file pre-commit checks pass.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
hidb4ai
left a comment
There was a problem hiding this comment.
The previous Authorization/Cookie counterexamples are fixed at this commit. A separate PEM redaction leak remains in the Python adapters; please address it before merging.
| r"""|(?<![\w-])["']?\b(?:api[_ -]?key|access[_ -]?(?:key|token)|client[_ -]?secret""" | ||
| r"""|secret(?:[_ -]?key)?|private[_ -]?key|password|passwd|passphrase|token|authorization|cookie)\b""" | ||
| r"""["']?\s*[:=]\s*(?:"[^"\r\n]*"|'[^'\r\n]*'|`[^`\r\n]*`|[^\s,;}\]]+)""" |
There was a problem hiding this comment.
[P1] Redact entire PEM blocks before assignment prefixes consume their markers
A multiline configuration value such as:
private_key="-----BEGIN PRIVATE KEY-----
synthetic-value-2084
-----END PRIVATE KEY-----"
becomes [REDACTED] PRIVATE KEY-----\nsynthetic-value-2084\n-----END PRIVATE KEY-----". With this combined regex, re.sub chooses the leftmost match: the assignment starts before the PEM block and consumes its BEGIN marker, so the private-key alternative never removes the body. The remaining credential is sent in the automatic /v1/context/prepare query even though capture is skipped.
I reproduced this through Claude Code, Codex and WorkBuddy hook subprocesses with a local HTTP fixture, for both complete and unfinished PEM blocks with capture enabled and disabled. The JS/TS implementations' separate replacement passes avoid this failure.
Please ensure assignment prefixes cannot prevent complete PEM redaction and add these combinations to the shared corpus. The identical Codex and WorkBuddy helpers need the same fix.
There was a problem hiding this comment.
Fixed in 238127c. The three Python adapters now remove whole PEM blocks in a separate pass before assignment or header matching. An earlier prefix therefore cannot consume the BEGIN marker and expose the remaining key body. Complete and unfinished assigned keys and a PEM-valued Authorization header are in the shared corpus.
All 18 new hook-subprocess/local-HTTP cases fail before the fix and pass afterward, with capture both enabled and disabled. They verify that preparation still succeeds and no key body reaches the query or hook output. Shared JS/TS corpus checks also pass. The existing whole-event capture policy and accepted Source content are preserved.
Remove complete or unfinished PEM blocks before assignment and header matching can consume their BEGIN markers. Apply the same sequence to Claude Code, Codex and WorkBuddy and extend the shared corpus. Constraint: retain whole-event capture skipping and normal read-only preparation. Scope-risk: only recognized private-key queries change; accepted Source content and identity remain unchanged. Tested: 18 subprocess/HTTP regressions fail before the fix; Linux Python suites 128 passed; Pi 129, OpenCode 96, Kimi/ZCode 91 passed; TypeScript and changed-file pre-commit hooks including full Python 3.11 Ty passed.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
Which issue or RFC does this PR close?
Closes #2165. Refs #2084 (R14). This implements the shared policy, local adapter regressions, and process-level acceptance for all seven host families.
Rationale for this change
Claude Code, Codex and WorkBuddy automatically captured credential-shaped text without the guards already present in other adapters. Existing capture guards also allowed the same text to leave the host in automatic context-preparation queries. A shared, explicit policy is needed without silently rewriting accepted Source evidence or changing event identity.
What changes are included in this PR?
prepare_bootstrap_context,record_bootstrap_delivery,resolve_artifact_handle,bind_artifact_handle); no Server endpoint is added.Are there any user-facing changes?
Recognized credential-shaped prompts, including conservative placeholders such as
password=example, are skipped for automatic capture. Automatic preparation still runs with recognized fragments replaced by[REDACTED], which can reduce query specificity. Capture remains enabled by default with existing disable controls.Detection is best effort and cannot guarantee that prompts contain no secrets. Operators who prohibit outbound prompts must also disable the automatic preparation hook/extension. Authentication headers still carry the configured credential to the selected Server. Stored Sources/revisions, explicit Server Source acceptance, event identity, Scope authorization and automatic Memory evolution are unchanged.
How was this change tested?
Current Python/Linux 3.11 hook/HTTP and usage-recorder suites: 104 passed (72 privacy cases and 32 recorder cases). All 30 new header regressions fail before the header fix and pass after it, including capture-disabled cases.
Kimi/ZCode hook/HTTP suites: 83 passed using system Node 24.13.1. Header cases run with capture both enabled and disabled.
Pi package: 126 passed; OpenCode package: 93 passed. Both TypeScript checks and normal OpenCode generation pass. Full Linux/Python 3.11 Ty and changed-file pre-commit checks pass.
Twelve-branch integration including the separate Windows bootstrap repair fix(integrations): persist bootstrap receipts on Windows #2177 and the test-only CI repair test(runtime): await usage settlement in rollback isolation test #2179: Windows hook/plugin/install/recorder suites 533 passed, 7 skipped. These combined results do not establish standalone Windows bootstrap readiness without fix(integrations): persist bootstrap receipts on Windows #2177.
Standalone expanded Linux plugin/install suite: 457 passed, 4 skipped before the additional header cases. Standalone expanded Windows originally exposed 14 existing bootstrap receipt replacement failures; fix(integrations): persist bootstrap receipts on Windows #2177 fixes that separate defect and both platforms' focused bootstrap suites pass 46 tests. Integration manifest/generated-document checks pass 50 tests.
Native Pi 0.82.1 on Linux/Node 24.13.1, isolated native dependencies, actual RPC host process and local Server/model HTTP fixtures: 27 scenarios passed with normal process exit on
b7d3c7b9a2e603242da2c95e111be177f079ba79. The scenarios include all 19 corpus cases, a repeated ordinary event with distinct Source identity, and capture-disabled header/ordinary cases. The configured Server credential differs from the synthetic header values so literal credential replacement cannot mask header-recognition failures. Historical context reaches the model fixture.Native Claude Code 2.1.219 on Windows, isolated profile/plugin/MCP configuration and local Server/model HTTP fixtures: 27 scenarios passed with normal process exit on combined Head
57b3e53efafe6f62c866b6d55ecfcefdf97f88c6, including fix(integrations): persist bootstrap receipts on Windows #2177. The test supplies a resolvable nativepython3using the installed Python interpreter. It checks the same corpus, capture-disabled headers, distinct ordinary events and context delivery to the model fixture.Native Codex CLI 0.163.0, Kimi Code 2.1.1, OpenCode 1.18.21, and ZCode CLI 0.16.9 each also passed 27 scenarios with normal exit on Windows, using isolated profiles and the same corpus/capture-disabled checks. Codex installed the local marketplace/plugin and used a loopback Responses fixture. ZCode was built from host commit
29628c9acdb81b703bbd4080c207a0e7ce5e276ewith pnpm 10.33.2 and Node 24.21.0. The other Windows Node probes used 24.13.1. The installed WorkBuddy 37.10.3-42 bundledcodebuddy-lite-wb.mjsalso passed 27 scenarios with normal exit on Node 24.13.1 with an isolated profile and the normally installed hook. These bring the total to 189 native host-process scenarios across seven hosts.Limits: these privacy runs use controlled local HTTP/model fixtures. Live-model quality, persisted Memory, host UI behavior and Windows Pi teardown are not established by them. Earlier failed/partial Pi attempts remain excluded. The separate bounded live-model/persistent-memory observation is recorded in #2186; it does not establish a general privacy or poisoning guarantee.
CI follow-up: #2177 and #2179 are merged. #2179 retains the usage rollback-isolation assertions while giving that test an explicit settlement budget; production deadlines are unchanged. The seekDB 3.11 job on this Head timed out once in a recovery-migration fixture and passed unchanged on rerun (run 39612419500, attempt 2). No production timeout or assertion was weakened, and the retry does not prove the cause of the first timeout.
Test isolation: initial Codex probes incorrectly relied on a URL environment override that its installed
.mcp.jsonoutranks. They are excluded from RED/GREEN evidence. Current tests copy the plugin into an isolated temporary directory and set the fixture MCP endpoint explicitly. Portable Windows Node 24.21.0 also failed an independent localhost/fetch probe; system Node 24.13.1 passed the same probe. Product timeouts were not changed.AI usage statement
Codex was used to implement and review the change and run the reported validation.