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.html

Step 1: Create the Project (macOS Terminal)

mkdir SecureCodePortfolio-Bandit
cd SecureCodePortfolio-Bandit
mkdir vulnerable secure

Step 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.txt

Verify installation:

bandit --version

Step 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_KEY

Problem: 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 . -lll

Generate HTML Report

bandit -r . -f html -o bandit_report.html
open bandit_report.html

Generate JSON Report

bandit -r . -f json -o bandit_report.json

Expected 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.html

Why 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 🔥