Bandit SAST
This skill performs static application security testing (SAST) for Python projects using Bandit, identifying common security anti-patterns such as use of dangerous functions, hardcoded credentials, insecure cryptography, and injection risks, then mapping findings to CWE and OWASP Top 10:2021 standards.
When to Use
- When the user asks to "scan Python code for security issues" or "run Bandit"
- When the user mentions "Python SAST" or "security scan Python"
- When reviewing Python code for vulnerabilities before deployment
- When a pull request contains changes to
.py files and a security check is requested
- When the user asks to find insecure patterns like
eval, exec, pickle, or hardcoded passwords in Python
When NOT to Use
- When scanning non-Python code (JavaScript, Go, Java, etc.) — you MUST decline and recommend
semgrep-rule-creator or the language-specific tool instead
- When the user is asking about Python code style, formatting, or linting — you MUST decline and recommend
pylint or flake8
- When the user wants runtime or dynamic analysis of a running application — you MUST decline and recommend DAST tools like
dast-nuclei
- When the user wants to generate security test code — you MUST decline and recommend
security-test-generator
- When the user wants a CI/CD security pipeline — you MUST decline and recommend
devsecops-pipeline
- When the
security-review skill already covers the request at a general level and no Python-specific SAST depth is needed
Prerequisites
Tool Installed (Preferred)
# Detection
which bandit || python -m bandit --version
# Installation (if not found)
pip install bandit
Minimum version: Bandit 1.7+. No API key required.
Tool Not Installed (Fallback)
Note: This is a limited review. Install Bandit for comprehensive scanning with full test coverage.
When Bandit is not available, perform these top-10 manual Python security checks:
- eval() / exec() usage — Search for
eval( and exec( calls, especially with user-controlled input
- subprocess with shell=True — Search for
subprocess.call(, subprocess.Popen(, subprocess.run( with shell=True and string interpolation
- Hardcoded passwords — Search for variables named
password, passwd, secret, api_key assigned to string literals
- pickle deserialization — Search for
pickle.loads(, pickle.load(, cPickle.load( on untrusted data
- Weak hashing — Search for
hashlib.md5(, hashlib.sha1( used for password hashing or security-sensitive operations
- assert in production — Search for
assert statements used for input validation (stripped in optimized mode)
- Wildcard imports — Search for
from module import * which can mask injected names
- try-except-pass — Search for bare
except: or except Exception: followed by pass, which silences security errors
- Insecure temp files — Search for
tempfile.mktemp( (use tempfile.mkstemp() or NamedTemporaryFile instead)
- yaml.load() without SafeLoader — Search for
yaml.load( without Loader=yaml.SafeLoader or yaml.safe_load(
Workflow
MANDATORY FIRST ACTION — Verify the tool before reporting its output.
Your first Bash call must be command -v bandit || bandit --version. Branch on the result:
- Bandit is available — proceed with the installed-tool workflow (step 3a below). The report may use
## Bandit SAST Scan Results, cite B-series test IDs (B101, B602, etc.), and reference the Bandit version, because real Bandit output backs all of it.
- Bandit is not available — proceed with the fallback workflow (step 3b below). The report must:
- Use header
## Python Security Review (Manual Fallback).
- Open with:
> Note: This is a limited review. Install Bandit for comprehensive scanning.
- Cite CWE + OWASP only. B-series test IDs are Bandit's internal taxonomy — using them without running Bandit misattributes the findings to a tool that did not produce them.
- Not claim a scanner, version, or file count that the turn history doesn't show.
The contract is simple: every artifact in the report must trace back to something the skill actually did in this turn. If you did not run bandit, don't present Bandit results. The user is relying on the report matching what was actually scanned.
- Detect Python project — Confirm Python files exist by checking for
*.py files, requirements.txt, setup.py, pyproject.toml, or Pipfile.
- Check for Bandit — Run
which bandit || python -m bandit --version to determine if Bandit is installed.
- If Bandit is installed:
a. Run
bandit -r . -f json -q to scan all Python files recursively with JSON output.
b. Parse the JSON output — each result contains test_id, test_name, issue_severity, issue_confidence, issue_text, filename, and line_number.
c. Map each test_id to its CWE using the Reference Tables below.
d. Map each CWE to its OWASP Top 10:2021 category.
- If Bandit is NOT installed:
a. Offer to install via
pip install bandit.
b. If the user declines, run the 10 manual fallback checks listed in Prerequisites.
c. Include the disclaimer: "This is a limited review. Install Bandit for comprehensive scanning."
- Compile findings — Deduplicate results and sort by severity: Critical > High > Medium > Low.
- Generate report — Present findings using the Findings Format below.
- Summarize — State total findings, breakdown by severity, and top 3 remediation priorities.
Findings Format
MANDATORY FORMAT: You MUST include Severity, CWE, and OWASP Top 10:2021 mapping on every finding. Use the exact table format shown below — do not use freeform text.
Each finding should include:
| Field |
Description |
| Severity |
Critical / High / Medium / Low |
| CWE |
CWE-XXX identifier |
| OWASP |
A01-A10 category (OWASP Top 10:2021) |
| Location |
file:line |
| Issue |
Description of the vulnerability |
| Remediation |
How to fix it |
Example Finding
| Field |
Value |
| Severity |
High |
| CWE |
CWE-78 |
| OWASP |
A03:2021 - Injection |
| Location |
app/utils.py:27 |
| Issue |
subprocess.call() with shell=True and f-string user input enables OS command injection |
| Remediation |
Use subprocess.run() with a list of arguments and shell=False (default) |
Reference Tables
Bandit Test ID to CWE Mapping
| Bandit Test ID |
Test Name |
CWE |
OWASP |
Default Severity |
| B101 |
assert_used |
CWE-703 |
A07:2021 - Security Misconfiguration |
Low |
| B102 |
exec_used |
CWE-78 |
A03:2021 - Injection |
Medium |
| B301 |
pickle |
CWE-502 |
A08:2021 - Software and Data Integrity Failures |
Medium |
| B303 |
md5 / sha1 |
CWE-328 |
A02:2021 - Cryptographic Failures |
Medium |
| B306 |
mktemp_q |
CWE-377 |
A01:2021 - Broken Access Control |
Medium |
| B307 |
eval |
CWE-78 |
A03:2021 - Injection |
Medium |
| B501 |
request_with_no_cert_validation |
CWE-295 |
A07:2021 - Security Misconfiguration |
High |
| B602 |
subprocess_popen_with_shell_equals_true |
CWE-78 |
A03:2021 - Injection |
High |
| B603 |
subprocess_without_shell_equals_true |
CWE-78 |
A03:2021 - Injection |
Low |
| B608 |
hardcoded_sql_expressions |
CWE-89 |
A03:2021 - Injection |
Medium |
| B105 |
hardcoded_password_string |
CWE-259 |
A07:2021 - Security Misconfiguration |
Low |
| B106 |
hardcoded_password_funcarg |
CWE-259 |
A07:2021 - Security Misconfiguration |
Low |
| B403 |
import_pickle |
CWE-502 |
A08:2021 - Software and Data Integrity Failures |
Low |
| B506 |
yaml_load |
CWE-502 |
A08:2021 - Software and Data Integrity Failures |
Medium |
OWASP Top 10:2021 Quick Reference
| Category |
Description |
Common CWEs |
| A01:2021 |
Broken Access Control |
CWE-377 |
| A02:2021 |
Cryptographic Failures |
CWE-328 |
| A03:2021 |
Injection |
CWE-78, CWE-89 |
| A07:2021 |
Security Misconfiguration |
CWE-259, CWE-295, CWE-703 |
| A08:2021 |
Software and Data Integrity Failures |
CWE-502 |
Example Usage
With Bandit Installed
User prompt:
"Run a Bandit scan on this Python project"
Expected output (abbreviated):
## Bandit SAST Scan Results
Scanned 14 files in src/
### Findings (4 total: 0 Critical, 2 High, 1 Medium, 1 Low)
| # | Severity | CWE | OWASP | Location | Issue |
|---|----------|-----|-------|----------|-------|
| 1 | High | CWE-78 | A03 | src/deploy.py:31 | subprocess.Popen() with shell=True and user input (B602) |
| 2 | High | CWE-78 | A03 | src/utils.py:12 | eval() called with user-supplied string (B307) |
| 3 | Medium | CWE-502 | A08 | src/cache.py:45 | pickle.loads() on data from network socket (B301) |
| 4 | Low | CWE-703 | A07 | src/config.py:8 | assert used for input validation (B101) |
### Recommendations
1. Replace subprocess shell calls with list-based arguments (Finding #1)
2. Replace eval() with ast.literal_eval() or a safe parser (Finding #2)
3. Use json.loads() instead of pickle for untrusted data (Finding #3)
Without Bandit (Fallback Mode)
User prompt:
"Check this Python code for security issues"
Expected output (abbreviated):
## Python Security Review (Manual Fallback)
> Note: This is a limited review. Install Bandit for comprehensive scanning.
Scanned 8 .py files
### Findings (2 total: 0 Critical, 1 High, 1 Medium)
| # | Severity | CWE | OWASP | Location | Issue |
|---|----------|-----|-------|----------|-------|
| 1 | High | CWE-78 | A03 | scripts/run.py:19 | subprocess.call() with shell=True and string formatting |
| 2 | Medium | CWE-259 | A07 | config/settings.py:5 | Hardcoded password: DB_PASSWORD = "admin123" |
### Recommendations
1. Use subprocess.run() with a list of arguments instead of shell=True (Finding #1)
2. Move credentials to environment variables or a secrets manager (Finding #2)
1---2name: bandit-sast3description: Use when scanning Python code for security vulnerabilities, running Bandit, performing Python SAST, auditing Python security bugs, or reviewing Python source for injection, weak crypto, or insecure deserialization.4---5
6# Bandit SAST
7
8This skill performs static application security testing (SAST) for Python projects using Bandit, identifying common security anti-patterns such as use of dangerous functions, hardcoded credentials, insecure cryptography, and injection risks, then mapping findings to CWE and OWASP Top 10:2021 standards.
9
10## When to Use
11
12- When the user asks to "scan Python code for security issues" or "run Bandit"
13- When the user mentions "Python SAST" or "security scan Python"
14- When reviewing Python code for vulnerabilities before deployment
15- When a pull request contains changes to `.py` files and a security check is requested
16- When the user asks to find insecure patterns like `eval`, `exec`, `pickle`, or hardcoded passwords in Python
17
18## When NOT to Use
19
20- When scanning non-Python code (JavaScript, Go, Java, etc.) — you **MUST** decline and recommend `semgrep-rule-creator` or the language-specific tool instead
21- When the user is asking about Python code style, formatting, or linting — you **MUST** decline and recommend `pylint` or `flake8`
22- When the user wants runtime or dynamic analysis of a running application — you **MUST** decline and recommend DAST tools like `dast-nuclei`
23- When the user wants to generate security test code — you **MUST** decline and recommend `security-test-generator`
24- When the user wants a CI/CD security pipeline — you **MUST** decline and recommend `devsecops-pipeline`
25- When the `security-review` skill already covers the request at a general level and no Python-specific SAST depth is needed
26
27## Prerequisites
28
29### Tool Installed (Preferred)
30
31```bash
32# Detection
33which bandit || python -m bandit --version
34
35# Installation (if not found)
36pip install bandit
37```
38
39Minimum version: Bandit 1.7+. No API key required.
40
41### Tool Not Installed (Fallback)
42
43> **Note:** This is a limited review. Install Bandit for comprehensive scanning with full test coverage.
44
45When Bandit is not available, perform these top-10 manual Python security checks:
46
471. **eval() / exec() usage** — Search for `eval(` and `exec(` calls, especially with user-controlled input
482. **subprocess with shell=True** — Search for `subprocess.call(`, `subprocess.Popen(`, `subprocess.run(` with `shell=True` and string interpolation
493. **Hardcoded passwords** — Search for variables named `password`, `passwd`, `secret`, `api_key` assigned to string literals
504. **pickle deserialization** — Search for `pickle.loads(`, `pickle.load(`, `cPickle.load(` on untrusted data
515. **Weak hashing** — Search for `hashlib.md5(`, `hashlib.sha1(` used for password hashing or security-sensitive operations
526. **assert in production** — Search for `assert` statements used for input validation (stripped in optimized mode)
537. **Wildcard imports** — Search for `from module import *` which can mask injected names
548. **try-except-pass** — Search for bare `except:` or `except Exception:` followed by `pass`, which silences security errors
559. **Insecure temp files** — Search for `tempfile.mktemp(` (use `tempfile.mkstemp()` or `NamedTemporaryFile` instead)
5610. **yaml.load() without SafeLoader** — Search for `yaml.load(` without `Loader=yaml.SafeLoader` or `yaml.safe_load(`
57
58## Workflow
59
60> **MANDATORY FIRST ACTION — Verify the tool before reporting its output.**
61>
62> Your first Bash call must be `command -v bandit || bandit --version`. Branch on the result:
63>
64> - **Bandit is available** — proceed with the installed-tool workflow (step 3a below). The report may use `## Bandit SAST Scan Results`, cite B-series test IDs (B101, B602, etc.), and reference the Bandit version, because real Bandit output backs all of it.
65> - **Bandit is not available** — proceed with the fallback workflow (step 3b below). The report must:
66> - Use header `## Python Security Review (Manual Fallback)`.
67> - Open with: `> Note: This is a limited review. Install Bandit for comprehensive scanning.`
68> - Cite CWE + OWASP only. B-series test IDs are Bandit's internal taxonomy — using them without running Bandit misattributes the findings to a tool that did not produce them.
69> - Not claim a scanner, version, or file count that the turn history doesn't show.
70>
71> The contract is simple: **every artifact in the report must trace back to something the skill actually did in this turn.** If you did not run `bandit`, don't present Bandit results. The user is relying on the report matching what was actually scanned.
72
731. **Detect Python project** — Confirm Python files exist by checking for `*.py` files, `requirements.txt`, `setup.py`, `pyproject.toml`, or `Pipfile`.
742. **Check for Bandit** — Run `which bandit || python -m bandit --version` to determine if Bandit is installed.
753. **If Bandit is installed:**
76 a. Run `bandit -r . -f json -q` to scan all Python files recursively with JSON output.
77 b. Parse the JSON output — each result contains `test_id`, `test_name`, `issue_severity`, `issue_confidence`, `issue_text`, `filename`, and `line_number`.
78 c. Map each `test_id` to its CWE using the Reference Tables below.
79 d. Map each CWE to its OWASP Top 10:2021 category.
804. **If Bandit is NOT installed:**
81 a. Offer to install via `pip install bandit`.
82 b. If the user declines, run the 10 manual fallback checks listed in Prerequisites.
83 c. Include the disclaimer: "This is a limited review. Install Bandit for comprehensive scanning."
845. **Compile findings** — Deduplicate results and sort by severity: Critical > High > Medium > Low.
856. **Generate report** — Present findings using the Findings Format below.
867. **Summarize** — State total findings, breakdown by severity, and top 3 remediation priorities.
87
88## Findings Format
89
90> **MANDATORY FORMAT:** You **MUST** include Severity, CWE, and OWASP Top 10:2021 mapping on **every** finding. Use the exact table format shown below — do not use freeform text.
91
92Each finding should include:
93
94| Field | Description |
95|-------|-------------|
96| Severity | Critical / High / Medium / Low |
97| CWE | CWE-XXX identifier |
98| OWASP | A01-A10 category (OWASP Top 10:2021) |
99| Location | file:line |
100| Issue | Description of the vulnerability |
101| Remediation | How to fix it |
102
103### Example Finding
104
105| Field | Value |
106|-------|-------|
107| Severity | High |
108| CWE | CWE-78 |
109| OWASP | A03:2021 - Injection |
110| Location | app/utils.py:27 |
111| Issue | `subprocess.call()` with `shell=True` and f-string user input enables OS command injection |
112| Remediation | Use `subprocess.run()` with a list of arguments and `shell=False` (default) |
113
114## Reference Tables
115
116### Bandit Test ID to CWE Mapping
117
118| Bandit Test ID | Test Name | CWE | OWASP | Default Severity |
119|----------------|-----------|-----|-------|------------------|
120| B101 | assert_used | CWE-703 | A07:2021 - Security Misconfiguration | Low |
121| B102 | exec_used | CWE-78 | A03:2021 - Injection | Medium |
122| B301 | pickle | CWE-502 | A08:2021 - Software and Data Integrity Failures | Medium |
123| B303 | md5 / sha1 | CWE-328 | A02:2021 - Cryptographic Failures | Medium |
124| B306 | mktemp_q | CWE-377 | A01:2021 - Broken Access Control | Medium |
125| B307 | eval | CWE-78 | A03:2021 - Injection | Medium |
126| B501 | request_with_no_cert_validation | CWE-295 | A07:2021 - Security Misconfiguration | High |
127| B602 | subprocess_popen_with_shell_equals_true | CWE-78 | A03:2021 - Injection | High |
128| B603 | subprocess_without_shell_equals_true | CWE-78 | A03:2021 - Injection | Low |
129| B608 | hardcoded_sql_expressions | CWE-89 | A03:2021 - Injection | Medium |
130| B105 | hardcoded_password_string | CWE-259 | A07:2021 - Security Misconfiguration | Low |
131| B106 | hardcoded_password_funcarg | CWE-259 | A07:2021 - Security Misconfiguration | Low |
132| B403 | import_pickle | CWE-502 | A08:2021 - Software and Data Integrity Failures | Low |
133| B506 | yaml_load | CWE-502 | A08:2021 - Software and Data Integrity Failures | Medium |
134
135### OWASP Top 10:2021 Quick Reference
136
137| Category | Description | Common CWEs |
138|----------|-------------|-------------|
139| A01:2021 | Broken Access Control | CWE-377 |
140| A02:2021 | Cryptographic Failures | CWE-328 |
141| A03:2021 | Injection | CWE-78, CWE-89 |
142| A07:2021 | Security Misconfiguration | CWE-259, CWE-295, CWE-703 |
143| A08:2021 | Software and Data Integrity Failures | CWE-502 |
144
145## Example Usage
146
147### With Bandit Installed
148
149**User prompt:**
150> "Run a Bandit scan on this Python project"
151
152**Expected output (abbreviated):**
153
154```text
155## Bandit SAST Scan Results
156
157Scanned 14 files in src/
158
159### Findings (4 total: 0 Critical, 2 High, 1 Medium, 1 Low)
160
161| # | Severity | CWE | OWASP | Location | Issue |
162|---|----------|-----|-------|----------|-------|
163| 1 | High | CWE-78 | A03 | src/deploy.py:31 | subprocess.Popen() with shell=True and user input (B602) |
164| 2 | High | CWE-78 | A03 | src/utils.py:12 | eval() called with user-supplied string (B307) |
165| 3 | Medium | CWE-502 | A08 | src/cache.py:45 | pickle.loads() on data from network socket (B301) |
166| 4 | Low | CWE-703 | A07 | src/config.py:8 | assert used for input validation (B101) |
167
168### Recommendations
1691. Replace subprocess shell calls with list-based arguments (Finding #1)
1702. Replace eval() with ast.literal_eval() or a safe parser (Finding #2)
1713. Use json.loads() instead of pickle for untrusted data (Finding #3)
172```
173
174### Without Bandit (Fallback Mode)
175
176**User prompt:**
177> "Check this Python code for security issues"
178
179**Expected output (abbreviated):**
180
181```text
182## Python Security Review (Manual Fallback)
183
184> Note: This is a limited review. Install Bandit for comprehensive scanning.
185
186Scanned 8 .py files
187
188### Findings (2 total: 0 Critical, 1 High, 1 Medium)
189
190| # | Severity | CWE | OWASP | Location | Issue |
191|---|----------|-----|-------|----------|-------|
192| 1 | High | CWE-78 | A03 | scripts/run.py:19 | subprocess.call() with shell=True and string formatting |
193| 2 | Medium | CWE-259 | A07 | config/settings.py:5 | Hardcoded password: DB_PASSWORD = "admin123" |
194
195### Recommendations
1961. Use subprocess.run() with a list of arguments instead of shell=True (Finding #1)
1972. Move credentials to environment variables or a secrets manager (Finding #2)
198```