fix(redact): keep the credential heuristics off ordinary files

Path layer: patterns now match with a right boundary (must not continue
into [a-z0-9]) and list only store-plural forms (credentials/secrets/
passwords, not tokens), so tokenizer.ts / dev.environment.ts /
style.keys.ts / api-tokens.md are no longer masked whole while
.env.local / credentials.json / mytoken.yaml still are.

Content layer: drop the bare key/auth/token/pass/bearer field names —
they label far more ordinary content (keyboard keys, CSS custom
properties, lexical tokens) than secrets; qualified names stay and
Bearer schemes remain covered by BEARER_RE.

Adds the over-masking regression cases; fixes the spec's source import
extension to .ts (repo convention).
This commit is contained in:
Menghuan1918
2026-09-06 23:46:46 +08:00
parent 03e19c4cd4
commit 04693192df
2 changed files with 52 additions and 15 deletions
+22 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'
import { isSensitivePath, redactContentText, redactText, REDACTED } from '../src/client/redact.js'
import { isSensitivePath, redactContentText, redactText, REDACTED } from '../src/client/redact.ts'
describe('isSensitivePath', () => {
it('marks credential-shaped filenames', () => {
@@ -15,6 +15,17 @@ describe('isSensitivePath', () => {
expect(isSensitivePath('docs/readme.md')).toBe(false)
expect(isSensitivePath('package.json')).toBe(false)
})
it('does not mask files whose names merely CONTAIN a pattern inside a longer word', () => {
expect(isSensitivePath('src/tokenizer.ts')).toBe(false)
expect(isSensitivePath('config/dev.environment.ts')).toBe(false)
expect(isSensitivePath('src/style.keys.ts')).toBe(false)
expect(isSensitivePath('docs/api-tokens.md')).toBe(false)
})
it('still masks pattern matches at a word boundary', () => {
expect(isSensitivePath('deploy/.env.local')).toBe(true)
expect(isSensitivePath('app/mytoken.yaml')).toBe(true)
expect(isSensitivePath('config/passwords.yml')).toBe(true)
})
})
describe('redactContentText', () => {
@@ -43,6 +54,16 @@ describe('redactContentText', () => {
const code = 'const x = 42\nconsole.log("count", x)'
expect(redactContentText(code)).toBe(code)
})
it('leaves generic bare-word assignments alone (key/auth/token/pass label ordinary content too)', () => {
expect(redactContentText("key: 'ArrowUp'")).toBe("key: 'ArrowUp'")
expect(redactContentText('--key: red')).toBe('--key: red')
expect(redactContentText('token: a lexical unit')).toBe('token: a lexical unit')
expect(redactContentText('auth: none')).toBe('auth: none')
})
it('still masks qualified secret field names', () => {
expect(redactContentText('access_token: ghp_abcdefghijklmnopqrst')).toBe('access_token: ' + REDACTED)
expect(redactContentText('client_secret: shhh-1234567890')).toBe('client_secret: ' + REDACTED)
})
})
describe('redactText', () => {