Skip to main content

disarm

PyPI Crates.io Documentation License: MIT

Identify malicious attacks hiding in text.

раypal.com — Cyrillic а (U+0430) and р (U+0440) — renders identically to paypal.com and is a different string. disarm finds that substitution and folds it back to its Unicode TR39 prototype, strips bidi overrides, zero-width and control characters, and flags spoofed hostnames — the Unicode layer your validation, dedup, moderation and logging code is missing.

One pure-Rust core, with bindings for Python, Rust, Ruby, Node.js, Java/Kotlin and C.

from disarm import canonicalize, is_suspicious_hostname

# U+202E is a right-to-left override and U+200B a zero-width space. Neither is
# visible, and both survive a copy-paste straight into your database.
assert canonicalize("\u202eexample\u200b.com") == "example.com"

# U+0397 is Greek capital eta and U+13D4 Cherokee letter wa. They render as H and W.
assert canonicalize("\u0397ello \u13d4orld") == "Hello World"

# Cyrillic small a (U+0430) standing in for Latin a: renders as "apple.com".
suspicious, analysis = is_suspicious_hostname("\u0430pple.com")
assert suspicious and analysis.canonical == "apple.com"

Try it in your browser · Documentation · API reference

Install

pip install disarm      # Python 3.10+   (wheels for Linux, macOS, Windows)
cargo add disarm        # Rust 1.81+     (pure Rust — no Python, no pyo3)
npm install disarm      # Node.js 14+
gem install disarm      # Ruby 3.1+

disarm capabilities

from disarm import canonicalize, collapse_whitespace, slugify, strip_obfuscation, transliterate

# Cyrillic er (U+0440) and es (U+0441) folded to Latin p and c — visual (TR39) mapping.
assert strip_obfuscation("\u0440rodu\u0441t") == "product"

# No-break space (U+00A0), ideographic space (U+3000), thin space (U+2009) and a
# line separator (U+2028) all collapse to one plain ASCII space.
assert collapse_whitespace("Ada\u00a0\u3000Lovelace\u2009\u2028King") == "Ada Lovelace King"

# Their zero-width look-alikes are not whitespace at all — U+200B and U+FEFF are
# format characters, so neither str.split() nor collapse_whitespace touches them.
assert collapse_whitespace("A\u200bB\ufeffC") == "A\u200bB\ufeffC"
assert canonicalize("A\u200bB\ufeffC") == "ABC"

# Phonetic romanization: a different mapping, and not a defence.
assert transliterate("Київ", lang="uk") == "Kyiv"
assert slugify("Héllo Wörld") == "hello-world"

Performance & benchmarks

Does it work? On the XMR confusable-recovery metric, disarm's visual mapping scores 0.63–0.68, against ≤ 0.19 for phonetic transliterators (unidecode, anyascii, uroman) and 0.10 for NFKC. → the evidence · what it misses

What does it cost? ~450M chars/sec on Latin (~38× Unidecode), ~106M on Cyrillic, ~712K slugs/sec (~10–24× python-slugify), ~65 ns for an already-ASCII call. Hardware-dependent and directional, not guarantees. → full results · how to read them · where disarm is slower

Both come from "Fire Extinguishers Full of Gasoline": 435,864 observations over eight tools, six attack types, three tasks and two model architectures. Zenodo · CITATION.cff

Bindings: one core, six languages

Each binding reads like its own ecosystem — snake_case in Ruby, camelCase and .d.ts in Node, builders in Java — over one shared core, so every language returns the same answer.

Language Package Getting started
Python 3.10+ disarm on PyPI guide
Rust 1.81+ disarm on crates.io guide · docs.rs
Ruby 3.1+, RubyGems 3.3.22+ disarm on RubyGems guide
Node.js 14+ disarm on npm guide
Java / Kotlin dev.disarm:disarm, dev.disarm:disarm-kotlin on Maven Central guide
C / other FFI C ABI and disarm.h bindings/cabi

Wheels, gems and addons are precompiled — no local Rust toolchain needed. The core crate is unsafe_code = "forbid" and stays pure Rust; BINDINGS.md is the bar a new binding has to meet.

Limitations: read this before deploying disarm

  • Defense in depth, not a complete control. disarm folds the confusables it bundles and strips the format characters it enumerates. The confusable space is larger than any table, so measure your residue with unmapped_confusables() rather than inferring it. Threat model.
  • Not an output sanitizer. disarm normalizes input. It performs no escaping — <script>alert(1)</script> passes through unchanged, and NFKC can even surface ASCII metacharacters from fullwidth look-alikes. Keep encoding at the output sink (framework auto-escaping, DOMPurify, parameterized queries); run disarm before it.
  • transliterate() is not a security control. It romanizes phonetically. For homoglyph defense use normalize_confusables() / strip_obfuscation().

CONFUSABLES_VERSION reports which confusables.txt release the bundled tables were folded from, so a deployment can answer "am I stale?" without inferring it from behaviour (provenance).

Found a bypass? Report it under the security policy rather than in a public issue.

Links

Documentation https://docs.disarm.dev/
Source code https://github.com/raeq/disarm
PyPI package https://pypi.org/project/disarm/
Rust crate https://crates.io/crates/disarm
Issue tracker https://github.com/raeq/disarm/issues
Security policy https://github.com/raeq/disarm/blob/main/SECURITY.md
Contributing https://github.com/raeq/disarm/blob/main/CONTRIBUTING.md
Changelog https://github.com/raeq/disarm/blob/main/CHANGELOG.md

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

disarm-0.15.0.tar.gz (806.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

disarm-0.15.0-cp310-abi3-win_amd64.whl (1.7 MB view details)

Uploaded CPython 3.10+Windows x86-64

disarm-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

disarm-0.15.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.7 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

disarm-0.15.0-cp310-abi3-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

disarm-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file disarm-0.15.0.tar.gz.

File metadata

  • Download URL: disarm-0.15.0.tar.gz
  • Upload date:
  • Size: 806.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for disarm-0.15.0.tar.gz
Algorithm Hash digest
SHA256 3fe28ae0ec5c73e3f70446cc69c574e831248d6c56e1851b3d61a0a6e53c1705
MD5 9f80d3cc5603c538ce81b3de4030e952
BLAKE2b-256 03042bdaf842477a239e3016f8a4b2c50a7fb7e2e0edca6c6aeed350529ab926

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0.tar.gz:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file disarm-0.15.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: disarm-0.15.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for disarm-0.15.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 369be02175abc2a161e65dfb219a677902d75c4ceccda8c7f886356380a2d4f4
MD5 f89b2ff284e03c3aca0984bb7f4a9afd
BLAKE2b-256 4813f9e78e8efcf9716f9b13165bfe1b32a4fbfc86c49256a9aeba4337f60447

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file disarm-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for disarm-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 83437f6520c1bbb2f3b19dd79aaa986558ac2c0ab7763de52f7bc9e71b2bc42e
MD5 6b846fb81353e05383ab1723e3ed11aa
BLAKE2b-256 2bf73ef3e3497f7b3e9d9229667bacbde44a3593f2eb61436731673489197bb6

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file disarm-0.15.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for disarm-0.15.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 099ee3e3c51d023414d0edb8e4a5b03e6eea1e4506878fc8a3cec4cfb65b96d6
MD5 59116d85e7b2663f1a71102656a04804
BLAKE2b-256 5dcad1178d926b1a05bd7e76fb379becdac86f1d93fa136ab0785f8de0ac3af9

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file disarm-0.15.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for disarm-0.15.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6ac827839b63c873f6504f9f762a433648c83e0845b370127bca733dbd482e9a
MD5 d4f01e0f8f7f31434e8e5d6db23c31f4
BLAKE2b-256 bf51178268e887c9d116d659315610763ff6870857a5038511ad7a79e2697e4b

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file disarm-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for disarm-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 9b8ae92792bb9f447a2f7fadbfc5e6bcc2d777a9ca8fcefb191cb7f3ca8d5855
MD5 c93b2f44734ec547cbe98a3c7614d97d
BLAKE2b-256 bcbe61c41a2432106238e70d236520d8b83b6ffb09c3501bffb60770959d0963

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.15.0-cp310-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on raeq/disarm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.16.0

6 files

This release

0.15.0 This release

6 files

0.14.1

6 files

0.14.0

6 files

0.13.0

6 files

0.12.0

6 files

0.11.1

6 files

0.11.0

6 files

0.10.0

6 files

0.9.1

6 files

0.9.0

6 files

0.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page