Skip to content

fix(redact): env.kv no longer fires on a function call assigned to a credential-shaped name - #2899

Open
Yugz29 wants to merge 1 commit into
garrytan:mainfrom
Yugz29:fix/env-kv-value-shape
Open

Yugz29 wants to merge 1 commit into
garrytan:mainfrom
Yugz29:fix/env-kv-value-shape

Conversation

@Yugz29

@Yugz29 Yugz29 commented Sep 18, 2026

Copy link
Copy Markdown

Problem

The env.kv value capture is [^\s'"]{8,} — any run of non-space, non-quote characters — so it matches code as well as literals. With a credential-shaped name in front (bare session, token, password all pass isCredentialShapedEnvName as bare suffixes), an ordinary source line is a MEDIUM finding whenever the expression's mixed case clears the 3.0-bit entropy gate.

Two pushes in a row were refused on a project where session is a domain word (a local pre-push stage that treats MEDIUM as blocking on a public repo), on these exact lines:

# intelligence/tests/test_core_client_retry.py — a fake requests.Session for a retry test
    session = _FlakySession(requests.ReadTimeout("Read timed out"))
    session = _FlakySession()

# intelligence/pulse_intelligence/cli.py — a lookup, not a secret
    session = find_session(client, args.session_id, now=now, state=state, day=args.date)
env.kv  MEDIUM  Secret-named assignment (env/YAML/JSON) with high-entropy value · _Fla********…
env.kv  MEDIUM  Secret-named assignment (env/YAML/JSON) with high-entropy value · find********…

Neither line contains a secret. The first was worked around by renaming the variable; the second is production code where session is the right name.

Fix

validate sees only the span, never the file, so the calibration is on the span's shape — as a negative test on exactly one code shape: a call, i.e. an identifier or dotted path immediately followed by (.

const ENV_KV_CALL_SHAPED = /^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*\(/;

Nothing else is excluded, deliberately:

  • a character allow-list was tried first and silenced real passwords carrying !#$&* (Tr0ub4dor&3!xyz);
  • excluding dotted attribute paths (settings.API_KEY) silenced passphrases (correct.horse.battery.staple) and fixed no false positive actually met.

Measurements

Four variants scanned side by side (upstream v1.79.0.0, the allow-list attempt, call + dotted-path, call only):

corpus upstream allow-list call + paths call only
27 lines: the blocking lines above, other calls, and literals (hex, base64 ==, JWT, sk-…, %-encoded DSN, five !#$&* passwords, a dotted passphrase) 6 false positives 8 (5 real passwords silenced) 3 (passphrases silenced) 0
the 22 env.kv cases already in test/redact-engine.test.ts 0 0 0 0

Tests

  • env.kv ignores function calls assigned to credential-shaped names: the four calls above plus a FastAPI Depends(...) stay silent.
  • env.kv still fires on literal secrets whatever punctuation they carry: hex, base64 with padding, JWT, sk-…, percent-encoded DSN, five punctuation-heavy passwords, correct.horse.battery.staple (bare and quoted).
  • bun test over the 16 redact/telemetry files: 325 pass, 0 fail. Tier 1 static checks otherwise untouched (no SKILL.md change).

🤖 Generated with Claude Code

…credential-shaped name

The env.kv value capture is `[^\s'"]{8,}` — any run of non-space, non-quote
characters — so it matches code as well as literals. With a credential-shaped
name in front (bare `session`, `token`, `password`), ordinary source lines
raise a MEDIUM confirm on every push:

    session = _FlakySession(requests.ReadTimeout("Read timed out"))
    session = find_session(client, args.session_id, now=now, state=state)
    token = make_token(user, scopes=["read"])
    password = getpass.getpass()

Each value's mixed case clears the 3.0-bit entropy gate. `validate` sees the
span, never the file, so the calibration is on the span's shape — as a
negative test on exactly one code shape: a call, i.e. an identifier or dotted
path immediately followed by `(`. Nothing else is excluded: a character
allow-list would silence real passwords carrying `!#$&*`
(`Tr0ub4dor&3!xyz`), and excluding dotted paths would silence passphrases
(`correct.horse.battery.staple`).

Tests: the four calls above plus a FastAPI `Depends(...)` stay silent; hex,
base64 with padding, JWT, `sk-…`, a percent-encoded DSN, five
punctuation-heavy passwords, a dotted passphrase and every existing env.kv
case still fire. On a project where `session` is a domain word, the pre-push
hook refused two pushes in a row on lines that only construct or look up a
session.
@trunk-io

trunk-io Bot commented Sep 18, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@Yugz29

Yugz29 commented Sep 18, 2026

Copy link
Copy Markdown
Author

Related: #2751. Both address gstack-redact-prepush false positives. #2751 lets a repo exclude deliberate test fixtures; this one stops env.kv from firing on function calls. They touch different files and can land in either order.

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