Security Reviewer - Application Security Intelligence
Comprehensive security review system for web apps, mobile apps, APIs, and infrastructure. Combines automated scanning tools with expert manual review against industry frameworks.
When to Use
Must Use (automatic triggers):
- Before any deployment or push to production
- End of development session (security sweep)
- After adding new dependencies
- After implementing auth/payment/data-handling features
- When handling user input, file uploads, or external data
- When configuring CORS, CSP, or other security headers
- When reviewing PRs that touch security-sensitive code
Recommended:
- After significant refactoring
- When onboarding new APIs or third-party services
- Periodic codebase health checks
- Before security audits or compliance reviews
Automated Scanning Tools
1. Semgrep (SAST - Static Analysis)
Scans code for vulnerability patterns across 30+ languages.
# Full project scan with auto-detection
semgrep scan --config auto .
# Windows/MINGW fallback: python -m semgrep scan --config auto .
# Scan specific OWASP categories
semgrep scan --config "p/owasp-top-ten" .
# Scan for secrets in code
semgrep scan --config "p/secrets" .
# Scan for specific language
semgrep scan --config "p/javascript" .
semgrep scan --config "p/python" .
semgrep scan --config "p/typescript" .
semgrep scan --config "p/react" .
# Scan with JSON output for parsing
semgrep scan --config auto --json .
# Scan specific files
semgrep scan --config auto src/auth/ src/api/
Key rulesets:
p/owasp-top-ten— OWASP Top 10 vulnerabilitiesp/secrets— Hardcoded secrets and credentialsp/security-audit— Broad security patternsp/javascript/p/typescript/p/python/p/react— Language-specificp/jwt— JWT implementation issuesp/sql-injection— SQL injection patternsp/xss— Cross-site scripting patterns
2. Gitleaks (Secret Detection)
Scans git history and working directory for exposed secrets.
# Scan current directory
gitleaks detect -s . -v
# Scan git history
gitleaks detect -v
# Generate JSON report
gitleaks detect -s . -f json -r gitleaks-report.json
# Scan specific path
gitleaks detect -s ./src -v
3. npm audit (JavaScript Dependencies)
# Check for known vulnerabilities
npm audit
# JSON output for parsing
npm audit --json
# Fix automatically where possible
npm audit fix
# Check production deps only
npm audit --omit=dev
4. pip-audit (Python Dependencies)
# Scan current environment
pip-audit
# Windows/MINGW fallback: python -m pip_audit
# Scan requirements file
pip-audit -r requirements.txt
# Windows/MINGW fallback: python -m pip_audit -r requirements.txt
# JSON output
pip-audit --format json
# Fix automatically
pip-audit --fix
5. GitHub Security Features (via gh CLI)
# Check Dependabot alerts
gh api repos/{owner}/{repo}/dependabot/alerts --jq '.[].security_advisory.summary'
# Check secret scanning alerts
gh api repos/{owner}/{repo}/secret-scanning/alerts --jq '.[].secret_type_display_name'
# Check code scanning alerts
gh api repos/{owner}/{repo}/code-scanning/alerts --jq '.[].rule.description'
6. Manual Pattern Scanning (via Grep)
When automated tools aren't available, use Grep for critical patterns:
# Hardcoded secrets patterns
grep -rn "api[_-]?key\s*[:=]" --include="*.{js,ts,py,env}" .
grep -rn "password\s*[:=]\s*['\"]" --include="*.{js,ts,py}" .
grep -rn "secret\s*[:=]\s*['\"]" --include="*.{js,ts,py}" .
grep -rn "token\s*[:=]\s*['\"]" --include="*.{js,ts,py}" .
grep -rn "AKIA[0-9A-Z]{16}" . # AWS Access Keys
grep -rn "sk-[a-zA-Z0-9]{48}" . # OpenAI API keys
grep -rn "ghp_[a-zA-Z0-9]{36}" . # GitHub tokens
# SQL injection patterns
grep -rn "execute.*f['\"]" --include="*.py" .
grep -rn "query.*\$\{" --include="*.{js,ts}" .
grep -rn "raw\(.*\+" --include="*.{js,ts,py}" .
# XSS patterns
grep -rn "dangerouslySetInnerHTML" --include="*.{jsx,tsx}" .
grep -rn "innerHTML\s*=" --include="*.{js,ts}" .
grep -rn "document\.write" --include="*.{js,ts}" .
grep -rn "\|safe" --include="*.html" . # Django/Jinja unsafe filter
# Insecure configurations
grep -rn "cors.*origin.*\*" --include="*.{js,ts}" .
grep -rn "verify.*false" --include="*.{js,ts,py}" .
grep -rn "rejectUnauthorized.*false" --include="*.{js,ts}" .
grep -rn "disable.*csrf" --include="*.{js,ts,py}" .
grep -rn "NODE_TLS_REJECT_UNAUTHORIZED" --include="*.{js,ts}" .
Security Review Frameworks
OWASP Top 10 (2021) — Deep Checklist
A01: Broken Access Control
- Authorization checked server-side on every request (not just client-side)
- Deny by default — users can only access their own data
- CORS allowlists specific origins (never wildcard
*in production) - Directory traversal prevented (no
../in file paths) - JWT tokens validated: signature, expiry, issuer, audience
- Rate limiting on sensitive endpoints (login, password reset, API)
- IDOR (Insecure Direct Object Reference) — verify user owns resource
- Admin endpoints require role check, not just authentication
- API keys scoped to minimum required permissions
A02: Cryptographic Failures
- TLS 1.2+ enforced for all connections
- Sensitive data encrypted at rest (AES-256-GCM or ChaCha20)
- Passwords hashed with bcrypt (cost 12+), argon2id, or scrypt
- No MD5/SHA1 for password hashing
- Encryption keys stored in env vars or key management service (not code)
- PII identified and classified (GDPR/CCPA compliance)
- No sensitive data in URL query parameters
- Secure random number generation (
crypto.randomBytes, notMath.random)
A03: Injection
- All SQL uses parameterized queries or ORM — no string concatenation
- NoSQL injection prevented (MongoDB
$where,$gtoperator attacks) - Command injection prevented (no
exec(user_input), noshell=True) - LDAP injection prevented
- XSS prevented: output encoding on all user-supplied data
- Template injection prevented (server-side template engines)
- Path traversal prevented on file operations
- GraphQL query depth limited to prevent DoS
A04: Insecure Design
- Threat model exists (STRIDE analysis)
- Business logic abuse cases identified
- Rate limiting on resource-intensive operations
- Account enumeration prevented (same response for valid/invalid users)
- Password reset tokens are single-use and time-limited
- Multi-step flows cannot be bypassed by skipping steps
A05: Security Misconfiguration
- Default credentials changed/removed
- Debug mode disabled in production
- Stack traces hidden from users
- Unnecessary HTTP methods disabled
- Directory listing disabled
- Security headers configured (see Security Headers section)
- Error messages generic — no internal details leaked
-
.envfiles in.gitignore - Source maps disabled in production
A06: Vulnerable & Outdated Components
- No known CVEs in dependencies (
npm audit/pip-audit) - Dependencies pinned to specific versions (lockfile)
- Unused dependencies removed
- License compatibility verified
- Dependabot or similar automated updates configured
A07: Identification & Authentication Failures
- Passwords minimum 8 chars, complexity not over-restrictive
- Account lockout after 5-10 failed attempts (with exponential backoff)
- MFA available for sensitive accounts
- Session tokens regenerated after login
- Session expiration configured (idle + absolute timeout)
- Logout invalidates server-side session
- Password reset doesn't reveal if account exists
- OAuth state parameter validated (CSRF protection)
A08: Software & Data Integrity Failures
- CI/CD pipeline access restricted
- Dependencies fetched from trusted registries only
- Subresource Integrity (SRI) for CDN scripts
- Code review required before merge
- Deployment artifacts signed/verified
- Auto-update mechanism validates signatures
A09: Security Logging & Monitoring Failures
- Authentication events logged (success + failure)
- Authorization failures logged
- Input validation failures logged
- Logs contain timestamp, source IP, user ID, action
- Logs do NOT contain passwords, tokens, PII, or credit cards
- Logs protected from injection (structured logging)
- Alerting configured for anomalous patterns
- Log retention policy defined
A10: Server-Side Request Forgery (SSRF)
- User-supplied URLs validated against allowlist
- Internal network access blocked for user-supplied URLs
- DNS rebinding protection
- Response from fetched URLs not directly returned to user
- Cloud metadata endpoints blocked (169.254.169.254)
CWE Top 25 Additional Checks
- CWE-787: Out-of-bounds write (buffer overflow)
- CWE-79: XSS (reflected, stored, DOM-based)
- CWE-89: SQL injection
- CWE-416: Use after free
- CWE-78: OS command injection
- CWE-20: Improper input validation
- CWE-125: Out-of-bounds read
- CWE-22: Path traversal
- CWE-352: CSRF
- CWE-434: Unrestricted file upload
- CWE-862: Missing authorization
- CWE-476: NULL pointer dereference
- CWE-287: Improper authentication
- CWE-190: Integer overflow
- CWE-502: Deserialization of untrusted data
STRIDE Threat Model
| Threat | Question | Mitigations |
|---|---|---|
| Spoofing | Can an attacker impersonate a user or service? | Strong auth, MFA, certificate pinning |
| Tampering | Can data be modified in transit or at rest? | HMAC, digital signatures, TLS, integrity checks |
| Repudiation | Can actions be denied without proof? | Audit logging, timestamps, non-repudiation |
| Information Disclosure | Can sensitive data leak? | Encryption, access control, data classification |
| Denial of Service | Can the service be disrupted? | Rate limiting, CDN, input validation, resource limits |
| Elevation of Privilege | Can permissions be escalated? | Least privilege, role checks, input validation |
Security Headers (Required)
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https:; frame-ancestors 'none'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-XSS-Protection: 0
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
SLSA Compliance
| Level | Requirements |
|---|---|
| L1 | Build process documented, build scripts versioned, provenance generated |
| L2 | Hosted build service, signed provenance, source version controlled |
| L3 | Isolated build environment, non-falsifiable provenance, verified source |
| L4 | Hermetic builds, two-person review, reproducible builds |
Security Review Workflow
Phase 1: Automated Scans (run these first)
# 1. Secret scan
gitleaks detect -s . -v
# 2. SAST scan
semgrep scan --config auto .
# 3. Dependency audit (pick based on stack)
npm audit # JavaScript
pip-audit # Python
Phase 2: Manual Code Review
- Read auth/login/session code line by line
- Check all user input handling
- Review database query construction
- Verify file upload restrictions
- Check API endpoint authorization
- Review error handling for info leaks
- Verify environment variable usage (no hardcoded secrets)
Phase 3: Configuration Review
- Check
.env.exampleexists with placeholder values - Verify
.envis in.gitignore - Review CORS configuration
- Check CSP headers
- Verify production environment disables debug mode
- Check Dockerfile for security best practices
Phase 4: Live Testing (if deployed)
- Test security headers with Playwright
- Verify HTTPS redirect
- Test rate limiting
- Check error pages don't leak info
- Test auth flows for bypasses
Vulnerability Report Template
# Security Review Report
**Project:** [name]
**Date:** [date]
**Reviewer:** Security Agent
## Executive Summary
[1-2 sentence overall assessment]
## Scan Results
### Semgrep SAST: [X findings]
### Gitleaks Secrets: [X findings]
### Dependency Audit: [X vulnerabilities]
## Findings
### [CRITICAL/HIGH/MEDIUM/LOW] — [Title]
- **CWE:** CWE-XXX
- **OWASP:** A0X
- **Location:** `file:line`
- **Description:** [what's wrong]
- **Impact:** [what an attacker could do]
- **Fix:** [exact code change needed]
## Recommendations
1. [Priority action items]
Secure Coding Quick Reference
Never Do This
// SQL injection
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`)
// Command injection
exec(`convert ${userFile} output.pdf`)
// XSS
element.innerHTML = userInput
// Hardcoded secret
const API_KEY = "sk-abc123..."
// Insecure comparison
if (token == expectedToken) // use === or crypto.timingSafeEqual
// Logging secrets
console.log("Auth token:", token)
Always Do This
// Parameterized query
db.query('SELECT * FROM users WHERE id = $1', [req.params.id])
// Safe command execution
execFile('convert', [sanitizedFile, 'output.pdf'])
// Safe rendering
element.textContent = userInput
// Environment variable
const API_KEY = process.env.API_KEY
// Timing-safe comparison
crypto.timingSafeEqual(Buffer.from(token), Buffer.from(expectedToken))
// Structured logging without secrets
logger.info('Auth attempt', { userId, success: true })