Skip to content

fix(ai-code-mode): warn when tool parameters look like secrets#431

Open
AlemTuzlak wants to merge 6 commits intomainfrom
fix/code-mode-secret-warning
Open

fix(ai-code-mode): warn when tool parameters look like secrets#431
AlemTuzlak wants to merge 6 commits intomainfrom
fix/code-mode-secret-warning

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented Apr 8, 2026

Summary

  • Add warnIfBindingsExposeSecrets() that scans tool input schemas for secret-like parameter names (apiKey, token, password, credential, secret, access_key, private_key, auth_token) and emits console.warn during development
  • Code Mode executes LLM-generated code in a sandbox — if a tool's input schema includes parameters that carry secrets, the generated code can access those values and potentially exfiltrate them via tool calls
  • This is a best-effort heuristic warning, not a security boundary — it catches the most common antipattern of passing API keys as tool parameters instead of keeping them in server-side implementations

Test plan

  • 5 test cases covering: secret detection, safe parameter names, multiple patterns, missing inputSchema, api_key/api-key variations
  • All 26 @tanstack/ai-code-mode tests pass

Summary by CodeRabbit

  • New Features
    • Code Mode now warns in development when tool parameter names resemble secrets (e.g., apiKey, token, password). Warning behavior is configurable per instance (warn/throw/ignore or custom handler) and deduplicated to avoid repeated messages.
  • Tests
    • Added comprehensive tests for secret-name detection across schema shapes, traversal, handler behaviors, deduplication, and cyclic schemas.

Code Mode executes LLM-generated code in a sandbox. If a tool's input
schema includes parameters like apiKey, token, or password, the
LLM-generated code can access those values and potentially exfiltrate
them via tool calls.

Add warnIfBindingsExposeSecrets() that scans tool input schemas for
secret-like parameter names and emits console.warn during development.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0405cd94-b357-46ca-8bbe-cfa86a7152b3

📥 Commits

Reviewing files that changed from the base of the PR and between 99163d6 and 2dea51e.

📒 Files selected for processing (5)
  • packages/typescript/ai-code-mode/src/create-code-mode-tool.ts
  • packages/typescript/ai-code-mode/src/index.ts
  • packages/typescript/ai-code-mode/src/types.ts
  • packages/typescript/ai-code-mode/src/validate-bindings.ts
  • packages/typescript/ai-code-mode/tests/validate-bindings.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/typescript/ai-code-mode/tests/validate-bindings.test.ts
  • packages/typescript/ai-code-mode/src/create-code-mode-tool.ts
  • packages/typescript/ai-code-mode/src/validate-bindings.ts

📝 Walkthrough

Walkthrough

A secret-parameter detection system was added to Code Mode tools. It scans tool input schemas for parameter names resembling secrets (e.g., apiKey, token, password) and invokes a configurable handler (warn/throw/ignore/callback) at tool creation for static bindings and at execution for dynamic bindings.

Changes

Cohort / File(s) Summary
Release Documentation
.changeset/fix-code-mode-secret-warning.md
Adds changeset metadata documenting the patch release for the secret-parameter warning feature.
Type Definitions
packages/typescript/ai-code-mode/src/types.ts
Adds optional onSecretParameter?: SecretParameterHandler to CodeModeToolConfig (type-imports SecretParameterHandler).
Validation Utility
packages/typescript/ai-code-mode/src/validate-bindings.ts
New exports: SecretParameterInfo, SecretParameterHandler, and warnIfBindingsExposeSecrets. Implements recursive JSON Schema traversal, limited $ref resolution, tokenized name matching, handler invocation modes (warn/throw/ignore/callback), and optional deduplication via dedupCache.
Tool Integration
packages/typescript/ai-code-mode/src/create-code-mode-tool.ts
createCodeModeTool accepts onSecretParameter, creates a per-instance secretDedupCache, validates static external_* bindings at creation and dynamic (skill-derived) bindings at execution, sharing deduplication across both.
Public API
packages/typescript/ai-code-mode/src/index.ts
Re-exports SecretParameterHandler and SecretParameterInfo from ./validate-bindings via package entrypoint.
Tests
packages/typescript/ai-code-mode/tests/validate-bindings.test.ts
Adds comprehensive Vitest suite covering detection across nested schemas, arrays, unions, $ref resolution, cycles, handler behaviors (warn/throw/ignore/callback), and deduplication semantics.

Sequence Diagram

sequenceDiagram
    actor Dev as Developer
    participant CM as createCodeModeTool
    participant VB as warnIfBindingsExposeSecrets
    participant H as Handler

    Dev->>CM: Create tool with config & static external_* bindings
    activate CM
    CM->>VB: Scan static bindings (pass dedupCache)
    activate VB
    VB->>VB: Traverse schemas, resolve local $defs, detect matches
    VB-->>H: Invoke handler (warn/throw/ignore/callback)
    deactivate VB
    Note over CM: store secretDedupCache
    CM-->>Dev: Return tool
    deactivate CM

    Dev->>CM: Execute tool with dynamic skill bindings
    activate CM
    CM->>VB: Scan dynamic bindings (reuse dedupCache)
    activate VB
    VB->>VB: Traverse schemas + dedupe matches
    alt new secret match
        VB-->>H: Invoke handler with SecretParameterInfo
    end
    deactivate VB
    deactivate CM
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I sniff the names in schema lairs,

I hop through props and nested layers,
token, apiKey, and hidden prayers,
I whisper warnings, gentle cares,
So code may hop with safer fares.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a warning mechanism for tool parameters that resemble secrets.
Description check ✅ Passed The description includes a detailed summary, test plan, and relevant context, though it lacks the structured sections (Changes, Checklist, Release Impact) specified in the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/code-mode-secret-warning

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

🚀 Changeset Version Preview

12 package(s) bumped directly, 21 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-code-mode 0.1.7 → 1.0.0 Changeset
@tanstack/ai-fal 0.6.17 → 1.0.0 Changeset
@tanstack/ai-gemini 0.9.1 → 1.0.0 Changeset
@tanstack/ai-grok 0.6.8 → 1.0.0 Changeset
@tanstack/ai-openai 0.8.1 → 1.0.0 Changeset
@tanstack/ai-openrouter 0.8.1 → 1.0.0 Changeset
@tanstack/ai-react 0.7.15 → 1.0.0 Changeset
@tanstack/ai-solid 0.6.19 → 1.0.0 Changeset
@tanstack/ai-svelte 0.6.19 → 1.0.0 Changeset
@tanstack/ai-vue 0.6.19 → 1.0.0 Changeset
@tanstack/ai-anthropic 0.8.1 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.1.7 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.1.7 → 1.0.0 Dependent
@tanstack/ai-event-client 0.2.7 → 1.0.0 Dependent
@tanstack/ai-groq 0.1.7 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.7 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.7 → 1.0.0 Dependent
@tanstack/ai-ollama 0.6.9 → 1.0.0 Dependent
@tanstack/ai-preact 0.6.19 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.6.1 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.6.1 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.13.0 → 0.14.0 Changeset
@tanstack/ai-client 0.7.14 → 0.8.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-code-mode-models-eval 0.0.10 → 0.0.11 Dependent
@tanstack/ai-devtools-core 0.3.24 → 0.3.25 Dependent
@tanstack/ai-isolate-cloudflare 0.1.7 → 0.1.8 Dependent
@tanstack/ai-vue-ui 0.1.30 → 0.1.31 Dependent
@tanstack/preact-ai-devtools 0.1.28 → 0.1.29 Dependent
@tanstack/react-ai-devtools 0.2.28 → 0.2.29 Dependent
@tanstack/solid-ai-devtools 0.2.28 → 0.2.29 Dependent
ts-svelte-chat 0.1.36 → 0.1.37 Dependent
ts-vue-chat 0.1.36 → 0.1.37 Dependent
vanilla-chat 0.0.34 → 0.0.35 Dependent

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 8, 2026

View your CI Pipeline Execution ↗ for commit 2dea51e

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 56s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-24 10:03:34 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Apr 8, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@431

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@431

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@431

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@431

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@431

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@431

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@431

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@431

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@431

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@431

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@431

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@431

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@431

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@431

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@431

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@431

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@431

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@431

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@431

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@431

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@431

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@431

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@431

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@431

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@431

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@431

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@431

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@431

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@431

commit: 2dea51e

@AlemTuzlak AlemTuzlak requested a review from jherr April 8, 2026 11:45
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/typescript/ai-code-mode/src/validate-bindings.ts (1)

91-136: Optional: shared seen across siblings suppresses reports for duplicated $ref/subschema references.

seen is threaded through the entire traversal, so once a subschema object (e.g. a $defs/Creds target) has been visited via one reference site, any other reference site to the same object is skipped entirely. In practice this means: if creds1 and creds2 both $ref the same definition that contains accessToken, only creds1.accessToken will be reported; creds2.accessToken won't appear in found at all. With the per-instance dedupCache in create-code-mode-tool.ts, the user still gets a warning for the tool, so impact is low — but the reported paramPath list under-represents the real exposure surface.

If you want full coverage, track cycle-breaking per-path (e.g. a Set of object seeded only with ancestors on the current DFS stack, removed after recursion) rather than a single global seen.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-code-mode/src/validate-bindings.ts` around lines 91 -
136, The traversal currently uses a global `seen` that permanently marks visited
subschema objects, which suppresses reporting when the same schema object is
referenced from different paths; in function findSecretParams change `seen` to
act as an ancestor/stack-local cycle detector by removing the schema from `seen`
before returning (i.e., after all recursive calls complete, call
seen.delete(schema)), so the object is only treated as visited for the current
DFS path and other sibling/reference sites will be explored; keep using the same
`seen` Set parameter but ensure you delete the schema at the end of
findSecretParams.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/typescript/ai-code-mode/src/validate-bindings.ts`:
- Around line 91-136: The traversal currently uses a global `seen` that
permanently marks visited subschema objects, which suppresses reporting when the
same schema object is referenced from different paths; in function
findSecretParams change `seen` to act as an ancestor/stack-local cycle detector
by removing the schema from `seen` before returning (i.e., after all recursive
calls complete, call seen.delete(schema)), so the object is only treated as
visited for the current DFS path and other sibling/reference sites will be
explored; keep using the same `seen` Set parameter but ensure you delete the
schema at the end of findSecretParams.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9159be81-e9e3-4194-af32-e06b98bcf0ae

📥 Commits

Reviewing files that changed from the base of the PR and between dc71c72 and 1a287c0.

📒 Files selected for processing (5)
  • .changeset/fix-code-mode-secret-warning.md
  • packages/typescript/ai-code-mode/src/create-code-mode-tool.ts
  • packages/typescript/ai-code-mode/src/types.ts
  • packages/typescript/ai-code-mode/src/validate-bindings.ts
  • packages/typescript/ai-code-mode/tests/validate-bindings.test.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
packages/typescript/ai-code-mode/tests/validate-bindings.test.ts (4)

333-348: Also assert no console.warn on the 'throw' path.

This test doesn’t spy on console.warn, so two things are left unverified/unsafe:

  1. If the implementation happens to call console.warn before throwing, that output leaks into the test runner’s stderr.
  2. The intended contract — handler: 'throw' throws instead of warning — isn’t actually asserted.

Adding a spy and a not.toHaveBeenCalled() assertion tightens the contract:

♻️ Suggested change
   it('handler: "throw" throws on the first match', () => {
+    const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
     expect(() =>
       warnIfBindingsExposeSecrets(
         [
           {
             name: 't',
             inputSchema: {
               type: 'object',
               properties: { apiKey: { type: 'string' } },
             },
           },
         ],
         { handler: 'throw' },
       ),
     ).toThrow(/apiKey/)
+    expect(warnSpy).not.toHaveBeenCalled()
+    warnSpy.mockRestore()
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-code-mode/tests/validate-bindings.test.ts` around
lines 333 - 348, Update the test for handler: "throw" to also spy on
console.warn and assert it was not called: before invoking
warnIfBindingsExposeSecrets create a jest.spyOn(console,
'warn').mockImplementation(() => {}) (or equivalent test spy), run the function
expecting toThrow(/apiKey/), then assert the spy.not.toHaveBeenCalled() and
finally restore the spy (spy.mockRestore()) to avoid cross-test pollution;
reference the test case and the warnIfBindingsExposeSecrets invocation to locate
where to add the spy and assertions.

350-383: Order-dependent assertion on matches[0]/matches[1].

toHaveLength(2) plus indexed toMatchObject assumes the traversal emits apiKey strictly before nested.token. That happens to match insertion order today, but it couples the test to the implementation’s DFS order rather than the observable contract ("each match is reported exactly once"). Consider asserting set-membership instead so a later traversal refactor (e.g., BFS, anyOf reordering) doesn't break the test:

♻️ Suggested change
-    expect(matches).toHaveLength(2)
-    expect(matches[0]).toMatchObject({
-      toolName: 'callApi',
-      paramName: 'apiKey',
-      paramPath: ['apiKey'],
-    })
-    expect(matches[1]).toMatchObject({
-      toolName: 'callApi',
-      paramName: 'token',
-      paramPath: ['nested', 'token'],
-    })
+    expect(matches).toHaveLength(2)
+    expect(matches).toEqual(
+      expect.arrayContaining([
+        expect.objectContaining({
+          toolName: 'callApi',
+          paramName: 'apiKey',
+          paramPath: ['apiKey'],
+        }),
+        expect.objectContaining({
+          toolName: 'callApi',
+          paramName: 'token',
+          paramPath: ['nested', 'token'],
+        }),
+      ]),
+    )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-code-mode/tests/validate-bindings.test.ts` around
lines 350 - 383, The test currently assumes traversal order by checking
matches[0] and matches[1]; update the assertions in the 'handler: function
receives each match' spec to be order-independent: collect the pushed
SecretParameterInfo objects from warnIfBindingsExposeSecrets (the matches array)
and assert that it has length 2 and contains both expected objects using
set-membership style assertions (e.g., expect.arrayContaining or multiple
expect(matches).toContainEqual calls) referencing the same toolName 'callApi'
and paramName/paramPath shapes rather than relying on index-based checks.

80-161: Nice coverage of naming variants — consider one more edge case.

The secretNames / safeNames matrix is thorough. One gap worth adding given the PR’s goal of catching "the common antipattern of passing API keys as tool parameters": a case-insensitive variant of a compound name like OPENAI_API_KEY or SLACK_WEBHOOK_SECRET (SCREAMING_SNAKE_CASE env-style names often copy-pasted into schemas). If the detector already normalizes case, it’s a free test; if it doesn’t, this surfaces a real gap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-code-mode/tests/validate-bindings.test.ts` around
lines 80 - 161, Add tests covering SCREAMING_SNAKE_CASE secret-like names so the
detector handles case-insensitive compound env-style names: extend the
secretNames array used by the warnIfBindingsExposeSecrets tests to include
examples like 'OPENAI_API_KEY' and 'SLACK_WEBHOOK_SECRET' (or other UPPER_SNAKE
variants), then ensure the existing test that expects a warning for each
secretName still passes; reference the warnIfBindingsExposeSecrets call and the
secretNames array in validate-bindings.test.ts to locate where to add these
entries.

7-7: Consider centralizing console.warn spy restoration via afterEach.

Every test manually creates a vi.spyOn(console, 'warn') and calls warnSpy.mockRestore() at the end. If any expect in a test fails, mockRestore() is skipped and the spy leaks into subsequent tests, which can mask or alter assertions (e.g., toHaveBeenCalledTimes in the dedup suite). A single afterEach(() => vi.restoreAllMocks()) (plus removing the per-test mockRestore() calls) makes the suite leak-safe and a bit less repetitive.

♻️ Sketch
 import { describe, expect, it, vi } from 'vitest'
+import { afterEach } from 'vitest'
 ...
+afterEach(() => {
+  vi.restoreAllMocks()
+})

Also applies to: 27-27, 47-47, 71-71, 110-110, 146-146, 165-165, 189-189, 216-216, 248-248, 275-275, 297-297, 314-314, 388-388, 407-407

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/typescript/ai-code-mode/tests/validate-bindings.test.ts` at line 7,
Centralize console.warn spy cleanup by adding an afterEach(() =>
vi.restoreAllMocks()) in the test suite and remove all per-test
warnSpy.mockRestore() calls; keep the existing vi.spyOn(console, 'warn') usages
(e.g., the const warnSpy = vi.spyOn(console, 'warn') lines) but rely on global
restoration so tests won't leak spies and assertions like toHaveBeenCalledTimes
remain reliable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/typescript/ai-code-mode/tests/validate-bindings.test.ts`:
- Around line 333-348: Update the test for handler: "throw" to also spy on
console.warn and assert it was not called: before invoking
warnIfBindingsExposeSecrets create a jest.spyOn(console,
'warn').mockImplementation(() => {}) (or equivalent test spy), run the function
expecting toThrow(/apiKey/), then assert the spy.not.toHaveBeenCalled() and
finally restore the spy (spy.mockRestore()) to avoid cross-test pollution;
reference the test case and the warnIfBindingsExposeSecrets invocation to locate
where to add the spy and assertions.
- Around line 350-383: The test currently assumes traversal order by checking
matches[0] and matches[1]; update the assertions in the 'handler: function
receives each match' spec to be order-independent: collect the pushed
SecretParameterInfo objects from warnIfBindingsExposeSecrets (the matches array)
and assert that it has length 2 and contains both expected objects using
set-membership style assertions (e.g., expect.arrayContaining or multiple
expect(matches).toContainEqual calls) referencing the same toolName 'callApi'
and paramName/paramPath shapes rather than relying on index-based checks.
- Around line 80-161: Add tests covering SCREAMING_SNAKE_CASE secret-like names
so the detector handles case-insensitive compound env-style names: extend the
secretNames array used by the warnIfBindingsExposeSecrets tests to include
examples like 'OPENAI_API_KEY' and 'SLACK_WEBHOOK_SECRET' (or other UPPER_SNAKE
variants), then ensure the existing test that expects a warning for each
secretName still passes; reference the warnIfBindingsExposeSecrets call and the
secretNames array in validate-bindings.test.ts to locate where to add these
entries.
- Line 7: Centralize console.warn spy cleanup by adding an afterEach(() =>
vi.restoreAllMocks()) in the test suite and remove all per-test
warnSpy.mockRestore() calls; keep the existing vi.spyOn(console, 'warn') usages
(e.g., the const warnSpy = vi.spyOn(console, 'warn') lines) but rely on global
restoration so tests won't leak spies and assertions like toHaveBeenCalledTimes
remain reliable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d3cc6a73-a4a6-46a3-a941-1476498eb472

📥 Commits

Reviewing files that changed from the base of the PR and between 1a287c0 and 99163d6.

📒 Files selected for processing (1)
  • packages/typescript/ai-code-mode/tests/validate-bindings.test.ts

Address CR findings on the secret-parameter warning heuristic:

- Recurse into nested object properties, array items, anyOf/oneOf/allOf
  branches, additionalProperties, and `$ref` targets — previously only
  top-level properties were scanned, so `{ auth: { token: string } }`
  slipped through.
- Replace the narrow anchored regex with a two-stage matcher
  (camelCase/snake/kebab word tokenization + compound-substring check)
  so common names now hit: `accessToken`, `bearerToken`, `refreshToken`,
  `sessionToken`, `clientSecret`, `x-api-key`, `openaiApiKey`,
  `passcode`, `pwd`, `jwt`, `Authorization`. Safe names stay safe:
  `tokenizer`, `tokens`, `foreignKey`, `sortKey`, `email`, `username`.
- Add `onSecretParameter` config option with `'warn' | 'throw' |
  'ignore' | fn` variants so consumers can route matches (throw in CI,
  ignore in trusted environments, log to an observability pipeline).
- Dedupe per `(toolName, paramPath)` across a single code-mode instance
  to stop the same binding warning on every execute call.
- Scan dynamic `getSkillBindings()` output too, with the same dedup
  cache; skill bindings are in the same exfiltration threat model.

Tests: 56 cases covering every pattern/safe-name pair, nested +
array + union + $ref + additionalProperties + cycle safety, and each
handler variant + dedup behavior.
@AlemTuzlak AlemTuzlak force-pushed the fix/code-mode-secret-warning branch from 99163d6 to 6287faa Compare April 24, 2026 09:56
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.

1 participant