Running a security audit on a Python codebase is no longer optional—modern applications handle sensitive data, interact with external services, and often sit behind public APIs. Bandit, the open‑source static analysis tool from the OpenStack Security Project, gives developers a lightweight yet powerful way to surface insecure patterns before they ship. In this guide we’ll walk through everything you need to know to run a thorough Bandit audit, customize its rules, integrate it into CI/CD, and interpret the results with confidence.
What You’ll Need
- Python 3.8 or newer installed on your workstation or build server
- Access to the codebase (local clone or remote repository)
- Virtual‑environment tool (venv, virtualenv, or conda)
- Basic familiarity with the terminal and Git
- Optional: CI platform (GitHub Actions, GitLab CI, Jenkins, etc.)
Step 1: Install Bandit in an Isolated Environment
Never install security tools globally on a production machine. Create a fresh virtual environment and install Bandit via pip. This isolates dependencies and ensures repeatable results.
python3 -m venv .bandit-env
source .bandit-env/bin/activate
pip install --upgrade pip
pip install bandit After activation, verify the installation:
bandit --version You should see something like bandit 1.7.5. If you get a command not found error, double‑check that the virtual environment is active.
Step 2: Run a Baseline Scan
Before you start tweaking rules, generate a baseline report for the entire repository. This gives you a reference point and helps you spot high‑severity findings immediately.
bandit -r . -f json -o bandit-baseline.json The -r flag tells Bandit to recurse, -f json selects JSON output (easy to parse later), and -o writes the report to a file. Open the JSON or use the built‑in text format to get a quick glance:
bandit -r . -f txt -o bandit-baseline.txt Take note of any HIGH or CRITICAL issues—these are the ones you’ll want to address first.
Step 3: Tailor the Scan with a Configuration File
Bandit ships with a default set of tests, but you can enable, disable, or fine‑tune them via a YAML config. Create .bandit.yaml at the repository root:
# .bandit.yaml
skips:
- B101 # assert used for debugging
- B404 # import subprocess (often false positive)
include:
- B101
- B102
- B108
severity:
- HIGH
- CRITICAL
confidence:
- HIGH In this example we skip the noisy B101 and B404 tests, but we explicitly include a handful of high‑impact checks. Adjust severity and confidence thresholds to focus on the most actionable findings.
Run Bandit again, pointing at the config:
bandit -r . -c .bandit.yaml -f html -o bandit-report.html The HTML report is handy for sharing with non‑technical stakeholders.
Step 4: Integrate Bandit into Your CI Pipeline
Static analysis loses its power if it’s run only locally. Hook Bandit into your CI so every pull request is automatically vetted.
GitHub Actions example:
name: Security Scan
on: [push, pull_request]
jobs:
bandit-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install Bandit
run: |
python -m venv venv
source venv/bin/activate
pip install bandit
- name: Run Bandit
run: |
source venv/bin/activate
bandit -r . -c .bandit.yaml -f json -o bandit-ci.json
- name: Upload report
uses: actions/upload-artifact@v3
with:
name: bandit-report
path: bandit-ci.json Adjust the python-version and path to your config as needed. Most CI platforms support similar steps; the key is to fail the build on any CRITICAL finding.
Step 5: Automate Remediation with Pre‑Commit Hooks
Even tighter feedback loops can be achieved by running Bandit as a pre‑commit hook. Add the following to .pre-commit-config.yaml:
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
hooks:
- id: bandit
args: [-c, .bandit.yaml, -r, .] Then install the hook:
pre-commit install Now any git commit that introduces a new Bandit warning will be blocked, forcing developers to fix the issue immediately.
Step 6: Analyze and Prioritize Findings
Bandit categorizes each issue by severity (LOW, MEDIUM, HIGH, CRITICAL) and confidence (LOW, MEDIUM, HIGH). Use both dimensions to prioritize:
- CRITICAL + HIGH confidence: Must be fixed before merge.
- HIGH + MEDIUM confidence: Schedule for the next sprint.
- MEDIUM + LOW confidence: Review manually; could be a false positive.
For each finding, Bandit provides a code (e.g., B108) and a short description. Cross‑reference the code with the official plugin list to understand the underlying risk.
Document the remediation steps directly in the issue tracker. A typical ticket might read:
Title: B108 – Use of subprocess without shell=False
Description: The function `run_cmd` calls `subprocess.Popen` with `shell=True`, exposing command injection risk.
Remediation: Replace with `subprocess.run(..., shell=False)` and validate inputs.
Severity: HIGH
Confidence: HIGH Common Mistakes to Avoid
Even seasoned developers stumble over a few recurring pitfalls when using Bandit:
- Running Bandit on compiled bytecode (.pyc): Bandit only analyzes source files; .pyc files will be silently skipped.
- Ignoring the confidence score: A HIGH severity with LOW confidence is often a false positive. Verify before raising tickets.
- Disabling too many tests: Over‑skipping can blind you to real issues. Use
skipssparingly and keep the default rule set as a baseline. - Hard‑coding credentials in test data: Bandit flags hard‑coded passwords. Store secrets in environment variables or secret managers instead.
- Forgetting to update Bandit: New plugins are added regularly. Schedule a monthly
pip install -U banditto stay current.
Tips and Tricks
Here are a few shortcuts that can make your audit smoother:
- Use the
--excludeflag to ignore third‑party directories (e.g.,--exclude tests,venv). - Generate SARIF output (
-f sarif) for direct import into Azure DevOps or GitHub Code Scanning. - Combine Bandit with other linters (flake8, pylint) to get a holistic view of code quality and security.
- Leverage the
--verboseflag during CI runs to surface which file triggered each warning. - Run Bandit on a Docker image by mounting the source code and executing inside the container, ensuring the same environment as production.
Frequently Asked Questions
Can Bandit detect insecure dependencies?
No. Bandit focuses on source‑code patterns. For dependency analysis use tools like safety or pip-audit alongside Bandit.
Is Bandit suitable for Django or Flask projects?
Absolutely. Bandit works on any pure‑Python code. However, framework‑specific checks (e.g., template injection) are not covered; consider adding custom plugins if needed.
How do I suppress a single false positive without disabling the whole test?
Use an inline comment with the Bandit code. Example:
# nosec B108: subprocess used safely with a constant argument This tells Bandit to ignore that line while keeping the rule active elsewhere.
Conclusion
Bandit gives you a fast, reproducible way to embed security into the Python development lifecycle. By installing it in an isolated environment, customizing the rule set, automating scans in CI, and treating the output as actionable tickets, you turn static analysis from a “nice‑to‑have” into a defensive cornerstone. Remember to keep the tool up‑to‑date, avoid over‑skipping rules, and combine Bandit with dependency‑checking utilities for a truly comprehensive audit. With these practices in place, your Python codebase will be far less likely to ship exploitable vulnerabilities.
Photo by Zulfugar Karimov on Unsplash





