taintrace
Typosquat detector for AI coding agent dependencies.
taintrace scans your lockfiles (Cargo.lock, package-lock.json, requirements.txt, go.sum) for package names that suspiciously resemble known legitimate packages — the exact vector used in the arrayref@0.3.10 attack (August 2026), where proc-macro1 imitated proc-macro2 to execute arbitrary code during cargo build.
Table of contents
- The Problem
- Install
- Usage
- Example typosquat reports
- Algorithms
- Python API reference (source)
- Multi-ecosystem
- CI/CD integration
The Problem
AI coding agents install dependencies automatically. Typosquats pass undetected by scanners like cargo audit or npm audit because they have no known CVE — they're brand new packages with malicious build.rs or proc-macros.
Traditional scanners check known-bad. taintrace checks suspicious-similar.
Install
pip install taintrace
Usage
Scan lockfiles
taintrace check Cargo.lock
Multiple lockfiles at once:
taintrace check Cargo.lock package-lock.json requirements.txt
Scan PHP/Composer lockfiles:
taintrace check composer.lock
Scan a lockfile
taintrace check Cargo.lock
╭──────────────────────────────────────────────╮
│ taintrace v0.2.1 — scanning Cargo.lock │
│ Total deps: 42 | Suspects: 1 │
╰──────────────────────────────────────────────╯
🚨 Typosquat Suspects
┏━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Package ┃ Risk ┃ Score ┃ Similar To ┃ Reason ┃
┣━━━━━━━━━━━━━╋━━━━━━━━━━╋━━━━━━━╋━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃ proc-macro1 ┃ CRITICAL ┃ 0.980 ┃ proc-macro2 ┃ Near-identical to... ┃
┗━━━━━━━━━━━━━┻━━━━━━━━━━┻━━━━━━━┻━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━┛
❌ 1 suspect(s) found — review required
Scan directory tree
taintrace scan-directory . # recursive scan of all lockfiles
taintrace scan-directory /path/to/repo # scan specific path
taintrace scan-directory . --format sarif # SARIF output for CI/CD
Auto-discovers: Cargo.lock, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb, requirements.txt, Pipfile.lock, poetry.lock, uv.lock, go.sum, Gemfile.lock, composer.json, composer.lock, Package.resolved, Package.swift, mix.lock, build.gradle, build.gradle.kts, gradle/libs.versions.toml.
JSON output (CI/CD)
taintrace check Cargo.lock --format json
SARIF output (GitHub Code Scanning)
taintrace check Cargo.lock --format sarif > results.sarif
Score a single package
taintrace score proc-macro1
Exit codes
0— no suspects found1— one or more suspects detected (use in CI/CD gates)
Configuration
taintrace automatically discovers and loads configuration files from the current working directory, parent directories, or your user home directory:
.taintrace.toml/taintrace.toml.taintrace.yaml/.taintrace.yml/taintrace.yaml/taintrace.ymlpyproject.toml(under[tool.taintrace]).taintracerc~/.config/taintrace/config.toml(or.yaml)
You can also explicitly pass a configuration file path using the -c / --config flag:
taintrace check Cargo.lock --config /path/to/my-config.yaml
Example .taintrace.toml
[taintrace]
threshold = 0.85
format = "text"
no_informational = true
ignore = [
"my-internal-mirror",
"legit-package-with-similar-name"
]
Example .taintrace.yaml
taintrace:
threshold: 0.85
output_format: sarif
no_informational: false
ignore:
- my-internal-mirror
- ${CUSTOM_IGNORE_PKG}
Example pyproject.toml
[tool.taintrace]
threshold = 0.80
ignore = ["my-corp-pkg"]
Environment Variable Interpolation
Configuration values support environment variable interpolation using $VAR or ${VAR} syntax (e.g. ${CI_PROJECT_NAME} or $SCAN_THRESHOLD).
Ignoring False Positives
To suppress specific known-safe packages via the CLI directly:
taintrace check Cargo.lock --ignore proc-macro1 --ignore some-legit-package
Ignored packages are excluded from CLI, JSON, and SARIF output. CLI flags override options defined in configuration files.
Algorithms
- Levenshtein distance — edit distance between names
- Soundex phonetic — catches homophones ("night" vs "nite")
- Substring matching — detects containment ("lodash" vs "lodash1")
- Combined scoring — weighted combination of all signals
Use --threshold with check or scan-directory to set the minimum similarity considered during matching. The default is 0.7; higher values require closer package-name matches. Reports include each matched package with its similarity score.
Multi-ecosystem
| Ecosystem | Lockfiles | Status |
|---|---|---|
| Rust | Cargo.lock, Cargo.toml | ✅ |
| Node.js | package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock, bun.lockb | ✅ |
| Python | requirements.txt, poetry.lock, pyproject.toml (PEP 621 + Poetry), uv.lock, Pipfile.lock | ✅ |
| Go | go.sum | ✅ |
| Ruby | Gemfile.lock | ✅ |
| PHP | composer.json, composer.lock | ✅ |
| Swift | Package.resolved, Package.swift | ✅ |
| Elixir | mix.lock | ✅ |
| Gradle | build.gradle, build.gradle.kts, gradle/libs.versions.toml | ✅ |
Known-package data
Similarity suggestions come from a built-in, offline package database. Each ecosystem is backed by real registry data, not hand-written guesses:
| Ecosystem | Source |
|---|---|
| Rust, Node.js, Python, Go, Ruby, PHP, Swift, Elixir | curated lists in taintrace/db.py |
| Java / Maven | src/taintrace/data/java_packages.txt — 7,848 groupId:artifactId coordinates generated from the Maven Central repository index, with provenance recorded in the file header |
Refresh the Java data against Maven Central with:
python scripts/generate_java_packages.py # regenerate
python scripts/generate_java_packages.py --check # validate the committed file (offline)
python scripts/generate_java_packages.py --verify # re-query Maven Central and diff
Java coordinates are matched in their groupId:artifactId form, the same shape the
Gradle parsers emit; a bare artifactId is also accepted. If you request an ecosystem
that has no data at all, taintrace says so explicitly instead of reporting
"no similar packages found".
CI/CD integration
GitHub Action
- uses: yunaremaia/taintrace@main
with:
lockfile: Cargo.lock
format: sarif
sarif-output: taintrace.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: taintrace.sarif
Auto-detect lockfiles in your repo root:
- uses: yunaremaia/taintrace@main
with:
format: cli
Pre-commit hook
repos:
- repo: https://github.com/yunaremaia/taintrace
rev: v0.2.1
hooks:
- id: taintrace
Why taintrace?
- AI-agent-aware — built for the vector AI agents expose (automatic dep installation)
- Zero config — just point at your lockfile
- Offline-first — no API calls, no data leaves your machine
- SARIF-native — integrates with GitHub Code Scanning
- Open source — MIT licensed, no paywall
How it differs
| Tool | CVE-based | Typosquat | AI-aware | Open source |
|---|---|---|---|---|
| cargo-audit | ✅ | ❌ | ❌ | ✅ |
| npm audit | ✅ | ❌ | ❌ | ✅ |
| Socket | ✅ | Partial | ❌ | ❌ |
| Phylum | ✅ | Partial | ❌ | ❌ |
| taintrace | ❌ | ✅ | ✅ | ✅ |
If this tool is useful to you, a star helps other people find it.
Related tools
- driftcheck — detect version drift between docs and toolchain files
- depscan — scan dependencies across multiple ecosystems
- agentcost — track and attribute LLM spend per agent
- vibeguard — scan AI-generated code for hardcoded secrets, SQL injection and dangerous eval/exec
Part of a family of focused, single-purpose developer tools — each one does one thing and does it well.
License
MIT — see LICENSE
Metadata
Release files for taintrace 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| taintrace-0.2.4.tar.gz | 129.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| taintrace-0.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 205.1 kB
Release files / taintrace-0.2.4.tar.gz
| Download URL | taintrace-0.2.4.tar.gz |
|---|---|
| Size | 129.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
08fe994e754dad606f31a1cf875340e5b14526ae1d0db532033877ef2e09137f
|
|
BLAKE2b-256 checksum How to use checksums |
f615c9c94c28774cc4199061574d044aa6bbaa93c923aa062bef9fb72f9dfe7a
|
| 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 Oct 5, 2026.
Transparency logRelease files / taintrace-0.2.4-py3-none-any.whl
| Download URL | taintrace-0.2.4-py3-none-any.whl |
|---|---|
| Size | 75.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
37c95a8019958862fd91aac6b07e05ec378613aebc53b88c4e69002348fb5157
|
|
BLAKE2b-256 checksum How to use checksums |
0acdc3eb123acc0a2af5411a5a0e56b183ff13611ad218c9e06f61d14d33025f
|
| 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 Oct 5, 2026.
Transparency log