The Python security tools worth running in almost every project are pip-audit for vulnerable dependencies, Bandit for insecure code patterns, and detect-secrets for leaked credentials — each covering a distinct risk class and each easy to wire into CI. You do not need a dozen tools; you need one from each category, gated properly, and the discipline to act on what they find.
This is a practitioner's shortlist, with the commands and the reasoning behind each pick.
pip-audit: known vulnerabilities in dependencies
Most of your security risk lives in packages you installed, many of them pulled in transitively. pip-audit, maintained under the Python Packaging Authority, checks your installed packages and requirements against the Python advisory database (PyPI's own vulnerability data plus OSV).
pip install pip-audit
pip-audit
To scan a requirements file specifically, or fail CI on findings:
pip-audit -r requirements.txt
pip-audit resolves the dependency tree, so it catches vulnerable transitive packages you never named directly. The workflow after a finding is standard: note the fixed version from the advisory, bump the dependency (directly or by constraining the transitive one), and re-run. For larger projects an SCA tool adds cross-ecosystem coverage and continuous monitoring for CVEs disclosed after you shipped, which pip-audit alone does not do.
A related habit: pin your dependencies. A requirements.txt with exact == versions, or a lockfile from Poetry/pip-tools, is what makes an audit meaningful — you cannot secure a dependency set that changes on every install.
Bandit: static analysis for insecure code patterns
pip-audit finds bugs you inherited; Bandit finds bugs you wrote. It is a SAST tool for Python that scans your source for common insecure patterns — use of assert for security checks, subprocess with shell=True, hardcoded passwords, weak hash functions, unsafe YAML loading, and eval on untrusted input.
pip install bandit
bandit -r your_package/
Bandit is opinionated and will flag things that are fine in context, so tune it. Suppress a specific false positive inline with a comment rather than lowering the whole ruleset:
subprocess.run(cmd, shell=True) # nosec B602 - cmd is a fixed constant
Configure severity and confidence thresholds so the gate blocks on what matters:
bandit -r your_package/ -ll -ii
That runs at low-severity-and-above, medium-confidence-and-above. Start stricter in report-only mode, triage the backlog, then flip the gate on.
detect-secrets: keep credentials out of the repo
Hardcoded secrets are one of the most common and most damaging findings, because a leaked key in Git history stays exploitable until it is rotated. detect-secrets (from Yelp) scans for credential patterns and, importantly, maintains a baseline so you can adopt it on an existing repo without drowning in historical noise.
pip install detect-secrets
detect-secrets scan > .secrets.baseline
Then run it as a pre-commit hook so new secrets are caught at commit time, and audit the baseline periodically. Pair it with a policy that any exposed credential gets rotated, not just removed — deleting the line does not un-leak the key.
Safety and the dependency-scanning overlap
safety is another dependency scanner you will see referenced. It overlaps heavily with pip-audit; the practical difference is that pip-audit is PyPA-maintained and uses open advisory sources, while safety has a commercial database tier. Running one dependency scanner is enough — pick pip-audit for the open baseline unless you already have a reason to use safety.
Wiring them into CI
The value is in automation. A single job can run all three:
python-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install tools
run: pip install pip-audit bandit detect-secrets
- name: Dependency audit
run: pip-audit -r requirements.txt
- name: Static analysis
run: bandit -r src/ -ll -ii
- name: Secret scan
run: detect-secrets scan --baseline .secrets.baseline
Order matters less here than gating: decide which findings fail the build (start with High-severity dependency CVEs and Bandit high-severity/high-confidence) and which merely report, so the gate stays trusted instead of bypassed.
What this stack does and does not cover
These three tools cover dependencies, code patterns, and secrets — the bulk of everyday Python risk. They do not cover runtime behavior. For that you add dynamic testing (DAST) against a running instance, which catches auth flaws and misconfiguration that static tools cannot see. And none of them replace a human review for logic flaws.
Start with pip-audit and detect-secrets because they are the highest value for the least tuning, then add Bandit once you have a triage habit. The Academy covers threshold tuning for each.
FAQ
What is the best Python dependency vulnerability scanner?
pip-audit, maintained by the Python Packaging Authority, is the strong open-source default. It checks installed packages and requirements against open advisory data (PyPI advisories and OSV) and resolves transitive dependencies. safety is a comparable alternative with a commercial database tier.
What does Bandit check for?
Bandit is a SAST tool that scans Python source for insecure patterns: shell=True in subprocess calls, hardcoded passwords, weak hashes, unsafe YAML loading, eval on untrusted input, and similar. Tune its severity and confidence thresholds and suppress false positives inline with # nosec.
How do I keep secrets out of my Python repo?
Use detect-secrets with a baseline to adopt it cleanly on an existing repo, run it as a pre-commit hook to catch new secrets, and rotate any credential that was ever committed — deleting the line does not undo the exposure.
Do I need both pip-audit and safety?
No. They overlap as dependency scanners. Run one; pip-audit is the open, PyPA-maintained baseline. Add a broader SCA tool if you need continuous monitoring and multi-ecosystem coverage.