AI coding assistants write requirements.txt entries from training data — not from the live PyPI index. The version they pin is the one most common in their training corpus, which was typically current 12 to 24 months before the model's training cutoff. By the time your team installs those dependencies, some may carry documented vulnerabilities. The package name is correct. The install succeeds. Nothing flags it until an audit runs.
This is a distinct problem from slopsquatting, where an AI hallucinates a package name that doesn't exist on PyPI — or that exists as a typosquat registered by a threat actor. Slopsquatting is visible the moment installation fails. The vulnerable-version problem stays invisible by default.
How AI Picks Package Versions
AI coding assistants generate requirements.txt entries from training data, not from the live PyPI index: they write the version they most commonly saw during training, which is often a version that was popular 12–24 months before the model's training cutoff and may have known vulnerabilities documented after that date. BrassCoders's AI-pattern scanner catches phantom imports — packages that don't exist on PyPI — but real packages at vulnerable versions install cleanly and pass every import check.
Models are trained on code snapshots with a fixed cutoff date. When you ask one to scaffold a Flask app, it writes Flask==2.3.2 because that version appeared in thousands of GitHub repos in its training window, not because it queried PyPI this morning. The same pattern applies to requests, cryptography, and Pillow — any dependency with an active CVE history becomes a candidate.
The gap matters most for packages that ship frequent security patches. cryptography discloses CVEs several times per year. Pillow has accumulated over 20 CVEs across its release history. If an AI recommends cryptography==41.0.3 and the current release is 43.x with several security fixes in between, your environment runs known-vulnerable code from day one.
The install doesn't fail. The import works. The tests pass. That's what makes this category of risk so easy to miss.
Real Packages, Vulnerable Versions: The Distinct Threat
BrassCoders's AI-pattern scanner catches phantom imports — packages that don't exist on PyPI — but a real package at a vulnerable version passes every import check and installs cleanly. The vulnerability is invisible until an explicit dependency audit runs.
Slopsquatting gets most of the attention because it's dramatic: the AI invents a package name, a threat actor registers that name on PyPI with malicious code, and the next developer who follows the AI's instructions runs the attack payload. The supply-chain angle makes for compelling security coverage.
The vulnerable-version pattern is quieter but affects a much broader class of packages. Every package with a CVE history is a candidate. The package exists on PyPI with a clean reputation, the version the AI pinned was safe when the AI was trained, and nothing in the standard development workflow surfaces the gap.
The PyPI Advisory Database, maintained by the Python Packaging Authority, tracks known vulnerabilities against specific package versions. pip-audit queries it in seconds. Your AI assistant has never heard of the CVEs published after its training cutoff.
Automating the Check AI Gets Wrong
pip-audit, the PyPA's official dependency auditing tool, cross-references your requirements.txt against the PyPI Advisory Database and GitHub Advisory Database in seconds — it catches what AI assistants miss by running against the live vulnerability index rather than training data. BrassCoders and pip-audit cover different layers and both belong in CI.
pip-audit is straightforward to install and run:
pip install pip-audit
pip-audit
Run it after pip install -r requirements.txt so it scans what's actually installed. No authentication needed. No API key. pip-audit exits with code 1 on any finding, which makes failing the CI build trivial to configure. Failures surface the CVE ID, the affected version, the fixed version, and a severity score — everything you need to triage the finding in under a minute.
One thing pip-audit doesn't cover: the security bugs in the code your team writes. Hardcoded credentials, unsafe subprocess calls, SQL injection patterns — those are code-layer risks that live inside your own modules. That's where BrassCoders takes over.
Where BrassCoders Fits in the Dependency Audit Stack
BrassCoders runs in CI and catches code-layer security bugs in the packages you write — SQL injection, hardcoded credentials, unsafe subprocess calls. pip-audit runs against your dependency manifest and catches known vulnerabilities in packages you import. Both belong in the same CI pipeline.
BrassCoders runs 12 static-analysis scanners against your source code: Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, and six custom detectors. The AI-coder bug benchmark shows BrassCoders catching 11 of 12 AI-generated bugs in a reproducible corpus, compared to Bandit's 6 of 12. That coverage is across your code, not your dependency manifest.
The six custom detectors add coverage for secret patterns, PII/privacy risks, AI-generated phantom imports, performance anti-patterns, content-moderation risks, and JavaScript/TypeScript. They run on the same local machine as the upstream scanners, with no outbound network calls in the OSS core.
The OSS core is free (Apache 2.0) and installs with:
pip install brasscoders
brasscoders scan .
BrassCoders Paid, at $12/dev/month, adds an AI-powered enrichment pass that deduplicates findings and ranks them by project-specific relevance. It sends only already-redacted findings and a project signature to the gateway — never raw source code.
Pair both tools in the same CI job. pip-audit catches the outdated, vulnerable dependency the AI pinned. BrassCoders catches the SQL injection the AI wrote into the route handler. Neither replaces the other.
pip install brasscoders. Add pip-audit right after pip install -r requirements.txt. Between the two, you cover code-layer bugs and dependency-level CVEs.
Top comments (0)