[object Object]

← back to Cli Printing Press

fix(cli): tighten phone-us PII regex to NANP-valid first digits (#1405)

15964974d79e0366e641ea1bbb90af11282c9f90 · 2026-05-14 12:54:07 -0700 · Trevin Chow

* fix(cli): tighten phone-us PII regex to NANP-valid first digits

The phone-us detector matched any 10-digit numeric of the shape
NNN[sep]?NNN[sep]?NNNN, which false-positived on 10-digit product UPCs
that start with 0 (`0190074442`) and coordinate-shaped numerics like
longitudes that start with 1 (`106.0512973`). Both shapes appeared in
the upstream DataForSEO OpenAPI spec and tripped the publish gate on a
spec the operator can't edit.

NANP requires the area code and exchange code to each start with 2-9 —
leading 0 or 1 is not a real US phone number. Constraining each quadrant
to `[2-9]\d{2}` filters out the documented false positives plus the
whole class of leading-0/1 numerics (epoch timestamps, latitudes/
longitudes 0-199, ISBNs, UPCs with check-digit position 0) without
losing any real US phone shape — every existing positive test still
passes.

Closes #1149

* test(cli): rename phone-us test to reflect actual area code 212

Files touched

Diff

commit 15964974d79e0366e641ea1bbb90af11282c9f90
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Thu May 14 12:54:07 2026 -0700

    fix(cli): tighten phone-us PII regex to NANP-valid first digits (#1405)
    
    * fix(cli): tighten phone-us PII regex to NANP-valid first digits
    
    The phone-us detector matched any 10-digit numeric of the shape
    NNN[sep]?NNN[sep]?NNNN, which false-positived on 10-digit product UPCs
    that start with 0 (`0190074442`) and coordinate-shaped numerics like
    longitudes that start with 1 (`106.0512973`). Both shapes appeared in
    the upstream DataForSEO OpenAPI spec and tripped the publish gate on a
    spec the operator can't edit.
    
    NANP requires the area code and exchange code to each start with 2-9 —
    leading 0 or 1 is not a real US phone number. Constraining each quadrant
    to `[2-9]\d{2}` filters out the documented false positives plus the
    whole class of leading-0/1 numerics (epoch timestamps, latitudes/
    longitudes 0-199, ISBNs, UPCs with check-digit position 0) without
    losing any real US phone shape — every existing positive test still
    passes.
    
    Closes #1149
    
    * test(cli): rename phone-us test to reflect actual area code 212
---
 internal/artifacts/pii.go      | 10 +++++++---
 internal/artifacts/pii_test.go | 14 ++++++++++++++
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/internal/artifacts/pii.go b/internal/artifacts/pii.go
index 97189c27..57acfc53 100644
--- a/internal/artifacts/pii.go
+++ b/internal/artifacts/pii.go
@@ -109,9 +109,13 @@ var piiDetectors = []piiDetector{
 	{
 		kind: PIIKindPhoneUS,
 		// US-shaped 10-digit phone with optional +1 prefix and
-		// parens/separators. Word boundaries on each end. Catches
-		// "(415) 555-0123", "415-555-0123", "+1 415 555 0123".
-		pattern: regexp.MustCompile(`\b(?:\+?1[\s.\-]?)?\(?\d{3}\)?[\s.\-]?\d{3}[\s.\-]?\d{4}\b`),
+		// parens/separators. NANP requires the area code and the
+		// exchange code to each start with 2-9 — leading 0 or 1 is
+		// not a real US phone. That constraint filters out 10-digit
+		// product UPCs (`0190074442`) and coordinate-shaped numerics
+		// (`106.0512973`) without rejecting any real phone shape.
+		// Catches "(415) 555-0123", "415-555-0123", "+1 415 555 0123".
+		pattern: regexp.MustCompile(`\b(?:\+?1[\s.\-]?)?\(?[2-9]\d{2}\)?[\s.\-]?[2-9]\d{2}[\s.\-]?\d{4}\b`),
 	},
 	{
 		kind: PIIKindZipPlus4,
diff --git a/internal/artifacts/pii_test.go b/internal/artifacts/pii_test.go
index 794453d0..d6a3f82a 100644
--- a/internal/artifacts/pii_test.go
+++ b/internal/artifacts/pii_test.go
@@ -72,6 +72,20 @@ func TestFindPII_PhoneUS(t *testing.T) {
 		{name: "country-code", line: `"phone": "+1 415 555 0123"`, expectKinds: []string{PIIKindPhoneUS}},
 		{name: "version-string", line: `"version": "1.2.3"`, expectKinds: nil},
 		{name: "ip-address", line: `"addr": "192.168.1.1"`, expectKinds: nil},
+		// NANP-shape filters — area code and exchange code must each
+		// start with 2-9. Regression for the dataforseo retro: 10-digit
+		// product UPCs and coordinate-shaped numerics false-positived.
+		{name: "no-product-upc-leading-zero", line: `"upc": "0190074442"`, expectKinds: nil},
+		{name: "no-coordinate-leading-one", line: `"lng": 106.0512973`, expectKinds: nil},
+		{name: "no-epoch-timestamp", line: `"updated_at": 1700000000`, expectKinds: nil},
+		// Boundary cases that prove the constraint is on the leading
+		// digit of each quadrant, not on the whole string.
+		{name: "no-area-code-leading-zero", line: `"phone": "015-555-0123"`, expectKinds: nil},
+		{name: "no-area-code-leading-one", line: `"phone": "115-555-0123"`, expectKinds: nil},
+		{name: "no-exchange-leading-zero", line: `"phone": "415-055-0123"`, expectKinds: nil},
+		{name: "no-exchange-leading-one", line: `"phone": "415-155-0123"`, expectKinds: nil},
+		{name: "area-code-212-valid", line: `"phone": "(212) 555-0123"`, expectKinds: []string{PIIKindPhoneUS}},
+		{name: "area-code-900-valid", line: `"phone": "(900) 234-5678"`, expectKinds: []string{PIIKindPhoneUS}},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {

← fdca9f7a fix(cli): make cookie-auth login --chrome work on Windows (#  ·  back to Cli Printing Press  ·  fix(cli): derive doctor probe path from auth.verify_path or d0226602 →