| name | bandit |
| description | Find Python security vulnerabilities with Bandit — a static analysis tool that detects common security issues in Python code like SQL injection, command injection, hardcoded secrets, use of insecure functions, and more. Use this skill whenever the user wants to audit Python code for security issues, add security linting to CI/CD, or review Python security findings. Trigger for "bandit python", "python security linting", "bandit scan", "python security audit", or "bandit security". |
| SAFETY NOTE | Bandit is a purely defensive static analysis tool that scans your own codebase. |
Bandit: Python Security Linter
Bandit is a static analysis tool for Python that finds common security issues in source code. It works by building an AST from Python code and running security checks (plugins) against it. It's a mandatory part of any Python project's security posture.
Installation
pip install bandit
pip install bandit[sarif]
pipx install bandit
Basic Usage
bandit my_script.py
bandit -r ./src/
bandit -r ./src/ -ll
bandit -r ./src/ -f json -o bandit-report.json
bandit -r ./src/ -v
bandit -r ./src/ --exit-zero
bandit -r ./src/
Understanding Severity and Confidence
>> Issue: [B608:hardcoded_sql_expressions] Possible SQL injection via string-based query construction.
Severity: Medium Confidence: Medium
CWE: CWE-89 (https://cwe.mitre.org/data/definitions/89.html)
Location: my_app/db.py:45
>> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified
Severity: High Confidence: High
CWE: CWE-78
Location: my_app/utils.py:89
- Severity: How bad the vulnerability is (LOW, MEDIUM, HIGH)
- Confidence: How certain Bandit is this is a real issue (LOW, MEDIUM, HIGH)
- CWE: Common Weakness Enumeration reference
Common Issues Bandit Catches
SQL Injection (B608)
def get_user(username):
query = "SELECT * FROM users WHERE name = '" + username + "'"
cursor.execute(query)
def get_user(username):
query = "SELECT * FROM users WHERE name = %s"
cursor.execute(query, (username,))
Command Injection (B602, B603, B605)
import subprocess
def run_command(user_input):
subprocess.call(user_input, shell=True)
import os
os.system("ls " + user_input)
def run_command(path):
subprocess.run(["ls", "-la", path], check=True)
Hardcoded Passwords and Secrets (B105, B106, B107)
password = "supersecret123"
def connect(host, password="admin123"):
...
import os
password = os.environ["DB_PASSWORD"]
Insecure Cryptography (B303, B304, B324)
import hashlib
hashlib.md5(data).hexdigest()
hashlib.sha1(data).hexdigest()
hashlib.sha256(data).hexdigest()
from Crypto.Cipher import DES
from Crypto.Cipher import AES
XML Vulnerabilities (B313-B320)
from xml.etree import ElementTree
tree = ElementTree.parse("file.xml")
import defusedxml.ElementTree
tree = defusedxml.ElementTree.parse("file.xml")
pip install defusedxml
Insecure Deserialization (B301, B302)
import pickle
data = pickle.loads(user_input)
import json
data = json.loads(user_input)
Random Number Security (B311)
import random
token = random.randint(100000, 999999)
import secrets
token = secrets.randbelow(900000) + 100000
session_key = secrets.token_hex(32)
Assert Statements (B101)
assert user.is_authenticated, "Must be logged in"
if not user.is_authenticated:
raise PermissionError("Must be logged in")
CI/CD Integration
GitHub Actions
name: Python Security Check
on: [push, pull_request]
jobs:
bandit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Bandit
run: pip install bandit[sarif]
- name: Run Bandit security scan
run: |
bandit -r src/ \
-f sarif \
-o bandit-results.sarif \
--severity-level medium \
--confidence-level medium
- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
Pre-commit Hook
repos:
- repo: https://github.com/PyCQA/bandit
rev: 1.7.8
hooks:
- id: bandit
args: ['-c', 'pyproject.toml']
types: [python]
Configuration
[tool.bandit]
exclude_dirs = ["tests", "venv", ".venv", "migrations"]
skips = ["B101", "B404"]
severity = "MEDIUM"
confidence = "MEDIUM"
[tool.bandit.assert_used]
skips = ["*_test.py", "*test_*.py"]
[bandit]
targets: src
exclude: tests,venv
skips: B101,B404
level: 2
confidence: 2
Suppressing False Positives
hash_value = hashlib.md5(non_sensitive_data).hexdigest()
result = subprocess.run(
["git", "log", "--oneline"],
capture_output=True,
text=True,
)
Useful Bandit Test IDs
| Test ID | Issue | Severity |
|---|
| B101 | assert_used | Low |
| B105 | hardcoded_password_string | Low |
| B106 | hardcoded_password_funcarg | Low |
| B301 | pickle usage | Medium |
| B303 | MD5/SHA1 use | Medium |
| B311 | random for security | Low |
| B324 | hashlib insecure | High |
| B501-B504 | SSL/TLS issues | High |
| B601 | paramiko shell | High |
| B602 | subprocess shell=True | High |
| B608 | SQL injection | Medium |
| B701 | Jinja2 autoescape | High |
| B703 | Django mark_safe | Medium |
GitNexus Index
This skill is indexed by GitNexus for knowledge graph traversal.
Index path: /Users/localuser/.claude/skills/bandit/.gitnexus
Last indexed: 2026-05-24