| name | security-auditor |
| description | Expert security audit and penetration testing skill for evaluating frontend and backend code repositories. Use this skill whenever a user wants to audit code for security vulnerabilities, find exposed secrets or API keys, check for OWASP Top 10 issues, review authentication/authorization flaws, identify injection vulnerabilities, assess dependency risks, or generate a security report. Trigger on any mention of "security audit", "pen test", "penetration test", "security review", "find vulnerabilities", "exposed keys", "hardcoded secrets", "security scan", "code security", "OWASP check", "security flaws", or whenever the user shares code and asks if it is secure. Also trigger when users ask to "check my repo", "review my code for security", or "find security issues" even without explicitly naming it a security audit.
|
Security Auditor — Code Repository Penetration Testing Skill
Overview
This skill performs systematic security audits of frontend and backend code repositories,
mimicking the methodology of a professional penetration tester and AppSec engineer. It covers:
- Secrets detection — hardcoded API keys, tokens, passwords, credentials
- OWASP Top 10 (2025) — the most critical web application security risks
- Frontend vulnerabilities — XSS, CSRF, insecure storage, CSP gaps
- Backend vulnerabilities — injection, broken auth, access control, cryptography flaws
- Dependency auditing — known CVEs in third-party libraries
- Infrastructure and config — exposed config files, insecure headers, misconfigurations
- Severity-rated findings with remediation guidance and a structured final report
Standards referenced: OWASP Top 10 (2025), CWE Top 25, NIST SP 800-92.
Audit Methodology
Follow this 6-phase approach. Adapt depth based on available code.
- Reconnaissance — understand the stack, entry points, data flows
- Secrets Detection — scan for leaked credentials FIRST
- Frontend Audit — client-side vulnerabilities
- Backend Audit — server-side vulnerabilities
- Dependency and Supply Chain — third-party risks
- Report Generation — severity-rated findings with remediation steps
When given a code repository or files, scan ALL files including tests, docs, and configs.
Note the tech stack early. Flag both confirmed vulnerabilities and suspicious patterns.
Phase 1: Reconnaissance and Repository Triage
Map the attack surface before diving into vulnerabilities.
Identify the tech stack:
- Frontend: React/Vue/Angular/plain JS, which bundler
- Backend: Node/Python/Java/Go/PHP/Ruby and framework
- Database: SQL (Postgres/MySQL) or NoSQL (MongoDB/Redis)
- Auth mechanism: JWT, OAuth, sessions, or custom
- Infrastructure: Docker, Kubernetes, cloud provider
High-value targets to locate immediately:
- .env, .env.local, .env.production, .env.* files
- config.js, config.ts, settings.py, application.properties
- docker-compose.yml and docker-compose.override.yml
- .aws/credentials, id_rsa, *.pem, *.key files
- terraform.tfvars, *.tfstate files
- .github/workflows/ and .gitlab-ci.yml (CI/CD configs)
- package.json, requirements.txt, Gemfile, pom.xml (dependency files)
Pre-audit questions to answer:
- Where does user input enter the system?
- What authenticates and authorizes users?
- Where is sensitive data stored and transmitted?
- What external services or APIs are integrated?
- Is there file upload functionality?
- Are there admin-only endpoints?
Phase 2: Secrets and Exposed Credentials Detection
This is always the FIRST scan. Exposed secrets are highest-impact, easiest-to-exploit.
Secret Patterns to Find
Scan ALL files — including comments, tests, and documentation:
| Secret Type | Detection Pattern | Notes |
|---|
| AWS Access Key | AKIA[0-9A-Z]{16} | e.g., AKIAIOSFODNN7EXAMPLE |
| AWS Secret Key | 40-char alphanum after aws_secret | wJalrXUtnFEMI/K7MDENG/... |
| GitHub Token | ghp_[a-zA-Z0-9]{36} or github_pat_ | |
| Google API Key | AIza[0-9A-Za-z_-]{35} | AIzaSyDdI0hCZtE6... |
| Stripe Live Key | sk_live_[0-9a-zA-Z]{24,} | pk_live_ is also sensitive |
| Slack Token | xox[baprs]-[0-9a-zA-Z]{10,} | |
| Twilio | SK[0-9a-fA-F]{32} | |
| JWT Token | eyJ[A-Za-z0-9_-]+.eyJ[A-Za-z0-9_-]+. | Decode and check alg/exp |
| Private Key | -----BEGIN (RSA | EC |
| Database URL | postgres://user:pass@host or mysql://user:pass@host | |
| Generic password | password = "somevalue" (non-placeholder) | |
| Generic API key | api_key = "somevalue" (non-placeholder) | |
Also look for:
- Tokens hardcoded in Authorization headers inside source code
- Credentials embedded in URLs: https://user:password@host
- Secrets inside comments: // TODO: remove this key: sk_live_...
- Base64-encoded secrets near keywords "key", "secret", "token"
- .env files accidentally committed (check .gitignore covers them)
- SSH keys or PEM certificates in the repository
File and Location Scanning Commands
find . -name "*.env" -o -name ".env*" | grep -v node_modules | grep -v ".env.example"
grep -rn "password\s*=" . --include="*.js" --include="*.ts" --include="*.py"
grep -rn "api_key\s*=" . | grep -v "YOUR_API_KEY\|PLACEHOLDER\|EXAMPLE"
grep -rn "secret\s*=" . | grep -v test
grep -rn "mongodb+srv://\|postgresql://\|mysql://" .
grep -rn "AKIA[0-9A-Z]" .
grep -rn "BEGIN PRIVATE KEY\|BEGIN RSA PRIVATE" .
find . -name "*.pem" -o -name "id_rsa" | grep -v node_modules
cat .gitignore | grep -E "\.env|secret|credential"
Critical: If .env is NOT in .gitignore, flag as HIGH — it is a data leak waiting to happen.
Git History Audit
Secrets deleted in a later commit are still exposed in git history:
git log --all --full-history -p | grep -E "(password|api_key|secret|token)\s*="
git log --all --diff-filter=D --name-only --pretty=format: | grep -i "secret\|key\|env\|config"
git log --all --oneline | grep -i "remove\|delete\|secret\|key\|password"
IMPORTANT: A secret found only in git history is still CRITICAL if the key has not been
rotated. Removing a secret from code does NOT revoke access to the service it unlocks.
Phase 3: Frontend Security Audit
XSS — Cross-Site Scripting (CWE-79)
XSS is the most prevalent frontend vulnerability with 30,000+ CVEs.
Flag these as CRITICAL or HIGH:
element.innerHTML = userInput;
document.write(userInput);
eval(userInput);
new Function(userInput)();
setTimeout(userInput, 100);
<div dangerouslySetInnerHTML={{ __html: userInput }} />
$(element).html(userInput);
$(element).append(userInput);
DOM-based XSS sources — dangerous if written unsanitized to DOM:
- location.hash, location.search, location.href
- document.referrer, window.name
- postMessage handlers — verify event.origin is validated before use
Safe patterns to confirm: textContent/innerText instead of innerHTML, DOMPurify
sanitization before any HTML insertion, React JSX default rendering (safe by default).
Security Headers and CSP
Check HTTP response headers. Flag missing or weak values:
| Header | Required Value | Risk if Missing/Weak |
|---|
| Content-Security-Policy | Restrictive policy | HIGH — XSS amplification |
| X-Frame-Options | DENY or SAMEORIGIN | HIGH — clickjacking |
| X-Content-Type-Options | nosniff | MEDIUM — MIME sniffing |
| Strict-Transport-Security | max-age=31536000; includeSubDomains | HIGH — SSL stripping |
| Referrer-Policy | strict-origin-when-cross-origin | LOW — data leakage |
| Permissions-Policy | Restrict unneeded browser features | LOW-MEDIUM |
CSP weaknesses to flag:
- Missing CSP entirely — HIGH
- script-src * or default-src * — HIGH (allows any script source)
- unsafe-inline in script-src — MEDIUM (allows inline scripts)
- unsafe-eval in script-src — MEDIUM (allows eval)
Authentication and Session Management (Frontend)
Token storage — flag insecure patterns:
localStorage.setItem("token", jwtToken);
localStorage.setItem("authToken", bearerToken);
CSRF protection checklist:
- CSRF tokens on all state-changing forms
- SameSite=Strict or SameSite=Lax on session cookies
- Custom request headers on AJAX calls (e.g., X-Requested-With)
JWT issues to flag:
- JWTs stored in localStorage — HIGH
- alg: none accepted by server — CRITICAL
- No expiry (exp claim) validation — HIGH
- Sensitive PII in JWT payload — MEDIUM (payload is base64, NOT encrypted)
Sensitive Data Exposure in Client Code
const API_KEY = "AIzaSy...";
const STRIPE_KEY = "sk_live_...";
const DB_PASSWORD = "password123";
const INTERNAL_API = "http://10.0.0.5:8080/internal";
Framework-specific env var exposure:
- React (CRA): REACT_APP_* vars are bundled into client JS — never put secrets here
- Vite: VITE_* vars end up in client bundle — never put secrets here
- Next.js: NEXT_PUBLIC_* vars are browser-visible — never put secrets here
- Flag any process.env.DATABASE_URL or process.env.JWT_SECRET in frontend code — CRITICAL
Third-Party Dependencies (Frontend)
npm audit --audit-level=high
npm outdated
yarn audit
Manual checks:
- CDN-loaded scripts missing Subresource Integrity (SRI) hashes — MEDIUM
Good: script src="cdn.example.com/lib.js" integrity="sha384-abc123..."
Bad: script src="cdn.example.com/lib.js" (no integrity attribute)
- Typosquatted package names: lodahs, recat, expres — CRITICAL
- Post-install scripts that make network requests — review carefully
Phase 4: Backend Security Audit
Injection Vulnerabilities
SQL Injection (CWE-89) — highest-impact injection risk:
query = "SELECT * FROM users WHERE id = " + user_id
cursor.execute("SELECT * FROM users WHERE name = '" + name + "'")
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
db.query("SELECT * FROM users WHERE email = '" + req.body.email + "'")
db.query("SELECT * FROM users WHERE email = ?", [req.body.email])
NoSQL Injection (MongoDB) — HIGH:
db.users.find({ username: req.body.username })
db.users.find({ username: String(req.body.username) })
Command Injection (CWE-78) — CRITICAL:
os.system("ping " + user_input)
subprocess.call("ls " + directory, shell=True)
subprocess.call(["ping", user_input])
Server-Side Template Injection (SSTI) — CRITICAL (leads to RCE):
render_template_string("Hello " + username)
Authentication and Authorization
Password storage — flag these immediately:
user.password = password
hashlib.md5(password).hexdigest()
hashlib.sha1(password).hexdigest()
from passlib.hash import bcrypt
hashed = bcrypt.hash(password)
JWT security:
jwt.verify(token, secret)
jwt.verify(token, secret, { algorithms: ["HS256"] })
const JWT_SECRET = "secret"
const JWT_SECRET = "changeme"
Session management checklist:
- Session IDs are cryptographically random (not sequential integers)
- Session regenerated after login (prevents session fixation CWE-384)
- Session invalidated on logout
- Absolute timeout (2-8 hours) AND idle timeout (15-30 min) enforced
Broken Access Control (OWASP #1 Risk 2025)
app.get("/api/users/:id", async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user);
});
app.get("/api/users/:id", requireAuth, async (req, res) => {
if (req.params.id !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: "Forbidden" });
}
const user = await User.findById(req.params.id);
res.json(user);
});
Mass assignment (parameter pollution) — HIGH:
const user = new User(req.body);
const user = new User({
name: req.body.name,
email: req.body.email
});
Access control checklist:
- All endpoints require authentication (no accidental @PermitAll or public routes)
- Authorization checked on every request, not just at login
- Admin-only endpoints have role checks beyond just authentication
- IDOR: resource IDs validated against requesting user's ownership
- Directory listing disabled on web server
- Sensitive API endpoints not publicly discoverable
Cryptography and Data Protection
Flag these cryptographic weaknesses:
| Issue | Severity | Fix |
|---|
| MD5 or SHA1 for passwords | CRITICAL | Use bcrypt/argon2/scrypt |
| DES or 3DES encryption | CRITICAL | Use AES-256-GCM |
| AES-ECB mode | HIGH | Use AES-GCM or AES-CBC with HMAC |
| Hardcoded encryption key | CRITICAL | Use key management service |
| HTTP for sensitive data transmission | HIGH | Enforce HTTPS/TLS 1.2+ |
| verify=False in HTTPS requests | CRITICAL | Never disable cert verification |
| Math.random() for security tokens | HIGH | Use crypto.randomBytes or secrets module |
requests.get(url, verify=False)
import random
token = str(random.random())
import secrets
token = secrets.token_urlsafe(32)
Security Misconfiguration
Framework-specific flags:
DEBUG = True
ALLOWED_HOSTS = ["*"]
SECRET_KEY = "django-insecure-..."
CORS_ALLOW_ALL_ORIGINS = True
app.use(cors()) # HIGH: allows all origins by default
app.use(cors({ origin: "*" })) # HIGH: explicit wildcard
Database security flags:
- Connection strings or credentials hardcoded in code — CRITICAL
- Database directly accessible from internet (no VPC/firewall) — CRITICAL
- App DB user has DROP, CREATE, or ALTER permissions — HIGH
- Principle of least privilege not applied to DB user — MEDIUM
Docker/container:
# HIGH: running container as root (no USER directive)
FROM node:18
# Should include: USER node
# CRITICAL: secrets baked into image layers
ARG API_KEY=secret123
ENV DATABASE_URL=postgres://user:pass@host/db
# Use --secret mount or runtime env vars instead
API Security
REST API checklist:
- Rate limiting on all public endpoints (prevents brute force and scraping)
- Rate limiting on auth endpoints specifically (prevents credential stuffing)
- Input validation on ALL parameters: type, length, format, range
- Output filtering: API never returns more fields than requested/needed
- Pagination limits enforced (no LIMIT 99999 or unbounded queries)
- HTTP method enforcement (PUT /users should reject GET)
GraphQL specific issues:
- Introspection enabled in production — MEDIUM (exposes schema to attackers)
- No query depth limiting — HIGH (enables DoS via deeply nested queries)
- No query complexity limiting — HIGH
- Batching attacks possible without rate limiting — HIGH
Verbose error messages — flag as MEDIUM:
{ "error": "SQLSTATE[42000]: Syntax error near 'id'..." }
{ "error": "Cannot read property 'id' at /app/routes/user.js:42" }
{ "error": "An internal error occurred. Please try again." }
File Upload and Path Traversal
File upload vulnerabilities:
filename = request.files["file"].filename
file.save(os.path.join(UPLOAD_DIR, filename))
import uuid, magic
safe_filename = str(uuid.uuid4()) + get_validated_extension(file)
Path traversal (CWE-22) — CRITICAL:
open(BASE_DIR + "/" + user_input)
real_path = os.path.realpath(os.path.join(BASE_DIR, user_input))
if not real_path.startswith(os.path.realpath(BASE_DIR) + os.sep):
raise PermissionError("Path traversal detected")
Logging and Error Handling
logger.info(f"Login: user={username}, password={password}")
logger.debug(f"Auth token: {jwt_token}")
logger.info(f"User SSN {user.ssn} accessed resource")
Logging checklist:
- Failed login attempts logged with timestamp, IP, and username
- Successful logins logged
- Authorization failures logged
- Passwords, tokens, PII, and card numbers NEVER logged (CWE-532)
- Log injection prevented (user input sanitized before logging)
- Error messages shown to users are generic — no stack traces in production
Phase 5: Dependency and Supply Chain Security
OWASP A06 (2021) / A03 (2025): Software Supply Chain Failures.
npm audit --audit-level=critical
npx snyk test
pip-audit
safety check -r requirements.txt
bundle audit
mvn dependency-check:check
govulncheck ./...
Manual dependency review:
- Flag packages abandoned for 2+ years with no security updates
- Verify package-lock.json or yarn.lock is committed (reproducible builds)
- Flag * or latest version pins — unpredictable, potentially insecure updates
- Inspect post-install scripts in package.json that make network requests
- Watch for typosquatted package names (lodahs, recat, expres, etc.)
- Transitive dependencies: trace with npm ls
Supply chain attack indicators:
- Package recently transferred to a new maintainer
- Sudden major version bump with minimal changelog
- Post-install or preinstall scripts that are undocumented
Phase 6: Infrastructure and Configuration Checks
Cloud IaC (Terraform/CloudFormation) — flag:
- acl = "public-read" on S3 buckets — HIGH
- cidr_blocks = ["0.0.0.0/0"] with wide port ranges — CRITICAL
- Secrets stored in tfvars files committed to repo — CRITICAL
- Security groups allowing 0.0.0.0/0 on port 22 or 3389 — CRITICAL
Docker/container security checklist:
- Base images pinned to specific digest, not "latest"
- USER directive present (non-root)
- No secrets in ENV, ARG, or image layers
- .dockerignore excludes .env, .git, credentials, id_rsa
- Multi-stage builds minimize final image size and attack surface
CI/CD pipeline security:
- Secrets stored in CI secrets manager, not in YAML files in repo
- Third-party GitHub Actions pinned to commit SHA (not tag):
Risky: uses: actions/checkout@v3
Safe: uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
- GITHUB_TOKEN permissions scoped to minimum needed
- Pull request workflows cannot access production secrets
Web server configuration:
- Server version header hidden (server_tokens off in nginx)
- Directory browsing disabled
- Sensitive paths blocked: .git, .env, phpinfo.php, /.git/config
- TLS 1.0 and TLS 1.1 disabled (TLS 1.2+ only)
- HTTPS redirect active for all HTTP traffic
Producing the Security Report
Severity Classification
| Severity | CVSS | Description | Example | SLA |
|---|
| CRITICAL | 9-10 | Direct path to full compromise | Hardcoded prod secret, RCE via injection | Immediate |
| HIGH | 7-8.9 | Significant data breach or system risk | SQLi, stored XSS, broken auth | 7 days |
| MEDIUM | 4-6.9 | Partial access or data exposure | Missing CSRF, weak crypto, verbose errors | 30 days |
| LOW | 1-3.9 | Defense-in-depth gap | Missing security header | 90 days |
| INFO | 0 | Best practice recommendation | Logging improvement | Backlog |
Report Template
Use this structure for every finding:
Finding #N: [Descriptive Title]
Severity: CRITICAL / HIGH / MEDIUM / LOW / INFO
Category: OWASP A0X:2025 — [Name] / CWE-XXX
Location: path/to/file.py:42 (or endpoint/component)
Description:
What the vulnerability is and why it is dangerous.
Evidence:
[Relevant code snippet showing the vulnerable pattern]
Impact:
What an attacker can achieve if this is exploited.
Remediation:
Step-by-step fix with corrected code example.
References:
CWE-N: https://cwe.mitre.org/data/definitions/N.html
OWASP: https://owasp.org/...
Report summary — always open with this:
SECURITY AUDIT SUMMARY
======================
Repository : [name]
Date : [date]
Stack : [tech stack identified]
FINDINGS OVERVIEW
Critical : X High : X Medium : X Low : X Info : X
Total : X
TOP 3 PRIORITY ACTIONS
1. [Most critical finding — action required]
2. [Second finding — action required]
3. [Third finding — action required]
POSITIVE SECURITY FINDINGS
- [Acknowledge good practices found, e.g., "Parameterized queries used consistently"]
- [e.g., "HTTPS enforced site-wide"]
- [e.g., "Dependency audit passing with no critical CVEs"]
Always include positive findings. Security audits that only list problems are less
effective. Teams are more receptive to security work when good practices are acknowledged.
Quick Grep Reference
Copy-paste these commands when auditing a repository:
find . -name ".env*" -not -path "*/node_modules/*" -not -name ".env.example"
grep -rn "AKIA[0-9A-Z]" . --include="*.js" --include="*.ts" --include="*.py" --include="*.yml"
grep -rn "password\s*=" . | grep -v "test\|spec\|example\|#\|YOUR_"
grep -rn "api_key\s*=" . | grep -v "PLACEHOLDER\|YOUR_API_KEY\|EXAMPLE"
grep -rn "BEGIN PRIVATE KEY\|BEGIN RSA PRIVATE" .
grep -rn "mongodb+srv://\|postgresql://\|mysql://" . | grep -v test
find . -name "*.pem" -o -name "id_rsa" 2>/dev/null | grep -v node_modules
grep -rn "innerHTML\s*=" . --include="*.js" --include="*.jsx" --include="*.tsx"
grep -rn "dangerouslySetInnerHTML" .
grep -rn "eval(" . --include="*.js" --include="*.ts" | grep -v "test\|spec"
grep -rn "shell=True" . --include="*.py"
grep -rn "os\.system(" . --include="*.py"
grep -rn "localStorage.*[Tt]oken\|localStorage.*[Aa]uth" . --include="*.js" --include="*.ts"
grep -rn "verify\s*=\s*False" . --include="*.py"
grep -rn "hashlib\.md5\|hashlib\.sha1" . --include="*.py"
grep -rn "DEBUG\s*=\s*True" . --include="*.py" --include="*.env"
grep -rn "CORS.*\*\|cors.*all" . --include="*.js" --include="*.ts"
grep -rn "0\.0\.0\.0/0" . --include="*.tf" --include="*.yml"
grep -rn "Math\.random()" . --include="*.js" | grep -i "token\|secret\|key\|session"
grep -rn "random\.random()\|random\.randint" . --include="*.py" | grep -i "token\|secret"