Your dependency scanner and your source code scanner are reading different files. That sounds obvious until a SQL injection bug ships in a codebase that passed its Snyk scan clean.
Snyk and BrassCoders are both security tools for Python projects. They almost never flag the same thing. One reads your requirements.txt; the other reads your .py files. Both attack surfaces are real. A team that runs only one is leaving half the problem unchecked.
What Snyk Scans
Snyk is the market leader in software composition analysis (SCA) — the discipline of scanning a project's dependency tree against known vulnerability databases to find CVEs before they reach production. It's used by tens of thousands of teams and has one of the largest vulnerability databases in the industry, maintained at snyk.io.
When you run snyk test, Snyk reads your package manifest — requirements.txt, Pipfile, pyproject.toml, or whatever lock file your project uses. It resolves the full dependency graph, including transitive dependencies you didn't explicitly choose. Then it checks every package version against its vulnerability database.
A classic Snyk find looks like this:
✗ High severity vulnerability found in requests@2.25.1
Description: SSRF via Proxy-Authorization header leak
Info: https://security.snyk.io/vuln/SNYK-PYTHON-REQUESTS-3333842
Introduced through: requests@2.25.1
Fix: Upgrade to requests@2.31.0
The CVE is real (CVE-2023-32681). The fix is concrete. Snyk didn't need to read a single line of your application code to find it.
That's also the boundary of what Snyk does. It doesn't read your source code. It doesn't know what your application does with the packages it imports. It won't flag a bug you wrote — only a bug someone else wrote in a package you imported.
What BrassCoders Scans
BrassCoders caught 11 of 12 planted AI-generated Python security bugs in a reproducible June 2026 benchmark (v2.0.8, methodology at coppersun.dev/blog/ai-coder-bug-benchmark/). Bandit caught 6 of 12 in the same test. Pylint caught 1 of 12.
BrassCoders reads your source files — the Python code you and your AI assistant wrote. It runs 12 scanners across that code: Bandit, Pylint, Pyre/Pysa, Semgrep, ast-grep, detect-secrets, and six custom detectors covering AI-pattern hallucination, performance, secrets, privacy, content moderation, and JavaScript/TypeScript. It doesn't read your dependency tree. It won't flag a CVE in a package.
A classic BrassCoders find looks like this:
# Your code
cursor.execute(f"SELECT * FROM users WHERE id = {uid}")
BrassCoders flags this as B608 (SQL injection via string interpolation) regardless of what database package you're using. The vulnerability is in the code you wrote. No CVE database entry needed — the pattern is wrong by construction.
Or this:
import subprocess
subprocess.run(f"convert {user_input}", shell=True)
Bandit flags B602 (subprocess with shell=True) and B603 (subprocess with user input). Both findings come from reading your source, not from checking the subprocess package version.
The phantom-API detector adds a third category with no Snyk equivalent. When an AI coding assistant generates an import for a library that doesn't exist on PyPI — import pandas_ml or from fastapi.middleware.csrf import CSRFMiddleware — BrassCoders flags it at scan time. That's a bug the AI introduced in your code, not a CVE in a dependency.
The Gap Between Them
There's a class of bug Snyk can't catch: bugs you wrote. subprocess.run(shell=True, args=user_input) has no CVE. It's your code, your bug. Snyk doesn't see it.
There's a class of bug BrassCoders can't catch: CVEs in third-party packages. If Pillow ships a memory corruption bug or cryptography has a padding oracle vulnerability, BrassCoders won't flag it. Snyk will. The package version and CVE match are what Snyk exists to find.
The two tools cover adjacent, non-overlapping attack surfaces. This is a feature, not a gap to paper over. Running both fills the picture:
Attack surface 1: Code YOU wrote → BrassCoders scans it
Attack surface 2: Code YOU imported → Snyk scans it
Neither tool pretends to cover both. That honesty is useful.
The AI-Coder Wrinkle
AI coding assistants make the BrassCoders layer more important. They write source code bugs at volume — SQL injection, shell injection, performance anti-patterns — at a rate that human-written code doesn't produce at scale. The June 2026 benchmark put 12 AI-generated Python files through BrassCoders, Bandit, Semgrep, and Pylint. BrassCoders caught 11 of 12. The single miss was an unguarded-division logic bug that no deterministic rule system reliably catches.
AI assistants also introduce a third category that sits at the boundary between source code and dependencies: hallucinated package imports. When Claude Code generates from pydantic_ai.validators import strict_mode, and no such module exists, you've got a phantom import. BrassCoders's AI-pattern scanner catches that in source before pip install runs. Snyk would report the package as unresolvable when it hits the manifest. Both signals matter — BrassCoders catches it in the source file first.
One workflow implication: if you run BrassCoders as a pre-commit hook or early CI step, you can catch hallucinated imports before they ever land in requirements.txt. That's a faster catch than waiting for the Snyk scan to flag an unresolvable package.
Running Both in CI
BrassCoders and Snyk take different inputs and write separate outputs. Adding both to CI is a two-job addition, not an architectural decision.
# GitHub Actions — add as two separate jobs or steps
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Snyk dependency scan
uses: snyk/actions/python@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
source-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install BrassCoders
run: pip install brasscoders
- name: BrassCoders source scan
run: brasscoders scan .
Snyk reads requirements.txt or your lock file and checks the dependency tree. BrassCoders reads your .py files and checks your source patterns. They fail on different inputs and report to different places. Neither job knows the other exists, and that's the right shape for tools with clean boundaries.
For teams already running Snyk, adding BrassCoders is one CI step and pip install brasscoders. For teams starting fresh, both tools install in minutes. The OSS core is free; BrassCoders Paid is $12/month per developer and adds AI-powered enrichment for finding deduplication and tighter triage.
Install the OSS core and run your first source scan in under a minute: coppersun.dev/install/.
Top comments (0)