fix(transformabl-core): ReDoS in email PII regex + scan cap (0.4.1)#30
Open
davidcrowe wants to merge 4 commits into
Open
fix(transformabl-core): ReDoS in email PII regex + scan cap (0.4.1)#30davidcrowe wants to merge 4 commits into
davidcrowe wants to merge 4 commits into
Conversation
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.
This was referenced Jul 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security fix (HIGH) — ReDoS in PII email detector
The email pattern used unbounded quantifiers around a single mandatory
@: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.detectPiiruns on the full input with no length cap, and the transformabl middleware feeds itJSON.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
detectPii(defense-in-depth).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.