Tainted CLI
The local developer loop. One surface, three ways to run analyze (static, fast, no
target or credentials needed):
- on demand —
tainted analyze <repo> - on save —
tainted watch <repo>(re-runs analyze on every file change) - on commit — the pre-commit hook (
tainted-gate), which blocks the commit on a high-severity possible hole
It also runs the interactive fix and can prove against a running target.
Install
pip install tainted-cli # the engine comes with it, pinned to this version
export GEMINI_API_KEY=... # optional: turns on the LLM "meaning" register
prove additionally needs the browser the engine's dynamic extra carries. A package cannot
request an extra of its own dependency, so that is a second command rather than a flag:
pip install "tainted[dynamic]" && playwright install chromium
From a checkout instead — the core is a sibling directory, not a release:
pip install -e . # the core engine (repo root)
pip install -e surfaces/cli # this surface
Use
tainted analyze ./my-app # ranked possible holes in the terminal
tainted analyze ./my-app --only bola,rls --json # narrow the checks + machine-readable output
tainted watch ./my-app # re-analyze on every save
tainted fix ./my-app --finding-id 1db5cbc5 --apply # write the fix to disk
tainted prove ./my-app \ # drive a running target
--url http://localhost:54321 --anon-key "$ANON" \
--login-a a@x.com:pw --login-b b@x.com:pw --seed invoices:<id>
tainted tutorial walks through all of this from inside the terminal: run it with no
argument to list the lessons, or tainted tutorial proving-it to read one.
tainted tutorial # first-scan, reading-a-report, on-save, on-commit, proving-it, fixing-it
tainted tutorial first-scan # one lesson, with the commands to run and what to expect back
prove is gated by an ownership check: a localhost target needs nothing; a remote
target needs --ownership-token (checked via a DNS TXT record or a
/.well-known/tainted-verify file).
Which hole is fix fixing?
analyze prints a # and an ID for every row, and fix takes either:
tainted fix ./my-app --finding-id 1db5cbc5350811b5 # the hole itself; survives a re-run
tainted fix ./my-app --index 0 # the row number from that one run
Prefer the id. # is a position in the report that run drew, and both the code and the model's
ranking move underneath it.
Pre-commit
The hook manifest is .pre-commit-hooks.yaml at the repository root — pre-commit reads it
from there and nowhere else — and it is a language: system hook, so it runs the tainted-gate
you installed above rather than building an environment of its own. (It cannot build one: a
language: python hook installs this repo's root package, which is the engine, and the engine
has no tainted-gate script and none of this surface's dependencies.)
So: install Tainted, then add this to the project's .pre-commit-config.yaml:
- repo: https://github.com/OWNER/tainted
rev: v0.1.1
hooks:
- id: tainted
Or skip the clone entirely and run the command already on your PATH:
- repo: local
hooks:
- id: tainted
name: Tainted security gate
entry: tainted-gate
language: system
pass_filenames: false
always_run: true
tainted analyze exits 0 or 1. prove exits 1 when a high-severity finding is
proven, and 2 when the ownership check fails — both usable directly in scripts.
Release files for tainted-cli 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tainted_cli-0.1.1.tar.gz | 24.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tainted_cli-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 44.6 kB
Release files / tainted_cli-0.1.1.tar.gz
| Download URL | tainted_cli-0.1.1.tar.gz |
|---|---|
| Size | 24.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6d7647520d387c7fa5e28b24e3fa5ce3edf97e60aa9faa1c3951d40992a6186
|
|
BLAKE2b-256 checksum How to use checksums |
a78fc7fb231301ee06a6d8d6e04f0da2b92e647e4d4477da149029281a1755d5
|
| 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 19, 2026.
Transparency logRelease files / tainted_cli-0.1.1-py3-none-any.whl
| Download URL | tainted_cli-0.1.1-py3-none-any.whl |
|---|---|
| Size | 19.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
888c5fa3c0d783c6b9e5967caea08461715b6467d2e79da92f7f419f63579287
|
|
BLAKE2b-256 checksum How to use checksums |
eecedbdaec0d8741279342522b36acb3cb3a7c259494d0c19b099b1b0ae29e61
|
| 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 19, 2026.
Transparency log