Running SAST and SCA Locally: Bandit and Safety Scanner Troubleshooting
The Static Analysis Using Bandit and Software Component Analysis Using Safety exercises teach hands-on use of Python security scanners. These exercises appear across 4 courses. This article covers installation issues, scan failures, and interpreting results.
Table of Contents
- Bandit Installation
- Bandit Scan: Recursive and Output Formats
- Bandit Severity Filtering
- Bandit Exit Codes
- Safety Installation Errors
- Safety Policy File Required
- Safety Scan Output
- Common Questions
Bandit Installation
Install Bandit
pip3 install bandit==1.8.5
This installs bandit with dependencies: PyYAML, stevedore, rich, markdown-it-py, pygments, mdurl.
Verify Installation
bandit --help
If bandit: command not found, check your PATH or try python3 -m bandit --help.
Bandit Scan: Recursive and Output Formats
Recursive Scan
bandit -r . -f json | tee bandit-output.json
Flags:
| Flag | Purpose |
|---|---|
-r . |
Recursively scan the current directory |
-f json |
JSON output format |
-o file.json |
Write output to file (alternative to tee) |
Key concept: Always use -r for recursive scanning. Without it, bandit only scans files explicitly listed.
Output Formats
Bandit supports multiple output formats:
screen(default): Colored terminal outputjson: Machine-readable, good for CI/CD pipelineshtml: Browser-viewable reportcsv: Spreadsheet formatxml: For tool integrationyaml: YAML format
Bandit Severity Filtering
Filter by Severity Level
Bandit findings have three severity levels. By default, all findings are shown:
bandit -r . -l # LOW and above (default)
bandit -r . -ll # MEDIUM and above
bandit -r . -lll # HIGH only
Common scenario: In CI/CD pipelines, you often want to fail only on HIGH severity findings:
bandit -r . -lll # Only fails if HIGH severity issues found
Understanding Results
A typical scan might find:
- 1 HIGH severity issue
- 1 MEDIUM severity issue
- 5 LOW severity issues
Results are variable and depend on the codebase being scanned.
Bandit Exit Codes
| Exit Code | Meaning |
|---|---|
0 |
No security issues found at the specified severity level |
1 |
Security issues detected |
2 |
Error during scan (invalid arguments, code parsing errors) |
Key concept: Exit code 1 means vulnerabilities were found. This is expected in most real-world codebases and is not a scanner error.
Safety Installation Errors <a name="safety-install>
Install Safety
pip3 install safety==2.3.5
Build Dependencies May Be Missing
If pip fails to install Safety due to missing C build tools:
Error: Could not build wheels for safety
Fix: Install build dependencies first:
apt-get update && apt-get install -y build-essential python3-dev
pip3 install safety==2.3.5
Safety Policy File Required
.safety-policy.yml Must Exist Before Scanning
Without a policy file, Safety may fail:
No such file or directory: .safety-policy.yml
Fix: Create the policy file before running the scan:
cat > .safety-policy.yml <<EOF
security:
ignore-vulnerabilities: {}
EOF
An empty ignore-vulnerabilities: {} means all vulnerabilities are reported. You can add specific vulnerability IDs to suppress them:
security:
ignore-vulnerabilities:
12345: {} # Ignore vulnerability ID 12345
Safety Scan Output
Running the Scan
safety check -r requirements.txt --json | tee safety-output.json
Interpreting Results
A typical scan output shows vulnerable packages with CVE details:
Found 26 known vulnerability alerts affecting 15 packages
For example, Django 3.0 may have CVE-2022-34265 (SQL injection via Trunc/Extract).
Safety Exit Codes
| Exit Code | Meaning |
|---|---|
0 |
No vulnerabilities found |
64 |
Vulnerabilities found |
Key concept: Exit code 64 is normal — it means the scanner did its job and found vulnerable dependencies.
Output Formats
safety check -r requirements.txt -o screen # Terminal output
safety check -r requirements.txt -o json # JSON output
safety check -r requirements.txt -o text # Plain text
safety check -r requirements.txt -o bare # Minimal output (just vuln IDs)
Common Questions
| Question | Answer |
|---|---|
| Bandit returns exit code 1 — is that an error? | No. Exit code 1 means vulnerabilities were found. This is expected behavior. |
| How do I only see HIGH severity findings? | Use bandit -r . -lll (three L's for HIGH only). |
| Safety fails with "No such file or directory: .safety-policy.yml." | Create the policy file first: cat > .safety-policy.yml <<EOF ... EOF. |
pip3 install safety fails with build errors. |
Install build dependencies: apt-get install -y build-essential python3-dev, then retry. |
| Where does Safety get its vulnerability database? | Safety queries pyup.io's database online. An API key (--key) unlocks the full database. |
| How do I ignore a specific vulnerability in Safety? | Add its ID to .safety-policy.yml under ignore-vulnerabilities. |
Wrap-Up
Bandit (SAST) and Safety (SCA/OAST) are foundational Python security tools. Key takeaways:
- Bandit scans source code for security issues. Use
-rfor recursive,-lllfor HIGH-only, and-f jsonfor machine-readable output. - Safety scans dependencies for known vulnerabilities. Requires
.safety-policy.ymlbefore scanning. - Both tools return non-zero exit codes when they find issues — this is expected, not an error.
- Exit code 1 (Bandit) and exit code 64 (Safety) mean vulnerabilities were found.
- Install build dependencies (
build-essential python3-dev) if Safety pip install fails.
The DevSecOps Box is stateless — all installed packages and files are lost on page refresh.