Two strings. Same pixels. Different bytes.
A compiled-Rust toolkit that canonicalizes and neutralizes adversarial Unicode — homoglyph spoofing, bidi / Trojan-Source, zalgo, and invisible characters — before it reaches your classifiers, indexes, logs, and identifiers. One core, wrapped by bindings that read the way each ecosystem writes.
worked example —
strip_obfuscation()
run it live in the tools
input
сheсkout
canonical
checkout
| offset | codepoint | char | name | action |
|---|---|---|---|---|
| 0 | U+0441 | с | CYRILLIC SMALL LETTER ES | FOLD → c |
| 1 | U+0068 | h | LATIN SMALL LETTER H | PASS |
| 2 | U+0065 | e | LATIN SMALL LETTER E | PASS |
| 3 | U+0441 | с | CYRILLIC SMALL LETTER ES | FOLD → c |
| 4–7 | U+006B… | kout | LATIN SMALL LETTERS | PASS |
What it catches, and what it will not claim
confusable
Adversarial-text defense
TR39 confusable folding, bidi / zero-width / control stripping, and zalgo capping — the Unicode attack surface most pipelines never check.
how it works →
engine
Fast, compiled core
A Rust engine with compile-time perfect-hash tables and one boundary crossing per call. No regex, and no per-character loops in the host language.
benchmarks →
safety
Safe by construction
unsafe_code = "forbid" across the entire codebase. Memory safety isn't traded for speed — it's a property of the build.
architecture →
bindings
One core, six languages
Python, Rust, Node, Ruby, Java and Kotlin, plus a C ABI for everything else. Each binding is written in its own ecosystem's idiom rather than transliterated from the Rust, so none of them reads like a wrapper.
all bindings →
coverage
Broad coverage
Transliteration, slugification, filename safety, and Unicode normalization across 83 built-in language profiles and many scripts.
language support →
migration
Drop-in friendly
Compatibility aliases for Unidecode, python-slugify, and pathvalidate make migration a one-line change.
migration guides →
scope
Honest about scope
disarm normalizes input. It is not an output sanitizer: encode at your sink. The threat model states exactly what is and isn't covered.
threat model →
Run any of it in the browser — nothing is uploaded
Each tool is disarm compiled to WebAssembly and run inside your browser. Every one reports exactly what it changed — on hostile Unicode the before and after usually look identical.
- 01Remove invisible characters
- 02Detect Trojan Source
- 03Check confusable characters
- 04Detect zalgo text
- 05Detect script spoofing
- 06Normalize Unicode whitespace
- 07Sanitize a filename
- 08Slugify any language
- 09Which cleanup do I need?
- 10Why don’t these two strings match?
- 11Truncate without breaking emoji
- 12Which of these names are the same name?
- 13Is this safe to write to a log?
Scope
“A defense-in-depth layer, not a complete control.” disarm reduces a specific, enumerated attack surface — and documents the rest.