Security Hunter
Audit code for security vulnerabilities — places where untrusted input flows into sensitive operations without
validation, secrets are embedded in source, defaults are permissive, or auth checks are missing. The goal: every
trust boundary validates its inputs, no secrets live in code, and the principle of least privilege is applied
throughout.
When to Use
- Reviewing code before production deployment
- Auditing trust boundaries (API endpoints, form handlers, webhooks)
- Preparing for a formal security review or pen test
- Onboarding third-party integrations or new external data sources
- Hardening an application after a security incident
Core Principles
Trust boundaries are the perimeter. Every point where external data enters the system — HTTP requests, file
uploads, environment variables, database reads, third-party API responses, CLI arguments — is a trust boundary.
Input must be validated, sanitized, or escaped before it flows into operations that could be exploited.
Secrets never belong in source control. API keys, passwords, tokens, certificates, and connection
strings must come from a secure source — environment variables, secret managers, vault services, or encrypted
config — never from source files, comments, or committed configuration.
Default to deny. Defaults should be restrictive: CORS origins explicit not *, auth required not optional,
permissions minimal not broad. Permissive defaults are the most common class of security misconfiguration.
Defense in depth. No single validation layer is sufficient. Validate at the boundary, escape at the output,
enforce at the data layer. If one layer fails, another should catch the exploit.
Fail closed. When auth/authz checks fail or raise, the result should be denial, not access. Error paths must
not bypass security controls.
What to Hunt
1. Hardcoded Secrets
API keys, passwords, tokens, or connection strings embedded directly in source code.
Signals:
- String literals matching key patterns:
sk_live_, AKIA, ghp_, password=, secret=
- Connection strings with embedded credentials:
postgres://user:pass@host
- Base64-encoded strings in auth headers or config
.env files or secret-bearing config committed to the repo
- Comments containing credentials ("temporary key: xyz123")
- Default values in function signatures:
def connect(password="admin123")
Action: Move to a secure secret source (environment variables, secret manager, vault). Add the pattern to
.gitignore. Rotate any committed secret immediately — it's already compromised if the repo was ever shared.
2. Injection Vulnerabilities
Untrusted input concatenated into strings that are interpreted as code, queries, or commands.
Signals:
- SQL: string concatenation or f-strings in query strings instead of parameterized queries
(
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}"))
- Command injection:
os.system(), subprocess.call(shell=True), subprocess.Popen(shell=True) with user-controlled
arguments
- Path traversal: user input in
open(), pathlib.Path(), or URL construction without sanitization
- Template injection: user input rendered in Jinja2/Mako templates with
| safe or autoescape disabled
- Regex: user input passed directly to
re.compile() (ReDoS risk)
eval(), exec(), compile() with dynamic input
pickle.loads() / pickle.load() on untrusted data (arbitrary code execution)
yaml.load() without Loader=SafeLoader (arbitrary code execution)
Action: Use parameterized queries, allowlists, path canonicalization, template auto-escaping, or regex escaping.
Never concatenate untrusted input into interpreted strings.
3. Missing Input Validation at Trust Boundaries
Endpoints, handlers, or integration points that accept external data without schema validation or sanitization.
Signals:
- API handlers that access
request.json, request.args, request.form without validation
(no Pydantic, marshmallow, attrs validators, etc.)
- File upload handlers without type/size/content validation
- Webhook handlers without signature verification
- URL parameters used directly in business logic without parsing/validation
- Form inputs passed to database operations without sanitization
- CLI arguments from
argparse/click used without additional validation for security-sensitive operations
Action: Add schema validation at every trust boundary. Validate type, format, range, and length. Reject invalid
input explicitly — don't coerce or default.
4. Insecure Defaults and Configuration
Permissive defaults that weaken security posture when not explicitly overridden.
Signals:
- CORS:
Access-Control-Allow-Origin: * or credentials: True with wildcard origin
- CSRF: protection disabled or missing on state-changing endpoints
- Cookie: missing
Secure, HttpOnly, or SameSite attributes
- TLS: certificate validation disabled (
verify=False in requests, ssl._create_unverified_context())
- Rate limiting: absent on auth endpoints or public APIs
- Debug mode:
DEBUG=True or app.run(debug=True) in production config
- Django:
ALLOWED_HOSTS = ['*'], SECRET_KEY hardcoded, DEBUG = True
- Flask:
app.secret_key hardcoded in source
Action: Restrict defaults. Require explicit opt-in for permissive settings with justification.
5. Authentication and Authorization Gaps
Missing or inconsistent auth checks that allow unauthorized access or privilege escalation.
Signals:
- Routes/endpoints without auth decorators where peer routes have them
- Authorization checks that compare user IDs with string equality on user-controlled values
- Role checks using client-provided role claims without server-side verification
- Admin functionality accessible by changing a URL parameter or request body field
- Session tokens without expiry, rotation, or revocation
- Password handling: plain-text storage, weak hashing (MD5, SHA1), missing salting
(use
bcrypt, argon2, or passlib instead)
Action: Ensure every endpoint has explicit auth/authz. Check authorization server-side against the session, not
client-provided claims. Use bcrypt/argon2/passlib for password hashing.
6. Sensitive Data Exposure
Personal data, secrets, or internal details leaked through logs, errors, responses, or storage.
Signals:
- Logging of request bodies, auth headers, or user PII (
logging.info(f"User data: {request.json}"))
- Error responses that include stack traces, internal paths, or database details (debug mode in production)
- API responses that return more fields than the client needs (no field filtering)
- Sensitive data stored in plaintext files or unencrypted databases
- PII in URLs (email, SSN, tokens in query parameters — logged by default in most servers)
- Tracebacks with local variables exposed to end users
Action: Strip sensitive fields from logs and error responses. Filter API responses to only required fields. Use
structured logging with field-level redaction.
7. Dependency and Supply-Chain Risks
Package dependencies with known vulnerabilities, unvetted install scripts, or lockfile integrity issues.
Signals:
requirements.txt dependencies without pinned versions (requests instead of requests==2.31.0)
- Lockfile (
poetry.lock, Pipfile.lock, uv.lock) not committed to the repo
- Packages with
setup.py that execute arbitrary code during install
- Dependencies with known CVEs (check via
pip-audit or safety)
- Importing from URLs or running
pip install from untrusted sources at runtime
Action: Commit the lockfile. Pin dependency versions. Run pip-audit (or safety check) and address
critical/high findings. Review setup scripts in new dependencies before adding them.
8. Unsafe Code Patterns
Language-level patterns that create exploitable conditions.
Signals:
pickle.loads() / pickle.load() on untrusted data (arbitrary code execution)
yaml.load(data) without Loader=yaml.SafeLoader (arbitrary code execution)
eval(), exec(), compile() with user-controlled input
__import__() or importlib.import_module() with user-controlled arguments
subprocess with shell=True and string arguments derived from user input
tempfile.mktemp() instead of tempfile.mkstemp() (race condition)
hashlib.md5() / hashlib.sha1() for security-sensitive hashing (use SHA-256+ or bcrypt)
random.random() for security tokens (use secrets module instead)
- XML parsing with
xml.etree.ElementTree without defusing (XXE attacks) — use defusedxml
marshal.loads() on untrusted data
Action: Use yaml.safe_load() instead of yaml.load(), secrets.token_urlsafe() instead of random, defusedxml
for XML parsing, subprocess.run() with list arguments instead of shell=True. Never deserialize untrusted data with
pickle.
9. Framework-Specific Security Misconfigurations
Security patterns specific to popular Python web frameworks that go beyond general best practices.
Signals:
- FastAPI:
CORSMiddleware with allow_origins=["*"] combined with allow_credentials=True (credential leakage),
routes missing Depends(get_current_user) where peer routes require auth, Pydantic Settings with hardcoded
secret_key or jwt_secret default values, uvicorn.run(host="0.0.0.0") without TLS in production config
- Django:
ALLOWED_HOSTS = ['*'] in production settings, SECRET_KEY hardcoded in settings.py (not from env),
DEBUG = True in production settings module, @csrf_exempt on state-changing views without justification,
SECURE_SSL_REDIRECT = False and SESSION_COOKIE_SECURE = False in production,
AUTH_PASSWORD_VALIDATORS = [] (empty password validation)
- Flask:
app.secret_key hardcoded in source, app.run(debug=True) in production entrypoint,
no SESSION_COOKIE_SECURE or SESSION_COOKIE_HTTPONLY configured, send_from_directory() or send_file()
with user-controlled paths without sanitization
Action: Move secrets to environment variables or secret managers. Enable framework security features explicitly.
Audit route-level auth coverage against a peer-comparison table. Ensure production settings modules disable debug
and enforce HTTPS.
Audit Workflow
Phase 1: Map Trust Boundaries
- Resolve audit surface. The prompt may specify the scope as:
- Diff: files changed on the current branch vs base (
main/master)
- Path: specific files, folders, or layers
- Codebase: the entire project
If unspecified, default to codebase. For diff mode, resolve the file list:
BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.
- Identify trust boundaries: API route handlers, webhooks, file upload endpoints, third-party integration points,
CLI argument parsers, config loaders.
- Identify auth/authz middleware and where it's applied.
Phase 2: Scan for Security Signals
EXCLUDE='--glob !**/venv/** --glob !**/.venv/** --glob !**/dist/** --glob !**/*_test.py --glob !**/test_*.py'
# Hardcoded secrets (common patterns)
rg --pcre2 '(sk_live_|AKIA|ghp_|password\s*=\s*["\x27]|secret\s*=\s*["\x27])' $EXCLUDE
rg --pcre2 '(postgres|mysql|mongodb)://\w+:\w+@' $EXCLUDE
# Injection: eval, exec, compile, pickle, yaml.load
rg '\beval\s*\(|\bexec\s*\(|\bcompile\s*\(' --type py $EXCLUDE
rg 'pickle\.(loads?|load)\s*\(' --type py $EXCLUDE
rg 'yaml\.load\s*\(' --type py $EXCLUDE
# Shell injection: subprocess with shell=True, os.system
rg 'shell\s*=\s*True|os\.system\s*\(' --type py $EXCLUDE
# SQL injection: f-strings or .format() in queries
rg --pcre2 '(execute|query)\s*\(\s*f["\x27]' --type py $EXCLUDE
rg --pcre2 '(execute|query)\s*\([^)]*\.format\(' --type py $EXCLUDE
# Insecure config
rg --pcre2 'verify\s*=\s*False|DEBUG\s*=\s*True|ALLOWED_HOSTS\s*=\s*\[\s*["\x27]\*' $EXCLUDE
# Missing auth (route definitions — project-specific, adapt pattern)
rg '@(app|router)\.(get|post|put|patch|delete)\s*\(' --type py $EXCLUDE
# Logging of sensitive data
rg --pcre2 'log(ger)?\.(info|debug|warning).*\b(password|token|secret|authorization|cookie)\b' -i --type py $EXCLUDE
# Insecure random
rg 'random\.(random|randint|choice|sample)\s*\(' --type py $EXCLUDE
# XML parsing without defusedxml
rg 'xml\.etree|xml\.dom|xml\.sax' --type py $EXCLUDE
# tempfile.mktemp (race condition)
rg 'mktemp\s*\(' --type py $EXCLUDE
Phase 3: Trace Data Flows
For each trust boundary found in Phase 1:
- Does untrusted input pass through validation before reaching sensitive operations?
- Are query parameters, body fields, and headers validated for type, format, and range?
- Could a malicious input reach
eval, exec, a database query, subprocess, or pickle.loads()?
Phase 4: Evaluate Auth Coverage
- List all routes/endpoints. For each: is auth middleware applied?
- Identify authorization logic. Is it server-side? Does it rely on client claims?
- Check session management: expiry, rotation, revocation.
Phase 5: Produce Report
Output Format
Save as YYYY-MM-DD-security-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs
folder exists).
# Security Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
## Trust Boundary Map
| # | Boundary | Location | Validation | Auth |
| - | -------- | -------- | ---------- | ---- |
| 1 | POST /api/users | file:line | Pydantic model | JWT middleware |
| 2 | Webhook /hooks/stripe | file:line | None | None |
## Findings
### Hardcoded Secrets
| # | Location | Pattern | Severity | Action |
| - | -------- | ------- | -------- | ------ |
| 1 | file:line | `sk_live_...` Stripe key | Critical | Move to env, rotate immediately |
### Injection Risks
| # | Location | Type | Untrusted Input | Action |
| - | -------- | ---- | --------------- | ------ |
| 1 | file:line | SQL injection | f-string in `cursor.execute()` | Use parameterized query |
| 2 | file:line | Pickle deserialization | Untrusted bytes in `pickle.loads()` | Use JSON or msgpack |
### Missing Input Validation
| # | Location | Boundary | Input | Action |
| - | -------- | -------- | ----- | ------ |
| 1 | file:line | POST /api/orders | `request.json` used without validation | Add Pydantic model |
### Insecure Defaults
| # | Location | Setting | Current | Action |
| - | -------- | ------- | ------- | ------ |
| 1 | file:line | TLS verification | `verify=False` | Enable verification |
### Auth/Authz Gaps
| # | Location | Endpoint | Issue | Action |
| - | -------- | -------- | ----- | ------ |
| 1 | file:line | GET /admin/users | No auth decorator | Add admin auth |
### Sensitive Data Exposure
| # | Location | Data | Channel | Action |
| - | -------- | ---- | ------- | ------ |
| 1 | file:line | Auth token | logging.info() | Remove from logs |
### Unsafe Patterns
| # | Location | Pattern | Risk | Action |
| - | -------- | ------- | ---- | ------ |
| 1 | file:line | `yaml.load(data)` | Arbitrary code execution | Use `yaml.safe_load()` |
### Dependency / Supply-Chain Risks
| # | Location | Issue | Severity | Action |
| - | -------- | ----- | -------- | ------ |
| 1 | requirements.txt | No lockfile committed | High | Commit lockfile |
## Recommendations (Priority Order)
1. **Critical**: {hardcoded secrets to rotate, injection vulnerabilities, pickle on untrusted data, auth bypasses}
2. **Must-fix**: {missing input validation, insecure defaults, auth gaps, dependency CVEs}
3. **Should-fix**: {sensitive data exposure, unsafe patterns, supply-chain hygiene}
Operating Constraints
- No code edits. This skill produces an audit report only. Implementation is a separate step.
- No empty sections. Include only categories with findings. Omit a heading, table, or list entirely when it would contain zero items — do not include empty tables, placeholder subsections, or negative statements like "no dead exports", "none found", or "no issues".
- Scope: security vulnerabilities only. Do not flag type invariants (→ invariant-hunter-py), type design
(→ type-hunter-py), structural complexity (→ simplicity-hunter-py), module boundary issues (→ boundary-hunter-py),
class/interface design (→ solid-hunter-py), missing documentation (→ doc-hunter-py), test quality (→ test-hunter-py),
or cosmetic style (→ slop-hunter-py). If a finding doesn't answer "could this be exploited?", it doesn't belong here.
- Evidence required. Every finding must cite
file/path.py:line with the exact code.
- Severity matters. Use Critical / Must-fix / Should-fix. A hardcoded production secret is not the same severity
as a missing
SameSite cookie attribute. Prioritize accordingly.
- Context over pattern-matching. A
re.compile() with a hardcoded pattern is fine. A re.compile(user_input) is
a ReDoS risk. Grep finds both — judgment separates them.
- No false confidence. This audit catches common patterns, not all vulnerabilities. It is not a substitute for
professional penetration testing or automated security scanning.
1---2name: security-hunter-py3description: Audit Python code for security vulnerabilities — hardcoded secrets, injection risks, missing input validation at trust boundaries, insecure defaults, auth gaps, sensitive data exposure, and unsafe patterns like eval, pickle, or shell injection. Use when: reviewing Python code before deployment, auditing trust boundaries, preparing for a security review, onboarding third-party integrations, or hardening an application. Reports omit empty sections — no placeholder headings, empty tables, or negative statements like "no issues found".4---56# Security Hunter78Audit code for **security vulnerabilities** — places where untrusted input flows into sensitive operations without9validation, secrets are embedded in source, defaults are permissive, or auth checks are missing. The goal: **every10trust boundary validates its inputs, no secrets live in code, and the principle of least privilege is applied11throughout.**1213## When to Use1415- Reviewing code before production deployment16- Auditing trust boundaries (API endpoints, form handlers, webhooks)17- Preparing for a formal security review or pen test18- Onboarding third-party integrations or new external data sources19- Hardening an application after a security incident2021## Core Principles22231. **Trust boundaries are the perimeter.** Every point where external data enters the system — HTTP requests, file24 uploads, environment variables, database reads, third-party API responses, CLI arguments — is a trust boundary.25 Input must be validated, sanitized, or escaped before it flows into operations that could be exploited.26272. **Secrets never belong in source control.** API keys, passwords, tokens, certificates, and connection28 strings must come from a secure source — environment variables, secret managers, vault services, or encrypted29 config — never from source files, comments, or committed configuration.30313. **Default to deny.** Defaults should be restrictive: CORS origins explicit not `*`, auth required not optional,32 permissions minimal not broad. Permissive defaults are the most common class of security misconfiguration.33344. **Defense in depth.** No single validation layer is sufficient. Validate at the boundary, escape at the output,35 enforce at the data layer. If one layer fails, another should catch the exploit.36375. **Fail closed.** When auth/authz checks fail or raise, the result should be denial, not access. Error paths must38 not bypass security controls.3940## What to Hunt4142### 1. Hardcoded Secrets4344API keys, passwords, tokens, or connection strings embedded directly in source code.4546**Signals:**4748- String literals matching key patterns: `sk_live_`, `AKIA`, `ghp_`, `password=`, `secret=`49- Connection strings with embedded credentials: `postgres://user:pass@host`50- Base64-encoded strings in auth headers or config51- `.env` files or secret-bearing config committed to the repo52- Comments containing credentials ("temporary key: xyz123")53- Default values in function signatures: `def connect(password="admin123")`5455**Action:** Move to a secure secret source (environment variables, secret manager, vault). Add the pattern to56`.gitignore`. Rotate any committed secret immediately — it's already compromised if the repo was ever shared.5758### 2. Injection Vulnerabilities5960Untrusted input concatenated into strings that are interpreted as code, queries, or commands.6162**Signals:**6364- SQL: string concatenation or f-strings in query strings instead of parameterized queries65 (`cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")`)66- Command injection: `os.system()`, `subprocess.call(shell=True)`, `subprocess.Popen(shell=True)` with user-controlled67 arguments68- Path traversal: user input in `open()`, `pathlib.Path()`, or URL construction without sanitization69- Template injection: user input rendered in Jinja2/Mako templates with `| safe` or autoescape disabled70- Regex: user input passed directly to `re.compile()` (ReDoS risk)71- `eval()`, `exec()`, `compile()` with dynamic input72- `pickle.loads()` / `pickle.load()` on untrusted data (arbitrary code execution)73- `yaml.load()` without `Loader=SafeLoader` (arbitrary code execution)7475**Action:** Use parameterized queries, allowlists, path canonicalization, template auto-escaping, or regex escaping.76Never concatenate untrusted input into interpreted strings.7778### 3. Missing Input Validation at Trust Boundaries7980Endpoints, handlers, or integration points that accept external data without schema validation or sanitization.8182**Signals:**8384- API handlers that access `request.json`, `request.args`, `request.form` without validation85 (no Pydantic, marshmallow, attrs validators, etc.)86- File upload handlers without type/size/content validation87- Webhook handlers without signature verification88- URL parameters used directly in business logic without parsing/validation89- Form inputs passed to database operations without sanitization90- CLI arguments from `argparse`/`click` used without additional validation for security-sensitive operations9192**Action:** Add schema validation at every trust boundary. Validate type, format, range, and length. Reject invalid93input explicitly — don't coerce or default.9495### 4. Insecure Defaults and Configuration9697Permissive defaults that weaken security posture when not explicitly overridden.9899**Signals:**100101- CORS: `Access-Control-Allow-Origin: *` or `credentials: True` with wildcard origin102- CSRF: protection disabled or missing on state-changing endpoints103- Cookie: missing `Secure`, `HttpOnly`, or `SameSite` attributes104- TLS: certificate validation disabled (`verify=False` in requests, `ssl._create_unverified_context()`)105- Rate limiting: absent on auth endpoints or public APIs106- Debug mode: `DEBUG=True` or `app.run(debug=True)` in production config107- Django: `ALLOWED_HOSTS = ['*']`, `SECRET_KEY` hardcoded, `DEBUG = True`108- Flask: `app.secret_key` hardcoded in source109110**Action:** Restrict defaults. Require explicit opt-in for permissive settings with justification.111112### 5. Authentication and Authorization Gaps113114Missing or inconsistent auth checks that allow unauthorized access or privilege escalation.115116**Signals:**117118- Routes/endpoints without auth decorators where peer routes have them119- Authorization checks that compare user IDs with string equality on user-controlled values120- Role checks using client-provided role claims without server-side verification121- Admin functionality accessible by changing a URL parameter or request body field122- Session tokens without expiry, rotation, or revocation123- Password handling: plain-text storage, weak hashing (MD5, SHA1), missing salting124 (use `bcrypt`, `argon2`, or `passlib` instead)125126**Action:** Ensure every endpoint has explicit auth/authz. Check authorization server-side against the session, not127client-provided claims. Use bcrypt/argon2/passlib for password hashing.128129### 6. Sensitive Data Exposure130131Personal data, secrets, or internal details leaked through logs, errors, responses, or storage.132133**Signals:**134135- Logging of request bodies, auth headers, or user PII (`logging.info(f"User data: {request.json}")`)136- Error responses that include stack traces, internal paths, or database details (debug mode in production)137- API responses that return more fields than the client needs (no field filtering)138- Sensitive data stored in plaintext files or unencrypted databases139- PII in URLs (email, SSN, tokens in query parameters — logged by default in most servers)140- Tracebacks with local variables exposed to end users141142**Action:** Strip sensitive fields from logs and error responses. Filter API responses to only required fields. Use143structured logging with field-level redaction.144145### 7. Dependency and Supply-Chain Risks146147Package dependencies with known vulnerabilities, unvetted install scripts, or lockfile integrity issues.148149**Signals:**150151- `requirements.txt` dependencies without pinned versions (`requests` instead of `requests==2.31.0`)152- Lockfile (`poetry.lock`, `Pipfile.lock`, `uv.lock`) not committed to the repo153- Packages with `setup.py` that execute arbitrary code during install154- Dependencies with known CVEs (check via `pip-audit` or `safety`)155- Importing from URLs or running `pip install` from untrusted sources at runtime156157**Action:** Commit the lockfile. Pin dependency versions. Run `pip-audit` (or `safety check`) and address158critical/high findings. Review setup scripts in new dependencies before adding them.159160### 8. Unsafe Code Patterns161162Language-level patterns that create exploitable conditions.163164**Signals:**165166- `pickle.loads()` / `pickle.load()` on untrusted data (arbitrary code execution)167- `yaml.load(data)` without `Loader=yaml.SafeLoader` (arbitrary code execution)168- `eval()`, `exec()`, `compile()` with user-controlled input169- `__import__()` or `importlib.import_module()` with user-controlled arguments170- `subprocess` with `shell=True` and string arguments derived from user input171- `tempfile.mktemp()` instead of `tempfile.mkstemp()` (race condition)172- `hashlib.md5()` / `hashlib.sha1()` for security-sensitive hashing (use SHA-256+ or bcrypt)173- `random.random()` for security tokens (use `secrets` module instead)174- XML parsing with `xml.etree.ElementTree` without defusing (XXE attacks) — use `defusedxml`175- `marshal.loads()` on untrusted data176177**Action:** Use `yaml.safe_load()` instead of `yaml.load()`, `secrets.token_urlsafe()` instead of `random`, `defusedxml`178for XML parsing, `subprocess.run()` with list arguments instead of `shell=True`. Never deserialize untrusted data with179`pickle`.180181### 9. Framework-Specific Security Misconfigurations182183Security patterns specific to popular Python web frameworks that go beyond general best practices.184185**Signals:**186187- **FastAPI**: `CORSMiddleware` with `allow_origins=["*"]` combined with `allow_credentials=True` (credential leakage),188 routes missing `Depends(get_current_user)` where peer routes require auth, Pydantic `Settings` with hardcoded189 `secret_key` or `jwt_secret` default values, `uvicorn.run(host="0.0.0.0")` without TLS in production config190- **Django**: `ALLOWED_HOSTS = ['*']` in production settings, `SECRET_KEY` hardcoded in `settings.py` (not from env),191 `DEBUG = True` in production settings module, `@csrf_exempt` on state-changing views without justification,192 `SECURE_SSL_REDIRECT = False` and `SESSION_COOKIE_SECURE = False` in production,193 `AUTH_PASSWORD_VALIDATORS = []` (empty password validation)194- **Flask**: `app.secret_key` hardcoded in source, `app.run(debug=True)` in production entrypoint,195 no `SESSION_COOKIE_SECURE` or `SESSION_COOKIE_HTTPONLY` configured, `send_from_directory()` or `send_file()`196 with user-controlled paths without sanitization197198**Action:** Move secrets to environment variables or secret managers. Enable framework security features explicitly.199Audit route-level auth coverage against a peer-comparison table. Ensure production settings modules disable debug200and enforce HTTPS.201202## Audit Workflow203204### Phase 1: Map Trust Boundaries2052061. **Resolve audit surface.** The prompt may specify the scope as:207 - **Diff**: files changed on the current branch vs base (`main`/`master`)208 - **Path**: specific files, folders, or layers209 - **Codebase**: the entire project210 If unspecified, default to **codebase**. For diff mode, resolve the file list:211 ```bash212 BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)213 SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)214 ```215 Constrain all subsequent scans to the resolved surface.2162. Identify trust boundaries: API route handlers, webhooks, file upload endpoints, third-party integration points,217 CLI argument parsers, config loaders.2183. Identify auth/authz middleware and where it's applied.219220### Phase 2: Scan for Security Signals221222```bash223EXCLUDE='--glob !**/venv/** --glob !**/.venv/** --glob !**/dist/** --glob !**/*_test.py --glob !**/test_*.py'224225# Hardcoded secrets (common patterns)226rg --pcre2 '(sk_live_|AKIA|ghp_|password\s*=\s*["\x27]|secret\s*=\s*["\x27])' $EXCLUDE227rg --pcre2 '(postgres|mysql|mongodb)://\w+:\w+@' $EXCLUDE228229# Injection: eval, exec, compile, pickle, yaml.load230rg '\beval\s*\(|\bexec\s*\(|\bcompile\s*\(' --type py $EXCLUDE231rg 'pickle\.(loads?|load)\s*\(' --type py $EXCLUDE232rg 'yaml\.load\s*\(' --type py $EXCLUDE233234# Shell injection: subprocess with shell=True, os.system235rg 'shell\s*=\s*True|os\.system\s*\(' --type py $EXCLUDE236237# SQL injection: f-strings or .format() in queries238rg --pcre2 '(execute|query)\s*\(\s*f["\x27]' --type py $EXCLUDE239rg --pcre2 '(execute|query)\s*\([^)]*\.format\(' --type py $EXCLUDE240241# Insecure config242rg --pcre2 'verify\s*=\s*False|DEBUG\s*=\s*True|ALLOWED_HOSTS\s*=\s*\[\s*["\x27]\*' $EXCLUDE243244# Missing auth (route definitions — project-specific, adapt pattern)245rg '@(app|router)\.(get|post|put|patch|delete)\s*\(' --type py $EXCLUDE246247# Logging of sensitive data248rg --pcre2 'log(ger)?\.(info|debug|warning).*\b(password|token|secret|authorization|cookie)\b' -i --type py $EXCLUDE249250# Insecure random251rg 'random\.(random|randint|choice|sample)\s*\(' --type py $EXCLUDE252253# XML parsing without defusedxml254rg 'xml\.etree|xml\.dom|xml\.sax' --type py $EXCLUDE255256# tempfile.mktemp (race condition)257rg 'mktemp\s*\(' --type py $EXCLUDE258```259260### Phase 3: Trace Data Flows261262For each trust boundary found in Phase 1:2632641. Does untrusted input pass through validation before reaching sensitive operations?2652. Are query parameters, body fields, and headers validated for type, format, and range?2663. Could a malicious input reach `eval`, `exec`, a database query, `subprocess`, or `pickle.loads()`?267268### Phase 4: Evaluate Auth Coverage2692701. List all routes/endpoints. For each: is auth middleware applied?2712. Identify authorization logic. Is it server-side? Does it rely on client claims?2723. Check session management: expiry, rotation, revocation.273274### Phase 5: Produce Report275276## Output Format277278Save as `YYYY-MM-DD-security-hunter-audit-{$LLM-name}.md` in the project's docs folder (or project root if no docs279folder exists).280281```md282# Security Hunter Audit — {date}283284## Scope285286- Surface: {diff / path / codebase}287- Files: {count or list}288- Exclusions: {list}289290## Trust Boundary Map291292| # | Boundary | Location | Validation | Auth |293| - | -------- | -------- | ---------- | ---- |294| 1 | POST /api/users | file:line | Pydantic model | JWT middleware |295| 2 | Webhook /hooks/stripe | file:line | None | None |296297## Findings298299### Hardcoded Secrets300301| # | Location | Pattern | Severity | Action |302| - | -------- | ------- | -------- | ------ |303| 1 | file:line | `sk_live_...` Stripe key | Critical | Move to env, rotate immediately |304305### Injection Risks306307| # | Location | Type | Untrusted Input | Action |308| - | -------- | ---- | --------------- | ------ |309| 1 | file:line | SQL injection | f-string in `cursor.execute()` | Use parameterized query |310| 2 | file:line | Pickle deserialization | Untrusted bytes in `pickle.loads()` | Use JSON or msgpack |311312### Missing Input Validation313314| # | Location | Boundary | Input | Action |315| - | -------- | -------- | ----- | ------ |316| 1 | file:line | POST /api/orders | `request.json` used without validation | Add Pydantic model |317318### Insecure Defaults319320| # | Location | Setting | Current | Action |321| - | -------- | ------- | ------- | ------ |322| 1 | file:line | TLS verification | `verify=False` | Enable verification |323324### Auth/Authz Gaps325326| # | Location | Endpoint | Issue | Action |327| - | -------- | -------- | ----- | ------ |328| 1 | file:line | GET /admin/users | No auth decorator | Add admin auth |329330### Sensitive Data Exposure331332| # | Location | Data | Channel | Action |333| - | -------- | ---- | ------- | ------ |334| 1 | file:line | Auth token | logging.info() | Remove from logs |335336### Unsafe Patterns337338| # | Location | Pattern | Risk | Action |339| - | -------- | ------- | ---- | ------ |340| 1 | file:line | `yaml.load(data)` | Arbitrary code execution | Use `yaml.safe_load()` |341342### Dependency / Supply-Chain Risks343344| # | Location | Issue | Severity | Action |345| - | -------- | ----- | -------- | ------ |346| 1 | requirements.txt | No lockfile committed | High | Commit lockfile |347348## Recommendations (Priority Order)3493501. **Critical**: {hardcoded secrets to rotate, injection vulnerabilities, pickle on untrusted data, auth bypasses}3512. **Must-fix**: {missing input validation, insecure defaults, auth gaps, dependency CVEs}3523. **Should-fix**: {sensitive data exposure, unsafe patterns, supply-chain hygiene}353```354355## Operating Constraints356357- **No code edits.** This skill produces an audit report only. Implementation is a separate step.358- **No empty sections.** Include only categories with findings. Omit a heading, table, or list entirely when it would contain zero items — do not include empty tables, placeholder subsections, or negative statements like "no dead exports", "none found", or "no issues".359- **Scope: security vulnerabilities only.** Do not flag type invariants (→ invariant-hunter-py), type design360 (→ type-hunter-py), structural complexity (→ simplicity-hunter-py), module boundary issues (→ boundary-hunter-py),361 class/interface design (→ solid-hunter-py), missing documentation (→ doc-hunter-py), test quality (→ test-hunter-py),362 or cosmetic style (→ slop-hunter-py). If a finding doesn't answer "could this be exploited?", it doesn't belong here.363- **Evidence required.** Every finding must cite `file/path.py:line` with the exact code.364- **Severity matters.** Use Critical / Must-fix / Should-fix. A hardcoded production secret is not the same severity365 as a missing `SameSite` cookie attribute. Prioritize accordingly.366- **Context over pattern-matching.** A `re.compile()` with a hardcoded pattern is fine. A `re.compile(user_input)` is367 a ReDoS risk. Grep finds both — judgment separates them.368- **No false confidence.** This audit catches common patterns, not all vulnerabilities. It is not a substitute for369 professional penetration testing or automated security scanning.