disarm

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

8 codepoints · 2 scripts · mixed Latin/Cyrillic

canonical

checkout

collides with the name a user already registered

offsetcodepointcharnameaction
0U+0441сCYRILLIC SMALL LETTER ESFOLD → c
1U+0068hLATIN SMALL LETTER HPASS
2U+0065eLATIN SMALL LETTER EPASS
3U+0441сCYRILLIC SMALL LETTER ESFOLD → c
4–7U+006B…koutLATIN SMALL LETTERSPASS

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.

All tools →

Scope

“A defense-in-depth layer, not a complete control.” disarm reduces a specific, enumerated attack surface — and documents the rest.