PII Redaction
June 19, 2026 · View on GitHub
Enable with ENABLE_PII_REDACTION=true. Masks likely PII in read-only query results before returning them.
How It Works
Detection combines:
- Built-in column name list:
email,ssn,phone,first_name,address,credit_card,password,api_key,token, etc. - Regex scanning of values: email, US phone, SSN, IPv4, Luhn-valid credit card numbers
Mask Examples
| Input | Output |
|---|---|
jane.doe@example.com | j***@e***.com |
415-555-0134 | ***-***-0134 |
123-45-6789 | ***-**-6789 |
4111 1111 1111 1111 | ****-****-****-1111 |
192.168.1.42 | ***.***.***.42 |
| Generic PII column value | J******** (first char + up to 8 asterisks) |
Value-level pattern masks run first, so a column matching the heuristic whose value contains an email gets the richer email mask.
Redaction only runs on read-only query results. Schema/table listing and write-operation summaries are unaffected.
Extending Column Detection
Both vars require ENABLE_PII_REDACTION=true and are additive to the built-in list.
PII_EXTRA_COLUMNS — comma-separated substrings, case-insensitive, matched against lowercased column name:
PII_EXTRA_COLUMNS=image_url,signed_url,internal_note
PII_EXTRA_COLUMN_PATTERNS — semicolon-separated JS regex bodies (no / delimiters, compiled with i flag):
PII_EXTRA_COLUMN_PATTERNS=^(signed|protected)_.*;^.*_token$
Use patterns when a substring match is too broad — anchors, alternation, character classes, etc. Invalid patterns are logged to stderr and skipped.
A column is flagged if it matches ANY built-in substring OR any PII_EXTRA_COLUMNS substring OR any PII_EXTRA_COLUMN_PATTERNS regex.
Hardening Guards
When ENABLE_PII_REDACTION=true, three additional pre-execution guards activate:
PII_ALLOW_SELECT_STAR (default: false)
Rejects queries with SELECT * or t.*. Forces explicit column projection so PII columns can't slip through via wildcard. COUNT(*) and aggregates are unaffected.
Set true to allow wildcards (value-level redactor still runs).
PII_ALLOW_REFERENCES (default: false)
Rejects any query referencing a PII-matched column — in projection, WHERE, JOIN ON, GROUP BY, HAVING, ORDER BY, subqueries, CTEs, INSERT ... SELECT. Closes the alias bypass (SELECT CONCAT(first_name, ' ', last_name) AS NAME would otherwise return cleartext). Error message names the offending column(s) so the LLM can self-correct.
Set true to disable (e.g. for ETL pipelines that legitimately need PII columns).
PII_ALLOW_INTROSPECTION (default: false)
Controls schema introspection behavior:
- Filtered (runs, PII rows dropped):
SHOW COLUMNS,SHOW FULL COLUMNS,SHOW FIELDS,DESCRIBE,DESC,EXPLAIN <table>,SHOW INDEX,SHOW INDEXES,SHOW KEYS - Pass-through (runs unchanged):
SHOW TABLES,SHOW TABLE STATUS,SHOW DATABASES,SHOW SCHEMAS,SHOW CHARACTER SET,SHOW COLLATION - Rejected:
SHOW CREATE TABLE,SHOW CREATE VIEW,SELECToninformation_schemaormysql
Use SHOW COLUMNS / DESCRIBE or the mysql://tables/{name} MCP resource instead of SHOW CREATE TABLE.
Set true to bypass entirely and return raw results.
PII_BLOCK_INTROSPECTION (default: false)
Stricter mode — hard-blocks ALL introspection statements including the pass-through kinds above. Matches pre-2.0.3 behavior. Ignored when PII_ALLOW_INTROSPECTION=true.
Known Limitations
- Column aliases (when
PII_ALLOW_REFERENCES=true):SELECT first_name AS fnreturns keyfn, bypassing column-name heuristic. LeavePII_ALLOW_REFERENCES=falseto prevent this. - Numeric-typed PII: SSNs/phones stored as
BIGINT/INTpass through — walker only inspects string values. - JSON columns: Regex-visible PII inside JSON strings (emails, phones, etc.) is masked, but nested fields like
first_nameinside JSON are not. - International formats: IPv6, non-US phones (E.164
+44…), IBANs, EU national IDs, international postal codes — not redacted. - Non-Luhn card numbers: 13–19 digit runs failing Luhn check are left alone (intentional, to avoid clobbering order IDs).
SHOW CREATE TABLE/information_schemaSELECTs: Rejected by default. UseSHOW COLUMNSor the MCP resource instead.- Filtered introspection leaks column count/types of non-PII columns — by design (discovery is the point).
- Write-operation summaries:
Insert successful…strings are not scanned. - Binary/Buffer payloads and Date instances: Passed through as-is; embedded PII not inspected.