Skip to content

chore: unify secret-redaction key matching in one shared helper#290

Merged
Colin Francis (colifran) merged 2 commits into
langchain-ai:mainfrom
bikeusaland:security/unify-secret-redaction
Jul 13, 2026
Merged

chore: unify secret-redaction key matching in one shared helper#290
Colin Francis (colifran) merged 2 commits into
langchain-ai:mainfrom
bikeusaland:security/unify-secret-redaction

Conversation

@bikeusaland

@bikeusaland Greg Land (bikeusaland) commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Problem

Three independent secret-redaction implementations had drifted apart, each matching a different set of secret-bearing key terms:

Location Terms matched
cli.tsx isSecretLikeKey api-key, authorization, bearer, token, secret, password
agent/index.ts sanitizeOpenRouterResponseBody (inline) api-key, authorization, bearer, password, secret, token, user_id
mcp-runtime.ts isSecretLikeKey token, secret, password, authorization, api-key, cookie

Because the lists differed, a value redacted on one path could leak through another — e.g. a user_id field is redacted in an OpenRouter response body but not in MCP tool output, and a cookie field is redacted in MCP output but not in the CLI diagnostics path. Redaction is a security boundary; whether a credential gets hidden should not depend on which code path happens to handle it.

Fix

Consolidate into a single exported isSecretLikeKey plus a shared SECRET_KEY_PATTERN_SOURCE in diagnostics.ts — already the documented, well-tested redaction module (test/redaction.test.ts). The unified term list is the union of all three:

api[-_]?key | authorization | bearer | token | secret | password | user_id | cookie

All three consumers now route through it:

  • cli.tsx — imports the shared predicate, local copy removed.
  • connectors/mcp-runtime.ts — imports the shared predicate, local copy removed.
  • agent/index.ts sanitizeOpenRouterResponseBody — builds its JSON-key regex from SECRET_KEY_PATTERN_SOURCE, so the body sanitizer and the key predicate can never drift again.

Net effect: every path now redacts the strict superset of what any path did before — no key that was previously hidden is now exposed.

Tests

  • isSecretLikeKey catches every term (including user_id and cookie, previously matched by only one path each), case-insensitively, and does not flag benign keys.
  • sanitizeOpenRouterResponseBody redacts user_id/cookie values while preserving non-secret fields.

Full gate passes: typecheck, lint:check, format:check, test suite (213 tests, +9 new).

Scope note

This PR intentionally unifies only the secret-key predicate. The related isRecord duplication (and the array-vs-record nuance in formatToolArgs) touches ~40 stream-parsing call sites and is left for a separate change to keep this security fix small and low-risk.

🤖 Generated with Claude Code

Related PRs

Part of a batch from a single codebase-review pass. Independent, main-based branches that can merge in any order.

Security

Correctness

Feature

(This PR is #290.)

Three independent secret-redaction implementations had drifted apart:

- cli.tsx:        api-key|authorization|bearer|token|secret|password
- agent/index.ts: api-key|authorization|bearer|password|secret|token|user_id
- mcp-runtime.ts: token|secret|password|authorization|api-key|cookie

Because the term lists differed, a key redacted on one path (e.g. user_id
in an OpenRouter body, or cookie in MCP output) could leak through
another. Redaction is a security boundary; it must not depend on which
code path happens to handle a value.

Consolidate into a single exported isSecretLikeKey plus a shared
SECRET_KEY_PATTERN_SOURCE in diagnostics.ts (the documented,
well-tested redaction module), using the union of all previous terms.
Route cli.tsx, mcp-runtime.ts, and the sanitizeOpenRouterResponseBody
JSON-key regex through it.

Add tests asserting the unified predicate catches every term (including
the ones previously matched by only one path) and that
sanitizeOpenRouterResponseBody redacts user_id/cookie while preserving
non-secret fields.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@colifran Colin Francis (colifran) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice Greg Land (@bikeusaland)! This was on my radar as well. Thanks for the PR!

@colifran Colin Francis (colifran) changed the title security: unify secret-redaction key matching in one shared helper chore: unify secret-redaction key matching in one shared helper Jul 13, 2026
@colifran
Colin Francis (colifran) merged commit 77aafde into langchain-ai:main Jul 13, 2026
9 checks passed
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.

2 participants