| name | injection |
| description | Expert Python security testing skill for OWASP A05:2025 โ Injection vulnerabilities in FastAPI and Flask applications. Use this skill whenever the user asks about: SQL injection, XSS, SSTI (Server-Side Template Injection), OS command injection, ORM injection, LLM prompt injection, input validation, parameterized queries, Jinja2 escaping, or any untrusted data reaching an interpreter (database, OS shell, template engine, or browser). Also trigger for security audits, pen testing, Bandit SAST scans, code review for injection flaws, or any request to "find injection bugs", "check for SQLi", "audit my Flask/FastAPI app", or "scan for command injection". This skill has the highest CVE count of all OWASP categories (62,445) โ always apply it proactively when reviewing Python web application security.
|
OWASP A05:2025 โ Injection: Python Security Testing Skill
Overview
Injection occurs when untrusted user input reaches an interpreter โ a database, OS shell, template engine, or browser โ
and is executed as part of a command or query. Despite dropping from #3 to #5 in 2025, Injection carries the most CVEs
of any OWASP category: 62,445, including 30,000+ for XSS alone.
Injection types covered by this skill:
- SQL Injection (SQLi) โ CWE-89
- Cross-Site Scripting (XSS) โ CWE-79
- Server-Side Template Injection (SSTI) โ CWE-94
- OS Command Injection โ CWE-78 / CWE-77
- ORM Injection
- Input Validation failures โ CWE-20
- LLM Prompt Injection (related class, noted in OWASP 2025)
Detection Checklist
1. SQL Injection (CWE-89)
High-risk patterns to flag:
db.execute(f"SELECT * FROM users WHERE email = '{email}'")
db.execute(text("SELECT * FROM users WHERE id = {}".format(user_id)))
User.query.filter(text("name = '" + name + "'"))
Safe patterns to enforce:
db.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
User.query.filter_by(email=email).first()
async def get_user(user_id: int, db: Session = Depends(get_db)):
return db.query(User).filter(User.id == user_id).first()
Testing approach:
- Search codebase for
text(, execute(, raw(, cursor.execute( + string interpolation
- Run Bandit:
bandit -r . -t B608 (hardcoded SQL expressions)
- Inject payloads:
' OR '1'='1, '; DROP TABLE users; --, 1 UNION SELECT null,null--
- Use OWASP ZAP active scan on all endpoints accepting string parameters
2. XSS โ Cross-Site Scripting (CWE-79)
High-risk patterns to flag:
app = Flask(__name__)
@app.route("/greet")
def greet():
name = request.args.get("name")
return render_template_string(f"<h1>Hello {name}</h1>")
Safe patterns to enforce:
from markupsafe import escape
safe_name = escape(request.args.get("name", ""))
@app.after_request
def set_csp(response):
response.headers["Content-Security-Policy"] = "default-src 'self'"
return response
Testing approach:
- Grep templates for
| safe โ each use needs manual review
- Confirm
autoescape=True in Jinja2 environment for all template types
- Inject:
<script>alert(1)</script>, "><img src=x onerror=alert(1)>
- Check Content-Security-Policy and X-XSS-Protection headers in responses
3. SSTI โ Server-Side Template Injection (CWE-94)
The most critical Injection risk for Flask apps using Jinja2. User input rendered as a template string allows full
Python expression execution.
High-risk patterns to flag:
from jinja2 import Template
user_template = request.args.get("template")
return Template(user_template).render()
return render_template_string(user_provided_content)
msg_template = user_input
msg_template.format(user=current_user)
Safe patterns to enforce:
return render_template("greet.html", name=user_name)
from jinja2.sandbox import SandboxedEnvironment
env = SandboxedEnvironment()
env.from_string(template_str).render(data=safe_data)
Testing approach:
- Search for
render_template_string(, Template(, .format( where source contains user data
- Inject SSTI probes:
{{7*7}} (expects 49), {{config}}, {{''.__class__.__mro__}}
- Escalation payload:
{{''.__class__.__mro__[1].__subclasses__()}} to enumerate classes
4. OS Command Injection (CWE-78, CWE-77)
High-risk patterns to flag:
import subprocess
filename = request.args.get("file")
subprocess.call(f"cat {filename}", shell=True)
os.system("convert " + user_filename + " output.jpg")
result = os.popen(f"ls {directory}").read()
Safe patterns to enforce:
subprocess.run(["cat", filename], shell=False, capture_output=True)
import re
if not re.match(r'^[a-zA-Z0-9._-]+$', filename):
raise ValueError("Invalid filename")
subprocess.run(["convert", filename, "output.jpg"])
import pathlib
pathlib.Path(path).unlink()
Testing approach:
- Bandit:
bandit -r . -t B602,B603,B605,B606,B607 (subprocess and shell issues)
- Grep:
os.system(, os.popen(, shell=True
- Inject:
; cat /etc/passwd, | id, && whoami, `id`
5. Input Validation Failures (CWE-20)
FastAPI (Pydantic โ preferred):
from pydantic import BaseModel, Field, constr
class UserCreate(BaseModel):
username: constr(min_length=3, max_length=50, pattern=r'^[a-zA-Z0-9_]+$')
age: int = Field(ge=0, le=150)
email: str = Field(max_length=255)
@app.post("/users")
async def create_user(user: UserCreate):
...
body = await request.body()
data = json.loads(body)
Flask (manual validation required):
from marshmallow import Schema, fields, validate
class UserSchema(Schema):
username = fields.Str(required=True, validate=validate.Length(min=3, max=50))
age = fields.Int(validate=validate.Range(min=0, max=150))
name = request.args.get("name")
db.execute(text(f"SELECT * FROM users WHERE name = '{name}'"))
Automated Scanning Pipeline
SAST โ Bandit
pip install bandit
bandit -r ./app -f json -o bandit_report.json
DAST โ OWASP ZAP
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://localhost:8000 \
-r zap_report.html
docker run -t owasp/zap2docker-stable zap-full-scan.py \
-t http://localhost:8000
Combined pipeline (CI/CD):
- name: Run Bandit SAST
run: bandit -r ./app -t B608,B602,B603,B605,B606,B701 --exit-zero -f json -o bandit.json
- name: Upload results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: bandit.json
Key CWEs Reference
| CWE | Name | Primary Risk |
|---|
| CWE-89 | SQL Injection | DB data exfiltration/modification |
| CWE-79 | XSS | Session hijacking, phishing |
| CWE-78 | OS Command Injection | RCE, server takeover |
| CWE-77 | Command Injection | RCE via eval/exec |
| CWE-94 | Code Injection / SSTI | Full Python execution |
| CWE-20 | Improper Input Validation | Gateway to all other injection types |
LLM Prompt Injection (2025 addition)
The 2025 OWASP list notes LLM prompt injection as a related class. For applications using AI APIs:
system_prompt = f"You are a helpful assistant. Context: {user_provided_context}"
messages = [
{"role": "system", "content": STATIC_SYSTEM_PROMPT},
{"role": "user", "content": user_input}
]
Quick Reference: Severity by Attack Type
| Attack | Severity | Ease of Exploit | Typical Impact |
|---|
| SSTI | Critical | Medium | RCE |
| OS Command Injection | Critical | Medium | RCE |
| SQL Injection | High | EasyโMedium | Data breach, auth bypass |
| XSS (Stored) | High | Easy | Session hijack |
| XSS (Reflected) | Medium | Medium | Phishing, credential theft |
| ORM Injection | High | Medium | Data exposure |
| Prompt Injection | MediumโHigh | Easy | Logic bypass, data exfil |
References