Skip to content

fix(transformabl-core): ReDoS in email PII regex + scan cap (0.4.1)#30

Open
davidcrowe wants to merge 4 commits into
mainfrom
fix/transformabl-redos
Open

fix(transformabl-core): ReDoS in email PII regex + scan cap (0.4.1)#30
davidcrowe wants to merge 4 commits into
mainfrom
fix/transformabl-redos

Conversation

@davidcrowe

Copy link
Copy Markdown
Collaborator

Security fix (HIGH) — ReDoS in PII email detector

The email pattern used unbounded quantifiers around a single mandatory @:

/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g

This is O(n²): a long alphanumeric run (no @ needed) forces the engine to scan-to-end then backtrack from every start position. Measured: 100k→4.4s, 200k→16.8s; a ~1MB body blocks the event loop for minutes. detectPii runs on the full input with no length cap, and the transformabl middleware feeds it JSON.stringify(req.body) — so one crafted request freezes a worker; a handful = DoS. This is an agent/LLM gateway, so large bodies are normal.

Fix

  • Bound the email quantifiers to RFC 5321 limits (local ≤64, domain ≤255, TLD ≤24) → constant work per start position (effectively linear).
  • Add a 512 KB scan-length cap in detectPii (defense-in-depth).
  • Verified: 300k-char attack input 4413ms → 39ms, real emails still detected.

Note

This branch also carries the previously-unpushed 0.3.0→0.4.0 work that is already live on npm — origin/main was behind (0.3.0) while npm shipped 0.4.0. This PR reconciles git to npm and adds the fix as 0.4.1. Squash-merge (protected main disallows merge commits), then npm publish [email protected].

Refs security review 2026-07-01.

Prepares for Presidio-parity and HIPAA-relevant recognizers by widening
the PiiType union and adding regulatory-category mappings for the new
types. Patterns themselves land in the next commit.

New types:
- US identifiers: us_bank_number, us_itin, us_passport, us_drivers_license
- International banking: iban
- Healthcare (HIPAA PHI): icd_10, icd_9, npi
- Financial: crypto_wallet

Regulatory mapping adds PCI (bank accounts, IBAN) and HIPAA (all three
healthcare types) attribution so classify.ts can report compliance
categories in ContentMetadata.
Extends detectPii with recognizers that Microsoft Presidio ships by
default (US-focused) plus healthcare-specific additions that are
HIPAA-standard but not covered by Presidio's core list.

Added:
- us_bank_number: 8-17 digits (loose by design)
- us_itin: 9XX-[78]X-XXXX — more specific than SSN pattern
- us_passport: 9 digits, or 1 letter + 8 digits
- us_drivers_license: letter + 7-8 digits, or prefixed form
- iban: ISO 13616 (2 letters + 2 digits + 11-30 alphanum)
- icd_10: letter + 2 chars + optional .Xn (up to 4 decimals)
- icd_9: 3 digits + optional .XX, or V/E codes
- npi: 10-digit starting with 1 or 2
- crypto_wallet: BTC (P2PKH + bech32) + ETH (0x + 40 hex)

Extended:
- phone: now matches E.164 international (+<country><digits>)
- ip_address: now matches IPv6 full and compressed forms

Motivation: ACP's content governance layer silently passed through
bank account numbers and ICD diagnosis codes (surfaced by an external
multi-agent privacy benchmark, AgentLeak). These are standard PII
categories in financial and healthcare contexts — a governance layer
that misses them can't credibly claim PII coverage.

Coverage surfaces via existing detectPii() API; no consumer changes
required beyond bumping the dep pin.

26 new unit tests; all 68 tests pass.
Minor version because the PiiType union is additive (no breaking
changes for existing consumers) but the set of types returned by
detectPii() has grown — callers that hard-code a closed set of
expected types should review.
….4.1)

Unbounded quantifiers around a single mandatory @ made the email pattern
O(n^2): a long alphanumeric run (no @ needed) forced scan-to-end-then-
backtrack from every start position — ~1MB blocked the event loop minutes.
Bound to RFC 5321 limits (local<=64, domain<=255, tld<=24) + a 512KB
detectPii scan cap. Verified: 300k-char attack 4413ms -> 39ms, emails still
detected. Security review 2026-07-01.
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