Repository navigation
feat(ai): add secret shield for outbound credential redaction - #58410
GiacomoSaccaggi wants to merge 3 commits into
Conversation
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 72 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
There was a problem hiding this comment.
The feature doesn't work as described yet. I ran the added tests (85 pass), type-checked ai and core (clean), and called the shield directly on sample requests.
Public API: adds the secret_shield config key (off|warn|mask|block), the OPENCODE_SECRET_SHIELD environment variable, and new @opencode/ai exports (applySecretShield, resolveSecretShieldMode, SecretShieldMode, ShieldResult, ShieldReport). Nothing is removed or changed, so it is backwards compatible. As wired now, though, the config key has no effect.
Main concerns (details inline):
- Config key not applied:
secret_shieldin config is never read. Only the environment variable works. - Tool results not scanned: only
textparts are scanned, so output frombash/readtools, the main way credentials reach the model (e.g.cat .env), is sent unmodified. Tool-call inputs and reasoning parts aren't scanned either. - False positives in mask mode: the prefix patterns have no left boundary.
risk-assessment-module,disk-usage-monitorandtask-scheduler-serviceare all masked, which corrupts code the model sees and then edits. - Package layering:
packages/aireadsprocess.envdirectly. The mode should be resolved in core, from config plus environment, and passed in. - Tests: at 562 lines and 85 tests, the test file is very large for what it covers. Please cut it down to one case per pattern family plus the mode behaviours, and add a test for tool-result redaction and for the config key actually taking effect.
Related: #51907
18ccf4d to
beb2e24
Compare
There was a problem hiding this comment.
The new commits have exactly the same content as the previous head (git diff between the two is empty), so none of the earlier points are addressed and those threads stay open. The added tests still pass (85/85). Public API impact is unchanged since the last review: adds the secret_shield config key, the OPENCODE_SECRET_SHIELD env var and new @opencode/ai exports (backwards compatible).
9fecf3a to
06ba5d3
Compare
There was a problem hiding this comment.
The earlier points are addressed: the config key is now read (with the env var as fallback), tool results, tool-call inputs and reasoning are scanned, the prefix patterns are anchored, and packages/ai no longer reads process.env. Tests (21/21) and tsc on ai and core pass. Public API impact is unchanged from the last review: adds the secret_shield config key, the OPENCODE_SECRET_SHIELD env var and new @opencode/ai exports, now applySecretShield (mode required), isSecretShieldMode and types. Backwards compatible.
One new problem with the new scanning: masking structured tool-call inputs or json tool results turns the object into a JSON string, which changes the shape sent to providers (see inline).
06ba5d3 to
e637f90
Compare
There was a problem hiding this comment.
The earlier points are addressed: tool-call inputs and json/error tool results now keep their structure, and only the string values inside them are redacted. Tests (23/23) and tsc on ai pass. Public API impact is unchanged since the last review: it adds the secret_shield config key, the OPENCODE_SECRET_SHIELD env var and new @opencode/ai exports, all backwards compatible.
One new gap: because the walker only looks at string values, plain secrets under sensitive keys in structured values (e.g. {password: "…"}) are no longer detected (see inline).
Scan LLM requests for credential patterns before sending to providers. Four modes: off (default), warn, mask, block. Detects 16 API key prefix families, JSON sensitive fields, HTTP auth headers, connection string URIs, and JWT tokens. Scans text, tool-result, reasoning, and tool-call content parts. Structured values are walked recursively to redact string leaves and values under sensitive keys while preserving object shapes. Left-anchored prefix patterns prevent false positives inside identifiers. Mode is an explicit argument; config and environment resolution belongs to core.
24 tests covering pattern families, false-positive boundaries, tool-result redaction (text, json, structured sensitive keys), reasoning scanning, tool-call input structure preservation, message and tool-description scanning, and all four mode behaviours. Runner: mode resolved from config with env var fallback; shield applied before steps.attempt().
e637f90 to
582e791
Compare
There was a problem hiding this comment.
The last point is addressed: plain secrets under sensitive keys (password, token, …) in structured tool-call inputs and json tool results are masked again, case-insensitively and including arrays under those keys, and the value's shape is kept. Tests (24/24) and tsc on ai pass. Public API impact is unchanged since the last review: it adds the secret_shield config key, the OPENCODE_SECRET_SHIELD env var and new @opencode/ai exports, all backwards compatible. No new concerns.
There was a problem hiding this comment.
The new commit only merges the latest dev into this branch. The pull request's own changes are the same as before, and the shield tests (24/24) and tsc on ai and core still pass on the merged code. Public API impact is unchanged: it adds the secret_shield config key, the OPENCODE_SECRET_SHIELD env var and new @opencode/ai exports, all backwards compatible. No new concerns.
|
This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window. Feel free to open a new pull request that follows our guidelines. |
Issue for this PR
Closes #50904
Type of change
What does this PR do?
Adds an opt-in credential scanning/redaction layer that intercepts outbound LLM requests before they reach providers. The shield scans system prompts, user messages, tool-result outputs (bash/read), tool-call inputs, reasoning parts, and tool descriptions for credential patterns.
Detection: 16 API key prefix families (OpenAI, Anthropic, GitHub, AWS, Stripe, Slack, etc.), JSON sensitive field names (password, token, api_key, etc.), HTTP auth headers, connection string URIs, and JWT tokens. Prefix patterns are left-anchored to avoid false positives inside identifiers like
risk-assessment-module.Structured value handling: Tool-call inputs and JSON tool results are walked recursively — only string leaves are redacted, preserving object shapes so providers receive valid structured data. Values under sensitive keys (password, token, etc.) are masked unconditionally.
Four modes (via
secret_shieldconfig key orOPENCODE_SECRET_SHIELDenv var):off(default) — no scanningwarn— scan and log detections, send unmodifiedmask— replace detected credentials with[REDACTED]block— reject the entire request if any credential detectedMode resolution lives in core (config first, env var fallback). The ai package takes an explicit
SecretShieldModeargument with no side effects.Files changed:
packages/ai/src/secret-shield.ts— redaction enginepackages/ai/src/index.ts— public exportspackages/schema/src/config.ts—secret_shieldconfig fieldpackages/core/src/session/runner/llm.ts— shield wired after request preparation, beforesteps.attempt()packages/ai/test/secret-shield.test.ts— 24 testsPort of #50905 to the V2 architecture.
How did you verify your code works?
bun typecheckclean on packages/ai, packages/schema, packages/core.applySecretShieldon sample requests with real credential patterns in all content part types.Screenshots / recordings
N/A — no UI changes.
Checklist