Security Scanner
Scan code for common security vulnerabilities. Follow OWASP Top 10 checklist. Provide specific fixes, not vague warnings.
Scan Categories
1. Injection Attacks
SQL Injection
# ❌ CRITICAL: String interpolation in SQL
query = f"SELECT * FROM users WHERE id = {user_id}"
query = "SELECT * FROM users WHERE id = " + user_id
query = "SELECT * FROM users WHERE id = '%s'" % user_id
# ✅ Parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
// ❌ CRITICAL: Template literal in SQL
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ Parameterized
const query = "SELECT * FROM users WHERE id = $1";
const result = await db.query(query, [userId]);
Command Injection
# ❌ CRITICAL: User input in shell command
os.system(f"convert {filename} output.png")
subprocess.call(f"grep {pattern} {file}", shell=True)
# ✅ Use argument list, no shell
subprocess.run(["convert", filename, "output.png"])
subprocess.run(["grep", pattern, file])
NoSQL Injection
// ❌ CRITICAL: User object passed directly to query
db.collection("users").find({ id: req.body.id });
// ✅ Validate and sanitize
const id = sanitize(req.body.id);
db.collection("users").find({ id: new ObjectId(id) });
2. Cross-Site Scripting (XSS)
// ❌ CRITICAL: Unescaped user input in HTML
element.innerHTML = userInput;
document.write(userContent);
dangerouslySetInnerHTML={{ __html: userData }}
// ✅ Escape or use textContent
element.textContent = userInput;
// React: JSX auto-escapes
<div>{userInput}</div>
# ❌ CRITICAL: Unescaped output in template
return f"<div>{user_input}</div>"
# ✅ Use template engine with auto-escaping
return render_template("page.html", content=user_input)
3. Authentication & Authorization
Check for:
- [ ] Missing authentication on endpoints
- [ ] Missing authorization checks (IDOR)
- [ ] JWT stored in localStorage (XSS risk)
- [ ] JWT not validated (signature, expiry, issuer)
- [ ] Password stored in plain text
- [ ] Weak password requirements
- [ ] No rate limiting on login
- [ ] Session tokens in URLs
# ❌ Missing auth check
@app.route("/api/users/<user_id>")
def get_user(user_id):
return User.query.get(user_id) # Anyone can access any user!
# ✅ Check authorization
@app.route("/api/users/<user_id>")
@login_required
def get_user(user_id):
if current_user.id != user_id and not current_user.is_admin:
abort(403)
return User.query.get(user_id)
4. Sensitive Data Exposure
# ❌ CRITICAL: Hardcoded secrets
API_KEY = "sk-abc123def456"
DATABASE_URL = "postgres://admin:password@localhost/db"
SECRET_KEY = "my-super-secret-key"
# ✅ Environment variables
API_KEY = os.environ["API_KEY"]
DATABASE_URL = os.environ["DATABASE_URL"]
SECRET_KEY = os.environ["SECRET_KEY"]
Scan for patterns:
- API keys: sk-[a-zA-Z0-9]{20,}, AIza[a-zA-Z0-9]{35}
- AWS keys: AKIA[a-zA-Z0-9]{16}
- Private keys: -----BEGIN (RSA |EC )?PRIVATE KEY-----
- Tokens: ghp_[a-zA-Z0-9]{36}, ghs_[a-zA-Z0-9]{36}
- Passwords in URLs: https?://[^:]+:[^@]+@
- Generic secrets: password\s*=\s*["'][^"']+["']
5. Insecure Deserialization
# ❌ CRITICAL: Unsafe deserialization
import pickle
data = pickle.loads(user_input) # Remote code execution!
import yaml
data = yaml.load(user_input) # Default loader is unsafe
# ✅ Safe alternatives
import json
data = json.loads(user_input)
import yaml
data = yaml.safe_load(user_input)
6. Security Misconfiguration
Check for:
- [ ] Debug mode enabled in production
- [ ] CORS set to allow all origins (*)
- [ ] Missing security headers (CSP, HSTS, X-Frame-Options)
- [ ] Verbose error messages exposing internals
- [ ] Default credentials not changed
# ❌ Debug in production
app.run(debug=True)
# ❌ Wide-open CORS
CORS(app, origins="*")
# ✅ Environment-aware
app.run(debug=os.environ.get("FLASK_ENV") == "development")
# ✅ Specific origins
CORS(app, origins=["https://myapp.com"])
7. Path Traversal
# ❌ CRITICAL: Unsanitized file path
@app.route("/files/<path:filename>")
def serve_file(filename):
return send_file(f"/uploads/{filename}")
# Attack: GET /files/../../etc/passwd
# ✅ Validate and sanitize
@app.route("/files/<path:filename>")
def serve_file(filename):
safe_path = os.path.normpath(filename)
if safe_path.startswith(".."):
abort(400)
return send_file(os.path.join("/uploads", safe_path))
Severity Levels
🔴 CRITICAL: Remote code execution, SQL injection, auth bypass
→ Fix immediately, do not merge without fix
🟡 HIGH: XSS, CSRF, sensitive data exposure, missing auth
→ Fix before merge
🟠 MEDIUM: Missing input validation, weak crypto, verbose errors
→ Fix in this PR if possible
🟢 LOW: Missing security headers, info disclosure
→ Create follow-up issue
Scan Output Format
When scanning code, output findings in this format:
## Security Scan Results
### 🔴 CRITICAL
1. **[SQL Injection]** src/api/users.ts:42
- Raw string interpolation in SQL query
- Fix: Use parameterized query with $1 placeholder
### 🟡 HIGH
2. **[Missing Auth]** src/api/admin.ts:15
- Admin endpoint has no authentication check
- Fix: Add @require_auth decorator
### Summary
- Critical: 1 | High: 2 | Medium: 3 | Low: 1
- Files scanned: 12
- Recommendation: Do not merge until 1 CRITICAL is resolved
Quick Check Commands
# Scan for hardcoded secrets
grep -rn "password\s*=" --include="*.{py,js,ts,go,java}" .
grep -rn "api_key\s*=" --include="*.{py,js,ts}" .
grep -rn "BEGIN PRIVATE KEY" .
# Check for eval/dangerous functions
grep -rn "eval(" --include="*.{js,ts}" .
grep -rn "pickle.loads" --include="*.py" .
grep -rn "shell=True" --include="*.py" .
grep -rn "innerHTML" --include="*.{js,ts,tsx}" .
# Check for SQL concatenation
grep -rn "f\"SELECT\|f\"INSERT\|f\"UPDATE\|f\"DELETE" --include="*.py" .
grep -rn "\`SELECT\|\`INSERT" --include="*.{js,ts}" .