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.16.0.tar.gz (886.5 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.16.0-cp310-abi3-win_amd64.whl (1.7 MB view details)

Uploaded CPython 3.10+Windows x86-64

disarm-0.16.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.16.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

disarm-0.16.0-cp310-abi3-macosx_11_0_arm64.whl (1.7 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

disarm-0.16.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.16.0.tar.gz.

File metadata

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

File hashes

Hashes for disarm-0.16.0.tar.gz
Algorithm Hash digest
SHA256 cb3313da5d14c481c650842a3af019043e84f3dcb89cfc01e3ad70415e51f146
MD5 f4c6b646c9492f4a350257163876c9ea
BLAKE2b-256 12185077e4b61db10d7c9c51332b5b13a69e428863982fc0c6bb678f9701419e

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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.16.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: disarm-0.16.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.16.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 b5c475646c768125d6958c9aeef4066c9b11165d204fd5e0f843926278f9dd3d
MD5 a98b9a455e4a97b3be1a5932fafa55d7
BLAKE2b-256 fe79bd97f0d54aa42dbbeb3f8b7b89954ae2ed5c8aed1997facb8bfcd0935288

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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.16.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for disarm-0.16.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 df11df12f1329216864dbd9b96ea45b30358d5ebadc6d24f7d43b5d4c62edc05
MD5 fac07220082351fb90f98ad52ccd812e
BLAKE2b-256 4ebc38e45d761ce225345a5a822fe130f94a3bbc857d9b0cce875e853a142b77

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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.16.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for disarm-0.16.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 45ac368a379eb7c76b56200e37a1d1333cfbef01921fc46066f6ba77c967b341
MD5 de0dbac6b048611f7dd62ff8eff92431
BLAKE2b-256 43fd0cd44cafc92eedc7c6b7f35a5fef2ef2eb50219e54e5cab56d040077194a

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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.16.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for disarm-0.16.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 6a44764552a030128beaa03cc53632e669f0eb94d4eb328dcc0b31bd1f5b6d30
MD5 21c393544c756811fac3ba907bd349c3
BLAKE2b-256 8a236facf9039d54cfb3dee0f307607911304b0439087d3a4688fe0e13529ecd

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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.16.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for disarm-0.16.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 112e537dc924c75032ce7d83fcd3c1c138c6ba15d0bc5eaa62b5e1065b2873c9
MD5 413d99d2bea83a05e5b577ebc70d3ce9
BLAKE2b-256 5c4673dbc1a4eab9c72567131c40991ad464efa08d150e64c6b9a5c83ca9b127

See more details on using hashes here.

Provenance

The following attestation bundles were made for disarm-0.16.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

This release

0.16.0 This release

6 files

0.15.0

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