Skip to content

fix(health): keep credentials out of the response sample we persist - #1596

Open
GeiserX wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
GeiserX:fix/health-response-sample-scrub
Open

fix(health): keep credentials out of the response sample we persist#1596
GeiserX wants to merge 1 commit into
UsefulSoftwareCo:mainfrom
GeiserX:fix/health-response-sample-scrub

Conversation

@GeiserX

@GeiserX GeiserX commented Aug 15, 2026

Copy link
Copy Markdown

A health check's response sample is written to connection.last_health, so whatever it carries is persisted. The operation being probed is chosen by the user from the plugin's catalog — so it can just as easily be a key-listing endpoint as a /me.

extractResponseFields walks every scalar leaf of the body with no filtering at all. A /me that returns an api_key, or a GET /keys, puts that value straight into the database.

The field immediately next to it already knew about this problem. detail is scrubbed of every credential value, and the comment there says why — upstream text can echo the request back. Error bodies are excluded from sampling for the same reason. So the surface was thought about; responseSample was the one left out.

The admin plane also already handles it, narrowing its read to {status, checkedAt} with a comment naming this exact leak. This brings the stored value in line with what the admin read assumed was safe.

The fix

Two directions, because they catch genuinely different things:

By key. Leaves whose key names a credential (token, secret, api_key, session, …) are redacted in the walker. This is the half that matters most: it covers secrets we have never seen. A key-listing endpoint returns different secrets than the one we authenticated with, and no scrub of a known value can possibly find those.

By value. The connection's own credential value is then scrubbed from what remains, using the same helper detail already uses. That covers a credential sitting under an innocent-looking key.

The row is kept and only its value replaced, so the preview still shows the response shape and the identity picker keeps working.

How I tested it

  • packages/core/sdk: 602 passed, 45 files. packages/plugins/openapi: 268 passed, 45 files.
  • The new tests use real response bodies of the shape these endpoints return — a /me, a nested key list, an array of {name, token} pairs — rather than synthetic shapes.
  • One test asserts the identity fields (email, login, id) still come through untouched, since blanking those would make the feature useless.
  • One asserts author is not redacted while authorization is. An over-eager substring match is the obvious way to get this wrong, so it has its own test.
  • One is a positive control on an entirely unredacted body — without it, an extractor that returned nothing, or redacted everything, would satisfy every other assertion.
  • Mutation check: I neutered the redaction (keeping it compiling) and re-ran. Three tests went red, including the author boundary one; the positive control correctly stayed green.
  • oxlint on all three changed files: 0 warnings, 0 errors.

A health check stores its response sample in connection.last_health, and the
operation being probed is picked by the user from the plugin's catalog. Point
it at a key-listing endpoint, or a /me that returns an api_key, and those
values land in the database.

The field right next to it already knew about this: `detail` is scrubbed of
every credential value, with a comment saying upstream text can echo the
request back. `responseSample` was left out of that scrub.

Two directions, because they catch different things. Leaves whose KEY names a
credential are redacted in the walker — that covers secrets we have never
seen, which is exactly what a key-listing endpoint returns and what no scrub
of a known value could find. The connection's own credential value is then
scrubbed from what remains, covering a secret under an innocent key.

The row is kept and its value replaced, so the preview still shows the shape.
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