python-bandit
Python Bandit Security Scanning
Bandit is a static analysis tool that finds common security issues in Python code. It processes each file, builds an AST, and runs security-focused plugins against AST nodes. Results are categorized by severity (LOW, MEDIUM, HIGH) and confidence (LOW, MEDIUM, HIGH).
Installation
Install the base package or add extras for specific features:
# Base installation
pip install bandit
# With TOML config support (pyproject.toml)
pip install "bandit[toml]"
# With SARIF output (for GitHub Advanced Security)
pip install "bandit[sarif]"
# With baseline support
pip install "bandit[baseline]"
Use the same Python version as the project under scan. Bandit relies on Python's ast module, which can only parse code valid for that interpreter version.
Core Usage
Scan a full project tree:
bandit -r path/to/project/
Scan with severity filter (report only HIGH):
bandit -r . --severity-level high
# or shorthand: -lll (high), -ll (medium+), -l (low+)
bandit -r . -lll
Scan with confidence filter:
bandit -r . --confidence-level high
# shorthand: -iii (high), -ii (medium+), -i (low+)
Target specific test IDs only:
bandit -r . -t B105,B106,B107 # hardcoded password checks only
Skip specific test IDs:
bandit -r . -s B101 # skip assert_used (common in tests)
Use a named profile:
bandit examples/*.py -p ShellInjection
Scan from stdin:
cat myfile.py | bandit -
Show N lines of context per finding:
bandit -r . -n 3
Configuration
pyproject.toml (Recommended)
Centralize Bandit settings alongside other tooling:
[tool.bandit]
exclude_dirs = ["tests", "migrations", "venv"]
skips = ["B101"] # assert_used — acceptable in test suites
tests = [] # empty = run all (minus skips)
Run with explicit config pointer:
bandit -c pyproject.toml -r .
.bandit (INI — auto-discovered with -r)
[bandit]
exclude = tests,migrations
skips = B101,B601
tests = B201,B301
Bandit auto-discovers .bandit when invoked with -r. No -c flag needed.
YAML Config
exclude_dirs: ['tests', 'path/to/file']
tests: ['B201', 'B301']
skips: ['B101', 'B601']
# Override plugin-specific defaults
try_except_pass:
check_typed_exception: true
Run: bandit -c bandit.yaml -r .
Generate a Config Template
bandit-config-generator > bandit.yaml
# Then edit — remove sections you don't need, adjust defaults
Suppressing False Positives
Mark individual lines with # nosec to suppress all findings:
self.process = subprocess.Popen('/bin/echo', shell=True) # nosec
Suppress specific test IDs only (preferred — avoids hiding future issues):
self.process = subprocess.Popen('/bin/ls *', shell=True) # nosec B602, B607
Use the full test name as an alternative to the ID:
assert yaml.load("{}") == [] # nosec assert_used
Always add a comment explaining why the suppression is justified.
Output Formats
bandit -r . -f json -o report.json # JSON (required for baseline)
bandit -r . -f sarif -o report.sarif # SARIF (GitHub Advanced Security)
bandit -r . -f csv -o report.csv # CSV
bandit -r . -f xml -o report.xml # XML
bandit -r . -f html -o report.html # HTML
bandit -r . -f screen # Terminal (default)
bandit -r . -f yaml -o report.yaml # YAML
Baseline Workflow
Use baselines to track only new issues, ignoring pre-existing findings:
# 1. Generate a baseline from the current state of the codebase
bandit -r . -f json -o .bandit-baseline.json
# 2. Commit the baseline to version control
git add .bandit-baseline.json
# 3. Future scans compare against the baseline
bandit -r . -b .bandit-baseline.json
Useful when adopting Bandit on an existing codebase — block only newly introduced issues.
Critical Plugin Categories
Bandit test IDs follow a group scheme:
| Range | Category |
|---|---|
| B1xx | Miscellaneous |
| B2xx | App/framework misconfiguration |
| B3xx | Blacklisted calls |
| B4xx | Blacklisted imports |
| B5xx | Cryptography |
| B6xx | Injection |
| B7xx | XSS |
High-Priority Checks to Always Enforce
Hardcoded secrets (B105, B106, B107) — passwords assigned to variables, passed as function arguments, or set as default parameters.
Injection (B602, B608) — shell injection via subprocess with shell=True, SQL injection via hardcoded SQL string construction.
Weak cryptography (B324, B501–B505) — MD5/SHA1 use, disabled TLS certificate validation, weak SSL versions, short cryptographic keys.
Unsafe deserialization (B301, B302, B303, B304) — pickle, marshal, yaml.load() without Loader.
Template injection (B701, B703, B704) — Jinja2 autoescape disabled, Django mark_safe, MarkupSafe XSS.
Common Findings and Fixes
B101 — assert_used
Asserts are stripped in optimized mode (python -O). Never use assert for security-critical checks.
# Bad
assert user.is_admin, "Not authorized"
# Good
if not user.is_admin:
raise PermissionError("Not authorized")
B105/B106/B107 — Hardcoded password
# Bad
password = "hunter2"
connect(password="secret")
# Good — read from environment or secrets manager
import os
password = os.environ["DB_PASSWORD"]
B324 — Weak hash (MD5/SHA1)
# Bad
import hashlib
hashlib.md5(data)
# Good — use SHA-256 or higher for security contexts
hashlib.sha256(data)
# If MD5 is for non-security use (checksums), suppress with comment:
hashlib.md5(data).hexdigest() # nosec B324 — used for cache key, not security
B506 — yaml.load()
# Bad — arbitrary code execution risk
import yaml
yaml.load(data)
# Good
yaml.safe_load(data)
# or
yaml.load(data, Loader=yaml.SafeLoader)
B602 — subprocess with shell=True
# Bad — shell injection vector
subprocess.Popen(user_input, shell=True)
# Good — pass args as a list, avoid shell
subprocess.Popen(["ls", "-l", path])
B608 — Hardcoded SQL
# Bad
query = "SELECT * FROM users WHERE name = '" + name + "'"
# Good — use parameterized queries
cursor.execute("SELECT * FROM users WHERE name = ?", (name,))
B501 — No certificate validation
# Bad
requests.get(url, verify=False)
# Good
requests.get(url) # verify=True by default
requests.get(url, verify="/path/to/ca-bundle.crt")
Severity Triage Workflow
- Run Bandit with JSON output format and save results to a file (see Output Formats section above).
- Fix all HIGH severity + HIGH confidence findings first — these are near-certain vulnerabilities.
- Evaluate MEDIUM severity findings for false positives; suppress with documented
# nosecif safe. - Decide team policy on LOW severity — consider skipping known false-positive-heavy tests via
skips. - Establish a baseline for legacy codebases to avoid alert fatigue during adoption.
Additional Resources
references/plugin-reference.md— Complete B-code listing with severity, description, and fix pattern per pluginreferences/ci-cd-integration.md— Pre-commit hooks, GitHub Actions, and baseline automation workflows
More from the-perfect-developer/the-perfect-opencode
html
Apply Google HTML style guide conventions to HTML code
19turso-libsql
This skill should be used when the user asks to "connect to Turso", "use libSQL", "set up a Turso database", "query Turso with TypeScript", or needs guidance on Turso Cloud, embedded replicas, or vector search with libSQL.
11alpinejs
This skill should be used when the user asks to "add Alpine.js", "create Alpine component", "use Alpine directives", "build interactive UI with Alpine", or needs guidance on Alpine.js development patterns and best practices.
10python-dependency-injection
This skill should be used when the user asks to "implement dependency injection in Python", "use the dependency-injector library", "decouple Python components", "write testable Python services", or needs guidance on Inversion of Control, DI containers, provider types, and wiring in Python applications.
3copilot-sdk
This skill should be used when the user asks to "integrate GitHub Copilot into an app", "use the Copilot SDK", "build a Copilot-powered agent", "embed Copilot in a service", or needs guidance on the GitHub Copilot SDK for Python, TypeScript, Go, or .NET.
3conventional-git-commit
This skill MUST be loaded on every git commit without exception. It should also be used when the user asks to "write a conventional commit", "format a commit message", "follow conventional commits spec", "create a semantic commit", "make a commit", "commit changes", or "git commit". Every commit message produced in this project MUST conform to this specification.
3