diff --git a/lib/redact-patterns.ts b/lib/redact-patterns.ts index 76b81f3d27..95d9791294 100644 --- a/lib/redact-patterns.ts +++ b/lib/redact-patterns.ts @@ -138,6 +138,36 @@ function looksLikeWallet(span: string): boolean { return span.length >= 26 && span.length <= 62; } +// Compact log/backup stamps (`20260727202423` = YYYYMMDDHHMMSS) are bare digit +// runs that the phone regex happily eats. Only a SEPARATOR-FREE 14-digit span +// qualifies: E.164 tops out at 15 digits and real numbers carry a + or spacing, +// so rejecting this shape costs no phone coverage. +function looksLikeCompactTimestamp(span: string): boolean { + if (!/^\d{14}$/.test(span)) { + return false; + } + const n = (from: number, to: number) => Number(span.slice(from, to)); + const [year, month, day, hour, minute, second] = [ + n(0, 4), + n(4, 6), + n(6, 8), + n(8, 10), + n(10, 12), + n(12, 14), + ]; + return ( + year >= 1900 && + year <= 2999 && + month >= 1 && + month <= 12 && + day >= 1 && + day <= 31 && + hour <= 23 && + minute <= 59 && + second <= 59 + ); +} + // ── Placeholder suppression (per-matched-span, NOT per-line) ───────────────── /** @@ -431,7 +461,8 @@ export const PATTERNS: RedactPattern[] = [ regex: /(?", - validate: (span) => span.replace(/\D/g, "").length >= 10, + validate: (span) => + span.replace(/\D/g, "").length >= 10 && !looksLikeCompactTimestamp(span), }, { id: "pii.ssn", diff --git a/test/redact-engine.test.ts b/test/redact-engine.test.ts index b52c630d54..d4b9d1fffb 100644 --- a/test/redact-engine.test.ts +++ b/test/redact-engine.test.ts @@ -185,8 +185,9 @@ describe("PII patterns", () => { scan("bob@acme.co", { repoVisibility: "private", repoPublicEmails: ["bob@acme.co"] }).findings, ).toHaveLength(0); }); - test("phone E.164", () => { + test("phone E.164 flags, skips compact timestamps", () => { expect(ids("call +14155550123 now")).toContain("pii.phone.e164"); + expect(ids("backup stamp 20260727202423 ran late")).not.toContain("pii.phone.e164"); }); test("ssn flags valid, skips 000 octet", () => { expect(ids("ssn 123-45-6789")).toContain("pii.ssn");