Skip to content

fix(list-secrets): mask secret values by default (opt-in via includeValues) - #22

Open
Shyrka973 wants to merge 1 commit into
Infisical:mainfrom
Shyrka973:fix/list-secrets-mask-values-by-default
Open

fix(list-secrets): mask secret values by default (opt-in via includeValues)#22
Shyrka973 wants to merge 1 commit into
Infisical:mainfrom
Shyrka973:fix/list-secrets-mask-values-by-default

Conversation

@Shyrka973

Copy link
Copy Markdown

Closes #21.

What

Add an includeValues: boolean parameter (default false) to list-secrets. When false, the response only contains secretKey for each secret; the secretValue field is omitted entirely. Also add a valuesMasked: <bool> flag to the response for agent self-introspection.

Why

See #21 for the full rationale. TL;DR: list-secrets is consumed by LLMs and unconditionally returning secretValue causes accidental leaks to provider logs on every exploratory call. get-secret doesn't have this issue because the caller names what it wants.

Changes

Single file: src/index.ts (+15/-9 lines).

  1. Zod schema: add includeValues: z.boolean().default(false)
  2. JSON schema (inputSchema.properties): add includeValues field with safety-oriented description
  3. Description of the capability: clarify default-masked behavior
  4. Handler: replace inline mapping with mapSecret helper that conditionally includes secretValue based on data.includeValues
  5. Response: add valuesMasked: !data.includeValues flag

Testing

Tested at three levels before opening this PR:

  1. Syntax: node --check dist/index.js → OK after npm run build
  2. Unit: mapper called in isolation. includeValues=false{secretKey: "X"}. includeValues=true{secretKey: "X", secretValue: "..."}
  3. Live stdio: spawned the freshly-built MCP via stdio with a real Infisical instance, called list-secrets on a path with 2 known secrets:
    • No includeValues → response: {"valuesMasked":true,"secrets":[{"secretKey":"OPENAI_API_KEY"},{"secretKey":"OAUTH_CLIENT_SECRET"}]}
    • includeValues: true → response: {"valuesMasked":false,"secrets":[{"secretKey":"OPENAI_API_KEY","secretValue":"..."},{"secretKey":"OAUTH_CLIENT_SECRET","secretValue":"..."}]}

This patched binary has been deployed locally as a workaround in my environment since 2026-05-26.

The repo has no test suite, so I've stopped here — happy to add one if you'd like guidance on the framework you'd prefer.

Backward compatibility

This is a breaking change for callers that relied on values being present unconditionally. As discussed in #21, I'd suggest bumping to 0.1.0 to signal the change.

Related

@greptile-apps

greptile-apps Bot commented May 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an includeValues: boolean parameter (defaulting to false) to the list-secrets tool, masking secret values by default to prevent accidental exposure in LLM provider logs. The change is focused and mechanically correct — a shared mapSecret helper is introduced and applied consistently to both top-level secrets and imported secrets.

  • Masking logic: mapSecret closes over data.includeValues and correctly omits secretValue when false; the ...imp spread in the imports path is immediately overridden by secrets: parsedImportSecrets, so no raw values leak through.
  • Response shape: valuesMasked: !data.includeValues is always present, giving agents a self-introspection signal.
  • Breaking change: Callers that relied on unconditional secretValue presence will now receive keys only unless they opt in — the PR author flags this and suggests a semver bump to 0.1.0.

Confidence Score: 4/5

Safe to merge; the masking is applied correctly across both the direct secrets list and the imported-secrets path, with no data leaking through the spread.

The diff is small and self-contained. The mapSecret helper is shared across both code paths (top-level and imports), so there is no risk of one path masking while the other does not. The only callout worth tracking before merging is the intentional breaking change: existing consumers will stop receiving secretValue without opt-in, so a version bump as suggested by the author would reduce surprises for downstream users.

No files require special attention; the single changed file (src/index.ts) is straightforward and the logic is easy to audit.

Important Files Changed

Filename Overview
src/index.ts Adds includeValues boolean parameter (default false) to list-secrets, introduces a mapSecret helper to conditionally strip secretValue, and surfaces a valuesMasked flag in the response. Masking is correctly applied to both top-level secrets and imported secrets via the shared helper.

Reviews (1): Last reviewed commit: "fix(list-secrets): mask secret values by..." | Re-trigger Greptile

@infisical-cla-app

infisical-cla-app Bot commented Jul 14, 2026

Copy link
Copy Markdown

CLA satisfied. All contributors have signed the current CLA. The cla/signed check is passing.

…alues)

Add a new `includeValues` boolean parameter to `list-secrets`, defaulting
to false. When false (the default), the response only contains `secretKey`
for each secret; the `secretValue` field is omitted entirely. Callers that
genuinely need values must explicitly pass `includeValues: true`.

Why
---
The previous behavior unconditionally serialized `secretValue` for every
secret in the path. Because this MCP is consumed by LLMs (Claude, GPT,
etc.), a single `list-secrets` invocation typically deposits the entire
path's values into the model's context window and from there into the
provider's logs for the duration of their retention policy.

This is asymmetric vs `get-secret`, which requires the caller to name the
secret it wants (leaks are bounded). `list-secrets` is exploratory by
design — agents call it when they don't yet know what's there, which is
exactly when they shouldn't see values.

Also add a `valuesMasked: <bool>` flag to the response so agents can
self-detect whether they've received only keys.

Tested
------
- Syntax: `node --check dist/index.js` OK
- Unit: mapper isolated; `includeValues=false` -> `{secretKey: X}`,
  `includeValues=true` -> `{secretKey: X, secretValue: ...}`
- Live stdio (real Infisical instance):
  - default -> response has `valuesMasked: true`, no `secretValue` field
  - `includeValues: true` -> response has `valuesMasked: false`, full values

This is a breaking change for callers relying on `secretValue` being
present unconditionally; suggest bumping to 0.1.0 in the release.
@Shyrka973
Shyrka973 force-pushed the fix/list-secrets-mask-values-by-default branch from d362272 to ee59040 Compare July 14, 2026 16:17
@Shyrka973

Copy link
Copy Markdown
Author

CLA signed — all checks are green now.

Flagging this as ready for review. It closes #21, and has been running as a patched local build in my environment since May with no issues.

(I force-pushed earlier today to correct a wrong author email on the commit — the CLA bot was attributing it to someone else's account. The diff is unchanged.)

Happy to add tests if you'd like — the repo has no suite today, so let me know which framework you'd prefer.

@roborew

roborew commented Jul 20, 2026

Copy link
Copy Markdown

Just spent some time realising the same issue with using this MCP, I would consider this a critical security patch. Although there are other ways to secure infisical from exposing keys/values. This should be in place by default as a defensive security measure as misconfiguration could expose sensitive vars to the LLM. Giving this a bump, thanks for the work @Shyrka973! @varonix0 Is this something you can take a look at soon?

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.

[security] list-secrets always returns secretValue — accidental leaks to LLM context

2 participants