normgap
Rewrites a labelled set in seven Unicode ways a person reads the same, runs it through Presidio, llm-guard, NeMo Guardrails and a rail of my own, and measures what each loses and what normalising first wins back.
- Year
- 2026
- Kind
- Security · Python
- Status
- Shipped

A person reads 'Card 4111 1111 1111 1111' as a card number. Presidio's credit-card recognizer returns nothing for it, because four digits are fullwidth. normgap measures that gap. It rewrites 404 synthetic items with seven families of human-equivalent rewrites, each tied to a named Unicode rule and property-tested, and scores every item before and after.
Undefended, the drop was material in 14 of 19 tested cells, at all five seeds. The worst: Presidio lost 66 of 88 items to punctuation and digit variants, because its email pattern needs an ASCII @ and dot. Normalising the text before detection brought 11 of the 14 to zero loss, but it was written knowing the families, so that is a consistency check, not a result.
The limits are stated. The set is small and has one author, the detectors run their pattern rules only, and rewrites land at random rather than where an attacker would aim. A payload counts only if its interpreter still reads it the same way. That rule removed 141 of 143 losses on NeMo Guardrails' shipped injection rules.
