From qa-sast
Configures and runs Bandit - Python-specific SAST from the OpenStack security plugin set covering 60+ rule IDs across 7 categories (B1xx misc, B2xx application, B3xx blacklists/cryptography, B4xx imports, B5xx, B6xx injections, B7xx XSS); supports `bandit -r .` recursive scan, `--severity-level low|medium|high`, `--confidence-level low|medium|high` filtering, `# nosec` and `# nosec B404` per-line + per-rule suppressions, `pyproject.toml` `[tool.bandit]` config including `exclude_dirs`. Use when the user works with Python and needs a focused, low-overhead SAST integrated with pre-commit / CI.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-sast:bandit-pythonThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Per [bandit.readthedocs.io/en/latest/start.html][bd-start]:
Per bandit.readthedocs.io/en/latest/start.html:
Bandit is the Python-specific SAST originally from OpenStack Security. Each finding has two dimensions:
The two-dimensional scoring lets you tune false-positive vs false-negative tradeoff per project.
semgrep-rules (broader,
cross-language) for layered coverage.Per bd-start:
pip install bandit[toml]
The [toml] extra enables pyproject.toml config support.
Per bd-start:
bandit -r path/to/your/code
Common usage:
bandit -r . # current dir, recursive
bandit -r src/ tests/ # multiple paths
bandit -r . -x tests,vendor # exclude dirs
bandit -r . -ll # minimum LOW confidence + LOW severity
Per bd-start verbatim CLI usage:
bandit examples/*.py -n 3 --severity-level=high
Combined two-dimensional filtering:
# Only HIGH severity findings with HIGH confidence
bandit -r . --severity-level=high --confidence-level=high
# All MEDIUM+ severity, any confidence
bandit -r . --severity-level=medium
The two flags compose; neither is a strict subset of the other.
pyproject.toml config# pyproject.toml
[tool.bandit]
exclude_dirs = ["tests", "vendor", "build"]
skips = ["B101"] # skip "assert used" rule globally
tests = ["B201", "B301"] # only run flask + pickle checks (whitelist mode)
[tool.bandit.assert_used]
skips = ["**/test_*.py", "**/*_test.py"]
tests = [...] activates whitelist mode (run ONLY listed checks);
skips = [...] activates blacklist mode (run all checks except
listed). They're mutually exclusive.
Bandit rules are organized by category prefix:
| Prefix | Category | Examples |
|---|---|---|
| B1xx | Miscellaneous | B101 assert used, B102 exec used, B105 hardcoded password string |
| B2xx | Application/Framework | B201 flask debug=True, B202 tarfile unsafe extract |
| B3xx | Blacklists / Cryptography | B301 pickle, B303 MD5, B311 random for crypto, B321 ftplib (cleartext), B324 hashlib weak hash, B403 import_pickle |
| B4xx | Imports | B401 import_telnetlib, B404 subprocess imported, B405 import_xml_etree, B413 import_pyCrypto |
| B5xx | (less common - varies) | |
| B6xx | Injections | B602 subprocess shell=True, B603 subprocess without shell=False, B608 sql_injection, B610 django extra used (sql injection-prone) |
| B7xx | XSS / templating | B701 jinja2 autoescape false, B703 django mark_safe |
Full catalog: bandit.readthedocs.io/en/latest/plugins/.
Per the canonical Bandit workflow, three suppression layers:
| Mechanism | Example | When to use |
|---|---|---|
Per-line # nosec | subprocess.run(cmd, shell=True) # nosec B602 | Single-line exception with rule ID |
Per-rule # nosec | # nosec B404 (above import statement) | Rule-specific suppression |
[tool.bandit] skips = ["..."] | Per-project rule disable | Categorical disable (test fixtures, etc.) |
[tool.bandit] exclude_dirs = ["..."] | Per-directory exclude | Generated code, vendored libs |
Justification template (mandatory in code):
import subprocess
# nosec B602 - Reason: command is statically defined, no user input
# Reviewer: [email protected] (2026-05-15)
# Expires: 2026-12-15
result = subprocess.run("ls -la /tmp", shell=True, check=True)
For rules that can never apply (e.g., B311 random is fine
outside crypto contexts), prefer per-rule disable in pyproject.toml
over per-line suppressions - fewer comments to maintain, easier to
audit at the project level.
Cadence: every quarter, grep for # nosec patterns lacking
# Reason: lines; flag for review.
bandit -r . -f txt # default human-readable
bandit -r . -f json -o bandit.json # JSON for sast-finding-triager
bandit -r . -f sarif -o bandit.sarif # SARIF for GitHub Code Scanning
bandit -r . -f xml -o bandit.xml # JUnit XML
bandit -r . -f html -o bandit.html # standalone HTML report
bandit -r . -f csv -o bandit.csv # CSV
bandit -r . -f screen # colorized terminal
# .pre-commit-config.yaml
repos:
- repo: https://github.com/PyCQA/bandit
rev: 1.7.10
hooks:
- id: bandit
args: ["--severity-level=medium", "--confidence-level=medium"]
files: \.py$
exclude: ^(tests/|venv/|.venv/)
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with: { python-version: '3.13' }
- run: pip install bandit[toml]
- run: bandit -r . -f sarif -o bandit.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: bandit.sarif }
For PR-blocking: pipe through --severity-level high to limit
noise.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
--severity-level=low everywhere | Noise overwhelms; team disables | Start --severity-level=medium; ratchet down (Step 3) |
# nosec without rule ID | Suppresses ALL rules on that line; over-broad | # nosec B602 (specific rule, Step 6) |
Skip --confidence-level filter | LOW confidence findings mostly false positives | Pair --severity-level=high --confidence-level=medium for triage |
| Run on tests directory | Test-only patterns (assert, pickle) trigger noise | exclude_dirs = ["tests"] (Step 4) |
| No baseline; every legacy finding blocks CI | Team disables Bandit | Use --baseline old-findings.json against a captured baseline |
gosec-go, for
JS use Semgrep.codeql-queries
for that).semgrep-rules,
sonarqube-rules,
codeql-queries,
gosec-go - sister scannerssast-finding-triager -
unifier agentProvides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.
npx claudepluginhub testland/qa --plugin qa-sast