Static Application Security Testing (SAST) isn't just theory. In this article, I'll walk through building a real, hands-on Python security project using Bandit, entirely from the macOS terminal — no fluff, no fake demos.
By the end, you'll have:
- A vulnerable vs secure codebase
- Automated security scanning with Bandit
- HTML + JSON security reports
- A project that actually belongs in a portfolio
Why Bandit?
Bandit is a Python SAST tool that analyzes source code for common security issues such as:
- SQL Injection
- Command Injection
- Cross-Site Scripting (XSS)
- Hardcoded secrets
- Unsafe system calls
It's widely used in CI pipelines and security reviews — not a toy tool.
Project Structure
We'll intentionally create insecure code, then fix it.
SecureCodePortfolio-Bandit/
├── vulnerable/
│ ├── sql_injection.py
│ ├── xss.py
│ ├── command_injection.py
│ └── hardcoded_secret.py
├── secure/
│ ├── sql_injection_fixed.py
│ ├── xss_fixed.py
│ ├── command_injection_fixed.py
│ └── hardcoded_secret_fixed.py
├── requirements.txt
└── bandit_report.htmlStep 1: Create the Project (macOS Terminal)
mkdir SecureCodePortfolio-Bandit
cd SecureCodePortfolio-Bandit
mkdir vulnerable secureStep 2: Install Bandit
Create requirements.txt:
cat <<EOF > requirements.txt
bandit
EOF(Optional but recommended)
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txtVerify installation:
bandit --versionStep 3: Create Vulnerable Code (On Purpose)
❌ SQL Injection
# vulnerable/sql_injection.py
import sqlite3
def get_user(user_id):
conn = sqlite3.connect("users.db")
cursor = conn.cursor()
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute(query)
return cursor.fetchall()Problem: String concatenation → SQL Injection.
❌ Cross-Site Scripting (XSS)
# vulnerable/xss.py
from flask import Flask, request
app = Flask(__name__)
@app.route("/hello")
def hello():
name = request.args.get("name")
return "<h1>Hello " + name + "</h1>"Problem: User input rendered without escaping.
❌ Command Injection
# vulnerable/command_injection.py
import os
def ping(host):
os.system("ping -c 1 " + host)Problem: Shell command injection.
❌ Hardcoded Secret
# vulnerable/hardcoded_secret.py
API_KEY = "sk_test_51H8abcSECRETKEY"
def connect():
return API_KEYProblem: Secrets committed to source code.
Step 4: Secure Implementations
✅ SQL Injection Fixed
# secure/sql_injection_fixed.py
cursor.execute(
"SELECT * FROM users WHERE id = ?",
(user_id,)
)✅ XSS Fixed
# secure/xss_fixed.py
from flask import Flask, request, escape
@app.route("/hello")
def hello():
name = escape(request.args.get("name", ""))
return f"<h1>Hello {name}</h1>"✅ Command Injection Fixed
# secure/command_injection_fixed.py
import subprocess
def ping(host):
subprocess.run(
["ping", "-c", "1", host],
check=True
)✅ Secret Management Fixed
# secure/hardcoded_secret_fixed.py
import os
API_KEY = os.getenv("API_KEY")Step 5: Run Bandit
Full Scan
bandit -r .High Severity Only
bandit -r . -lllGenerate HTML Report
bandit -r . -f html -o bandit_report.html
open bandit_report.htmlGenerate JSON Report
bandit -r . -f json -o bandit_report.jsonExpected Results
FolderResultvulnerable/❌ High & Medium issuessecure/✅ CleanHTML Report📊 Visual evidenceJSON Report🤖 CI / automation ready
This clearly demonstrates security improvement, not just scanning.
One-Liner for Demos / Interviews
bandit -r . -lll -f html -o bandit_report.htmlWhy This Is Portfolio-Ready
This project shows that you:
- Understand OWASP-style vulnerabilities
- Can fix, not just detect, security issues
- Use real industry tooling
- Can generate audit-grade report
This is the kind of project that stands out in:
- Security engineering interviews
- Backend developer roles
- DevSecOps portfolios
What I'll Add Next (Optional Enhancements)
- OWASP Top 10 mapping per finding
- SSRF & Path Traversal examples
- GitHub Actions CI with Bandit
- Resume-ready project description
- Threat model diagram
Final Thoughts
Security isn't about tools — it's about intentional coding. Bandit just makes your intent visible.
If you're learning AppSec, build projects like this. Scanning random repos won't teach you half as much.
If you want, next I can:
- Rewrite this for SEO-heavy Medium growth
- Add diagrams & screenshots
- Convert it into a resume bullet + GitHub README
- Add CI/CD + badges
Just tell me 🔥