Security Policy¶
disarm provides building blocks for adversarial-text defense — it is a defense-in-depth layer, not a complete control. Before relying on it in a security context, and before reporting a "bypass," please read the Threat Model: it defines precisely what these mechanisms do, what is out of scope by design, and how we distinguish a vulnerability from a known limitation.
Supported versions¶
Security fixes are released against the latest 0.6.x release on PyPI. Older minor
series are not maintained.
| Version | Supported |
|---|---|
| 0.6.x | Yes |
| < 0.6 | No |
Reporting a vulnerability¶
Please do not open a public GitHub issue for security vulnerabilities.
Report privately via GitHub Security Advisories. We aim to acknowledge within 5 business days and to publish a fix within 30 days for confirmed issues. disarm is maintained by a small team — if a deadline is at risk we will say so on the advisory thread rather than go silent.
Please include: - A description of the issue - A minimal reproduction (input → actual output → expected output) - Which documented behavior or invariant you believe is violated
What makes a report actionable¶
disarm is maintained by a small team, so a report we can reproduce in minutes is the difference between a same-week fix and a thread that goes nowhere. A strong report has a runnable reproduction (the exact input, the actual output, and the expected output) and points at the specific documented invariant or mechanism in the Threat Model that it violates.
AI tools are fine for finding and writing this up — but please run the reproduction
yourself before submitting and confirm it holds against the latest release. A
speculative "there may be a buffer overflow / use-after-free here" with no reproduction,
no triggering input, and an author who can't answer follow-up questions is not a report
we can act on, and we will close it without an extended back-and-forth. This library
forbids unsafe crate-wide and is exhaustively fuzzed and tested for no-panic,
linear-time behavior, so memory-safety claims in particular need a concrete trigger.
If you're not sure whether your finding is a vulnerability or an out-of-scope limitation, that's fine — say so, and lean toward reporting. We would rather triage a known limitation than miss a real invariant failure.
What we treat as a vulnerability¶
A case where disarm fails to do what the Threat Model says it does —
for example a documented invariant failing (normalize_confusables emitting a code point
the bundled table maps to the target; a documented bidi/zero-width code point not stripped;
an idempotence violation), a panic / memory-safety issue / super-linear blowup, or
is_suspicious_hostname failing to flag a label despite a mixed-script or bundled-table confusable condition it claims to detect.
A "bypass" that depends on an out-of-scope item (most commonly a confusable that is simply not in the bundled TR39 data, a whole-script spoof, or a multi-character confusable) is a known limitation, not a vulnerability — but such reports are still welcome as coverage/enhancement requests. Expanding the bundled data is how this layer improves.
Mechanisms (what is security-relevant)¶
disarm is a pure, in-process text-transformation library: no network access, no filesystem writes, no code execution, no runtime dependencies. Security-relevant mechanisms — described as mechanisms, not guarantees:
- TR39 confusable mapping —
normalize_confusables/is_confusablemap characters in the bundled Unicode TR39 table to a target script (coverage = that table). - Bidi / zalgo / zero-width stripping — remove the enumerated control, combining-mark, and invisible code points.
- Hostname / IDN analysis —
is_suspicious_hostnameflags mixed-script labels and bundled-table confusables (not whole-script spoofs); a not-suspicious result is not a safety guarantee. - Filename sanitization —
../ path-separator handling for safer filenames. - Encoding detection —
chardetng/encoding_rs(Mozilla); no arbitrary code paths.
Supply-chain assurance¶
What is attested for consumers of the published artifacts, and how to verify it:
- Dependency policy gate (CI). Every PR runs
cargo audit(RustSec advisories) andcargo deny check licenses bans sources(license allow-list, banned/wildcard crates, and registry/source restrictions — only crates.io is trusted). Both are part of the required Rust checks passed merge gate. The policy lives indeny.toml. - SBOM. Each GitHub Release carries a CycloneDX software bill of materials
(
*.cdx.json) of the full Rust dependency graph, so downstream users can mechanically audit the dependency tree of a given release. Generated bycargo cyclonedxin the release workflow. - Build provenance (PEP 740). Wheels and the sdist are published to PyPI via OIDC Trusted Publishing, and each distribution carries a signed PEP 740 attestation binding it to this repository, the publishing workflow, and the exact commit — no long-lived PyPI API token exists. Verify a downloaded distribution with:
pip install pypi-attestations
python -m pypi_attestations verify pypi --repository https://github.com/raeq/disarm disarm-<version>-*.whl
(PyPI also surfaces the attestation/provenance on each file's page.)