Skip to content

fix(evals-core): redact credentials in published results - #246

Open
tanya732 wants to merge 1 commit into
mainfrom
feat/add-redaction-in-serializers
Open

fix(evals-core): redact credentials in published results#246
tanya732 wants to merge 1 commit into
mainfrom
feat/add-redaction-in-serializers

Conversation

@tanya732

@tanya732 tanya732 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Description

serializers.ts is the single choke point for everything a run publishes — JSON results, HTML reports, and dashboard traces. redactSecrets already existed (#238), but was only wired into formatCommandTrace for LLM-bound traces.

This PR wires the publish path through the same scrubber across 6 sites and fixes three matcher gaps found by measuring against shipped reports.

Redaction happens during serialization, after graders and scoring, so it cannot affect verdicts or scores. It also keeps the report templates free of redaction logic.

Approach

Extends the existing utils/redact.ts and wires redactSecrets / redactArgs into:

  • formatStep
  • serialiseTrace
  • mapGraders
  • serialiseBaseline
  • serialiseAgent
  • serialiseError

formatStep redacts before truncation so a partial credential cannot be leaked.

Deliberately left readable: client IDs, user IDs, AUTH0_DOMAIN, audiences, and session_id. These are identifiers/configuration rather than credentials and are useful for verifying SDK wiring and correlating traces.

Matcher fixes

  • Numeric VALUE guard: prevents status codes such as 401 and 403 from being incorrectly redacted.
  • redactArgs consults object keys: catches credentials such as { Authorization: 'Bearer …' } while keeping end-anchored matching to avoid false positives like token_endpoint_auth_method.
  • Credential words embedded in tokens: detects cases where a grader echoes a credential-like needle without a nearby key, while requiring a separator-flanked word, 6+ character suffix, and a digit to avoid ordinary identifiers such as reset_password_2fa.

Additional coverage

  • PEM private key blocks
  • Vendor-prefixed keys such as sk-, ghp_, xox...
  • Recursive redactArgs support for nested objects and arrays
  • .env content passed as a string value
  • Non-string primitives remain unchanged

Testing Results

  • evals-core: 573/573 passing (+21 tests)
  • Two shipped reports replayed end-to-end: no credentials left behind
  • Client ID / domain / audience preserved; only fields containing fixture secrets changed
  • 28 PROMPT.md files: 5 correct redactions, 0 false positives
  • Every new matcher has a paired negative test to guard against false positives

Testing

  • This change adds test coverage for new/changed/fixed functionality

Checklist

  • I have added documentation for new/changed functionality...
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not the default branch

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 55 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d0df516e-fd26-4816-869b-5dd748fe537d

📥 Commits

Reviewing files that changed from the base of the PR and between 6aeeb73 and baec173.

📒 Files selected for processing (6)
  • packages/evals-core/src/index.ts
  • packages/evals-core/src/serializers.ts
  • packages/evals-core/src/utils/redact.ts
  • packages/evals-core/tests/redact.test.ts
  • packages/evals-core/tests/serializers-error.test.ts
  • packages/evals-core/tests/traces.test.ts

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.

`serializers.ts` is the single choke point for everything a run publishes —
the JSON results file, the HTML report, the trace rendered in a dashboard —
and none of it was redacted. `redactSecrets` already existed but was wired
only into `formatCommandTrace` for LLM-bound traces.

Wire the publish path through the same scrubber at six sites, and close
three matcher gaps found by measuring against shipped reports.

Redaction runs at serialisation, after graders and the scorer, so it cannot
change a verdict or a score; graders still read the real workspace. It also
keeps the report template free of redaction logic.

Client ids, user ids, `AUTH0_DOMAIN`, audiences and `session_id` are
deliberately left readable: they are identifiers and public configuration,
not credentials, and they are what makes a trace reviewable.

Matcher fixes:

- Numeric guard on `VALUE`. `No token = 401. Valid token but wrong scope =
  403.` published as `No token = [REDACTED SECRET] Valid…`, losing the status
  code and the sentence break while telling a reader a secret was exposed
  where none was. A credential is never a bare number.
- `redactArgs` consults the key. In `{ Authorization: 'Bearer …' }` the
  credential's name is the object key, invisible to the text rules, and the
  value is often too short for the opaque-token floor.
- A rule for a credential word embedded inside a token with no key beside
  it, as in a grader detail that echoes the needle it searched for. Gated on
  a separator-flanked word, a 6+ character suffix and a digit, so
  `reset_password_2fa` and `change_password_v2` keep their values.

New patterns for value-shaped secrets carrying no name: PEM private key
blocks (ordered before the shape rules so the base64 body is not chewed
into, leaving BEGIN/END behind) and vendor-prefixed keys, which sit below
the opaque-token floor and carry no credential word.

`traces.test.ts` used `'x'.repeat(500)` to exercise truncation. That is
indistinguishable from an opaque token and now redacts to the marker,
leaving nothing to truncate, so the fixture is word-broken and the
redaction behaviour is asserted in two separate tests instead.

Verified: 573 tests pass in evals-core. Two shipped reports replayed through
the new code leave no credential behind, with client id, domain and audience
preserved. 28 PROMPT.md files give 5 correct redactions and no false
positives; 123 shipped text fields give none. Every new pattern has a paired
negative test — a false positive here costs a run its security score, since
the judge reads the marker as evidence a secret was exposed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tanya732
tanya732 force-pushed the feat/add-redaction-in-serializers branch from 9f2030c to baec173 Compare August 26, 2026 12:48
@tanya732
tanya732 marked this pull request as ready for review August 26, 2026 12:54
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