Skip to content

Safe literal secret references: x-kody-secret-resolution opt-out header and inert placeholder convention - #676

Merged
kentcdodds merged 1 commit into
mainfrom
cursor/secret-resolution-opt-out-4ed0
Jul 8, 2026
Merged

kentcdodds merged 1 commit into
mainfrom
cursor/secret-resolution-opt-out-4ed0

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 8, 2026 •

Copy link
Copy Markdown
Owner

Why

The fetch gateway resolves {{secret:...}} placeholders on the final serialized request β€” URL, headers, and body β€” with no escape mechanism. Any prose that mentions the placeholder syntax (a Discord message, an issue body, docs written via API) either fails closed ("Secret … was not found") or, worse, resolves a real secret into third-party-visible content. Discovered while shipping #674: a summary message about the placeholder syntax could not be sent through the gateway.

What

  • Opt-out header: x-kody-secret-resolution: off disables placeholder resolution for one gateway fetch. The gateway strips the header before the request leaves, placeholders pass through literally, and no secret is resolved. Value on (or omitting the header) keeps normal behavior; unknown values fail loudly so a typo cannot silently re-enable resolution. Out-of-band by design: only calling code can set a header, so attacker-controlled data in a URL or body can never disable resolution.
  • Inert mention convention: documented {{secret:<name>}} as the way to mention placeholder syntax in prose β€” angle brackets are outside the placeholder name charset ([a-zA-Z0-9._-]), so the form can never resolve, in this request or any later one.
  • Docs updated in docs/use/secrets-and-values.md (new "Mentioning placeholders without resolving them" section), docs/use/execute.md, and docs/guides/secret-backed-integration.md, replacing the vague "obfuscate it" guidance with both concrete options and when to use each.
System recap β€” extends existing primitives (medium risk)

Mode: recap Β· Base: main @ f456bebf Β· Head: 30be6328

Classification: extends β€” the fetch gateway's secret-resolution contract gains an explicit, header-scoped opt-out; no primitives added.

Primitives touched

Primitive Group Impact
secrets storage extends β€” gateway resolution gains x-kody-secret-resolution: off opt-out

System map

flowchart LR
	capabilitiesExecute["capabilities-execute"]:::untouched
	packageRuntime["package-runtime"]:::untouched
	secrets["secrets"]:::extended
	thirdParty["third-party API"]:::untouched
	capabilitiesExecute --> secrets
	packageRuntime --> secrets
	secrets --> thirdParty
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Before / after

before: body contains "{{secret:name}}"  β†’ resolved (or fails: secret not found)
        no way to send the literal text through the gateway

after:  header x-kody-secret-resolution: off
        β†’ header stripped, placeholders pass through literally, zero resolution
        prose mention β†’ use inert {{secret:<name>}} form (never resolves)

Invariants

Secret isolation is preserved: the opt-out can only prevent resolution, never widen it. The header cannot be injected via data (headers are code-controlled), and unknown header values throw instead of silently resolving.

Testing

  • New unit tests in fetch-gateway.node.test.ts: opt-out sends placeholders literally with the header stripped and resolveSecret never called; on resolves normally and strips the header; unknown values throw.
  • npm run validate green (format, lint, typecheck, 688 unit tests, Playwright E2E, MCP E2E).
Open in WebΒ Open in CursorΒ 

Summary by CodeRabbit

  • New Features

    • Added a way to keep secret placeholders literal in selected requests using an opt-out header.
    • Improved documentation for safely mentioning placeholder syntax in prose without triggering resolution.
  • Bug Fixes

    • Tightened handling so invalid opt-out header values now produce a clear error instead of being accepted.
    • Clarified when placeholder resolution happens, helping prevent accidental exposure in chat, logs, and issue text.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. πŸŽ‰

ℹ️ Recent review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f15658ac-49d4-480e-85a9-4654f70db3ac

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between f456beb and 30be632.

πŸ“’ Files selected for processing (5)
  • docs/guides/secret-backed-integration.md
  • docs/use/execute.md
  • docs/use/secrets-and-values.md
  • packages/worker/src/mcp/fetch-gateway.node.test.ts
  • packages/worker/src/mcp/fetch-gateway.ts

πŸ“ Walkthrough

Walkthrough

Adds an x-kody-secret-resolution header to the MCP fetch gateway allowing callers to opt out of secret placeholder resolution for a single request. Implements a mode-reading helper, an early-return bypass path in expandSecretPlaceholders, new tests, and documentation describing inert placeholder syntax and the opt-out escape hatch.

Changes

Secret resolution opt-out feature

Layer / File(s) Summary
Opt-out header and mode reader
packages/worker/src/mcp/fetch-gateway.ts
Adds exported secretResolutionHeaderName constant and readSecretResolutionMode helper that reads/strips the header, returning 'on'/'off' or throwing on invalid values.
Early-return bypass logic
packages/worker/src/mcp/fetch-gateway.ts
Reorders baseUrl validation and adds an early-return path forwarding requests unchanged when resolution mode is 'off', skipping placeholder resolution and host approval checks.
Opt-out behavior tests
packages/worker/src/mcp/fetch-gateway.node.test.ts
Adds tests for 'off' (no resolution, header stripped), 'on' (normal resolution, header stripped), and invalid header value rejection.
Placeholder syntax and opt-out documentation
docs/guides/secret-backed-integration.md, docs/use/execute.md, docs/use/secrets-and-values.md
Documents the anti-leak rule, the inert {{secret:<name>}} prose form, and the x-kody-secret-resolution: off escape hatch.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant expandSecretPlaceholders
  participant readSecretResolutionMode
  participant SecretService

  Client->>expandSecretPlaceholders: fetch request with x-kody-secret-resolution header
  expandSecretPlaceholders->>readSecretResolutionMode: readSecretResolutionMode(headers)
  readSecretResolutionMode-->>expandSecretPlaceholders: 'off' or 'on'
  alt mode is off
    expandSecretPlaceholders-->>Client: forwarded Request (placeholders literal)
  else mode is on
    expandSecretPlaceholders->>SecretService: resolveSecret(placeholder)
    SecretService-->>expandSecretPlaceholders: resolved secret value
    expandSecretPlaceholders-->>Client: forwarded Request (resolved)
  end
Loading

Possibly related PRs

  • kentcdodds/kody#85: Both PRs modify expandSecretPlaceholders in fetch-gateway.ts and its tests to change secret placeholder handling during request processing.
  • kentcdodds/kody#452: Both PRs modify secret-placeholder expansion logic in the same expandSecretPlaceholders function.
  • kentcdodds/kody#605: Both PRs modify the secret-resolution flow within expandSecretPlaceholders in fetch-gateway.ts.
πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title accurately summarizes the main change: literal secret placeholders via an opt-out header and inert placeholder syntax.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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 cursor/secret-resolution-opt-out-4ed0

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.

@kentcdodds
kentcdodds marked this pull request as ready for review July 8, 2026 13:50
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

πŸ”Ž Preview deployed: https://kody-pr-676.kody-a99.workers.dev

Worker: kody-pr-676
D1: kody-pr-676-db
KV: kody-pr-676-oauth-kv

Mocks:

@kentcdodds
kentcdodds merged commit 26e2e57 into main Jul 8, 2026
8 checks passed
@kentcdodds
kentcdodds deleted the cursor/secret-resolution-opt-out-4ed0 branch July 8, 2026 13: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.

2 participants