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.88+     (pure Rust — no Python, no pyo3)
npm install disarm      # Node.js 22+
gem install disarm      # Ruby 3.3+

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? 24–116× Unidecode on Latin-script text, ~13× on Cyrillic, Greek, Arabic, Persian and Hebrew, ~6–11× python-slugify, ~57 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.88+ disarm on crates.io guide · docs.rs
Ruby 3.3+, RubyGems 3.3.22+ disarm on RubyGems guide
Node.js 22+ disarm on npm guide
Java 21+ / 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.

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

Metadata

Release files for disarm 0.17.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for disarm 0.17.0
File Size Uploaded
disarm-0.17.0.tar.gz 1.0 MB Details

Built distributions (wheels)

Table of built distributions (wheels) for disarm 0.17.0
File
disarm-0.17.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
disarm-0.17.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
disarm-0.17.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
disarm-0.17.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
disarm-0.17.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 10.5 MB

Release files / disarm-0.17.0.tar.gz

Download URL disarm-0.17.0.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
77c323fbc56a4188076d4a2ca5adc8596a74988377cc174ac644cea804e4aaea
BLAKE2b-256 checksum
How to use checksums
7fbef2c4c63752129d83651f55a0fddcb110b93c198697919f6bfba545215085
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / disarm-0.17.0-cp310-abi3-win_amd64.whl

Download URL disarm-0.17.0-cp310-abi3-win_amd64.whl
Size 1.9 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
3c7e1095d91475ba922b0fe946fd618dd199fc8b0a00350f51b188efbcff21d0
BLAKE2b-256 checksum
How to use checksums
4183c1489f5a94a27ff19fefcc988c810df1ae5d0679f95b8777909d4cfec039
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / disarm-0.17.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL disarm-0.17.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 2.0 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
eab445922e7ebe2a1070b8dbbd4b3a6741c5c14135522a058d1ad9f592a6fab0
BLAKE2b-256 checksum
How to use checksums
5c1ada1965ba2781bc15e45dbfb0728a0ea7f62803886f8d0a4605d2e70d11fa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / disarm-0.17.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL disarm-0.17.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 1.9 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
310fa6c70e81f134409a0c70e4a987bb308bf2e38330859130324ff951ea23d4
BLAKE2b-256 checksum
How to use checksums
41fbe3cfd248298beebd83bbd1d631f2ad0702dc5c279dfe993277ff37fae685
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / disarm-0.17.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL disarm-0.17.0-cp310-abi3-macosx_11_0_arm64.whl
Size 1.8 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
82d97a33459fdf56925b8f6d019e9f30bd9d2883302ccc95019cdedf4cea1120
BLAKE2b-256 checksum
How to use checksums
70004de8381b4bfd4ca34f546e69be47a316adee15eba66258c80f03698749d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / disarm-0.17.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL disarm-0.17.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 1.9 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
66773e7caa2c426e92c984effbd4e160dd425c10e6fe3325b5e45bc7ccc0e0da
BLAKE2b-256 checksum
How to use checksums
0c6884ed177678f32c927d7fdae7bf91c89bb374d4c7c6b3d95b8b1223995148
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.17.2

6 release files

0.17.1

6 release files

This release

0.17.0 This release

6 release files

0.14.1

6 release files

0.14.0

6 release files

0.13.0

6 release files

0.12.0

6 release files

0.11.1

6 release files

0.11.0

6 release files

0.10.0

6 release files

0.9.1

6 release files

0.9.0

6 release files

0.0.0

2 release 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