security-engineering
This skill should be used when auditing code for security issues, reviewing authentication/authorization, evaluating input validation, analyzing cryptographic usage, or reviewing dependency security. Provides OWASP patterns, CWE analysis, and threat modeling guidance.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install outfitter-dev-agents-security-engineering
Repository
Skill path: baselayer/skills/security-engineering
This skill should be used when auditing code for security issues, reviewing authentication/authorization, evaluating input validation, analyzing cryptographic usage, or reviewing dependency security. Provides OWASP patterns, CWE analysis, and threat modeling guidance.
Open repositoryBest for
Primary workflow: Run DevOps.
Technical facets: Full Stack, Security.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: outfitter-dev.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install security-engineering into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/outfitter-dev/agents before adding security-engineering to shared team environments
- Use security-engineering for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: security-engineering
version: 1.0.0
description: This skill should be used when auditing code for security issues, reviewing authentication/authorization, evaluating input validation, analyzing cryptographic usage, or reviewing dependency security. Provides OWASP patterns, CWE analysis, and threat modeling guidance.
---
# Security Engineering
Threat-aware code review. Vulnerability detection. Risk-ranked remediation.
<when_to_use>
- Security audits and code reviews
- Authentication/authorization review
- Input validation and sanitization checks
- Cryptographic implementation review
- Dependency and supply chain security
- Threat modeling for new features
NOT for: performance optimization, general code review, feature implementation
</when_to_use>
<phases>
Track with TodoWrite. Each phase feeds the next.
| Phase | Trigger | activeForm |
|-------|---------|------------|
| Threat Model | Session start | "Building threat model" |
| Attack Surface | Model complete | "Mapping attack surface" |
| Vulnerability Scan | Surface mapped | "Scanning for vulnerabilities" |
| Risk Assessment | Vulns identified | "Assessing risk levels" |
| Remediation Plan | Risks assessed | "Planning remediation" |
Critical findings: add urgent remediation task immediately.
</phases>
<severity_levels>
CVSS-aligned severity for findings:
| Indicator | Severity | CVSS | Examples |
|-----------|----------|------|----------|
| **Critical** | 9.0-10.0 | RCE, auth bypass, mass data exposure, admin privesc |
| **High** | 7.0-8.9 | SQLi, stored XSS, auth weakness, sensitive data leak |
| **Medium** | 4.0-6.9 | CSRF, reflected XSS, info disclosure, weak crypto |
| **Low** | 0.1-3.9 | Misconfig, missing headers, verbose errors |
Format: "**Critical** RCE via unsanitized shell command"
</severity_levels>
<threat_modeling>
## STRIDE Framework
Systematic threat identification by category:
| Threat | Question | Check |
|--------|----------|-------|
| **S**poofing | Can attacker impersonate? | Auth mechanisms, tokens, sessions, API keys |
| **T**ampering | Can attacker modify data? | Input validation, integrity checks, DB access |
| **R**epudiation | Can actions be denied? | Audit logs, signatures, timestamps |
| **I**nfo Disclosure | Can attacker access secrets? | Encryption, access control, logging |
| **D**enial of Service | Can attacker disrupt? | Rate limits, timeouts, input size |
| **E**levation | Can attacker gain access? | Authz checks, RBAC, least privilege |
## Attack Trees
Map paths from attacker goal to entry points:
```
Goal: Steal credentials
- Attack login
- SQLi in username
- Brute force (no rate limit)
- Session fixation
- Intercept traffic
- HTTPS downgrade
- MITM
- Exploit reset
- Predictable token
- No expiry
```
For each branch assess: feasibility, impact, detection, current defenses.
## Trust Boundaries
Identify where data crosses trust levels:
- Browser to server
- Server to database
- Service to third-party API
- Internal service to service
Every boundary needs validation.
</threat_modeling>
<attack_surface>
## Entry Points
**External**:
- HTTP/API endpoints (REST, GraphQL, gRPC)
- WebSocket connections
- File uploads
- OAuth/SAML flows
- Webhooks
**Data Inputs**:
- User data (forms, query params, headers)
- File content (type, size, payload)
- API payloads (JSON, XML)
- Database queries
**Auth Boundaries**:
- Public (no auth)
- Authenticated
- Admin/privileged
- Service-to-service
## Prioritize Review
1. Unauthenticated external inputs
2. Privileged operations
3. Data persistence layers
4. Third-party integrations
For each entry point document:
- Auth required? (none/user/admin)
- Input validated? (none/basic/strict)
- Rate limited?
- Logged?
- Encrypted?
</attack_surface>
<vulnerability_patterns>
## Quick Reference
| Vulnerability | Vulnerable | Secure |
|--------------|------------|--------|
| SQL Injection | String concat in query | Parameterized queries |
| XSS | innerHTML with user data | textContent or DOMPurify |
| Command Injection | exec() with user input | execFile() with array |
| Path Traversal | Direct path concat | basename + prefix check |
| Weak Password | MD5/SHA1/plain | bcrypt (12+) or argon2 |
| Predictable Token | Math.random/Date.now | crypto.randomBytes(32) |
| Broken Auth | Client-side role check | Server-side every request |
| IDOR | No ownership check | Verify user owns resource |
| Hardcoded Secret | API key in code | Environment variable |
| Info Leak | Stack trace to user | Generic error, log detail |
## Critical Checks
**Authentication**:
- Passwords: bcrypt/argon2, cost 12+
- Sessions: crypto.randomBytes(32), httpOnly, secure, sameSite
- JWT: verify signature, specify algorithm, short expiry
- Reset: random token, 1hr expiry, hash stored token
**Authorization**:
- Server-side on every request
- Verify ownership before resource access
- Explicit allowlist for mass assignment
- No role elevation from client input
**Input Validation**:
- Type, length, format on all inputs
- Parameterized queries (never concat)
- Escape/sanitize HTML output
- Validate file uploads (type, size, content)
**Cryptography**:
- AES-256-GCM, SHA-256+
- Never MD5, SHA1, DES, ECB
- Secrets from env, never hardcoded
- crypto.randomBytes for all tokens
See [vulnerability-patterns.md](references/vulnerability-patterns.md) for code examples.
</vulnerability_patterns>
<owasp_top_10>
2021 OWASP Top 10 categories. Check each during vulnerability scan.
| # | Category | Key CWEs | Top Mitigations |
|---|----------|----------|-----------------|
| A01 | Broken Access Control | 200, 352, 639 | Server-side checks, ownership validation |
| A02 | Cryptographic Failures | 259, 327, 331 | TLS, bcrypt, no hardcoded secrets |
| A03 | Injection | 20, 79, 89 | Parameterized queries, input validation |
| A04 | Insecure Design | 209, 256, 434 | Threat modeling, rate limiting |
| A05 | Security Misconfiguration | 16, 611, 614 | Security headers, disable debug |
| A06 | Vulnerable Components | 1035, 1104 | npm audit, Dependabot |
| A07 | Auth Failures | 287, 307, 521 | Strong passwords, MFA, rate limiting |
| A08 | Integrity Failures | 502, 494 | Verify signatures, schema validation |
| A09 | Logging Failures | 117, 532, 778 | Audit logs, redact sensitive data |
| A10 | SSRF | 918 | URL allowlist, block private IPs |
See [owasp-top-10.md](references/owasp-top-10.md) for detailed breakdowns with code examples.
</owasp_top_10>
<workflow>
**Loop**: Model Threats -> Map Surface -> Scan Vulnerabilities -> Assess Risk -> Plan Remediation
1. **Threat Model**
- STRIDE analysis for component
- Attack trees for critical paths
- Identify trust boundaries
- Document threat actors
2. **Attack Surface**
- Inventory all inputs
- Classify by auth level
- Map data flows across boundaries
- Prioritize high-risk entry points
3. **Vulnerability Scan**
- Check each entry against OWASP Top 10
- Review auth/authz
- Validate input handling
- Check crypto usage
- Scan deps: `npm audit`, `cargo audit`
4. **Risk Assessment**
- Rate severity (Critical/High/Medium/Low)
- Consider exploitability
- Assess impact (CIA triad)
- Calculate risk score
5. **Remediation Plan**
- **Critical**: immediate action
- **High**: fix before release
- **Medium**: schedule in sprint
- **Low**: backlog or accept
Update todos as you progress. Use [review-checklist.md](references/review-checklist.md) for verification.
</workflow>
<reporting>
## Finding Format
```markdown
## {SEVERITY} {VULN_NAME}
**Category**: {OWASP} | **CWE**: {ID} | **File**: {PATH}:{LINES}
### Issue
{CLEAR_EXPLANATION}
### Impact
{WHAT_ATTACKER_COULD_DO}
### Fix
{SPECIFIC_REMEDIATION_WITH_CODE}
```
## Summary Format
```markdown
# Security Audit: {SCOPE}
| Severity | Count |
|----------|-------|
| Critical | N |
| High | N |
| Medium | N |
| Low | N |
## Key Findings
1. {TOP_CRITICAL}
2. {SECOND}
3. {THIRD}
## Recommendations
- Immediate: {CRITICAL_FIXES}
- Short-term: {HIGH_MEDIUM}
- Long-term: {HARDENING}
```
See [report-templates.md](references/report-templates.md) for full templates.
</reporting>
<rules>
ALWAYS:
- Start with threat modeling before code review
- Map complete attack surface
- Check against all OWASP Top 10 categories
- Use severity indicators consistently
- Provide specific remediation with code
- Verify fixes don't introduce new vulnerabilities
- Document security assumptions
- Update todos when transitioning phases
NEVER:
- Skip threat modeling for "simple" features
- Assume input is trustworthy
- Rely on client-side security
- Use deprecated crypto (MD5, SHA1, DES)
- Log sensitive data
- Disable security checks "temporarily"
- Mark complete without remediation plan
</rules>
<references>
**Deep dives**:
- [vulnerability-patterns.md](references/vulnerability-patterns.md) - secure vs vulnerable code examples
- [owasp-top-10.md](references/owasp-top-10.md) - detailed OWASP categories with CWE mappings
- [review-checklist.md](references/review-checklist.md) - complete security review checklist
- [report-templates.md](references/report-templates.md) - finding and audit report templates
**Related skills**:
- codebase-analysis - evidence-based investigation foundation
- debugging - when security issues manifest as bugs
**External**:
- [OWASP Top 10](https://owasp.org/Top10/)
- [CWE Database](https://cwe.mitre.org/)
- [OWASP Cheat Sheets](https://cheatsheetseries.owasp.org/)
</references>
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/vulnerability-patterns.md
```markdown
# Vulnerability Patterns Reference
Secure vs vulnerable code patterns organized by category. Each pattern shows the vulnerability and its remediation.
---
## Input Validation
### SQL Injection
```typescript
// VULNERABLE
const query = `SELECT * FROM users WHERE email = '${userEmail}'`;
// SECURE - parameterized queries
const query = 'SELECT * FROM users WHERE email = ?';
db.execute(query, [userEmail]);
```
### XSS (Cross-Site Scripting)
```typescript
// VULNERABLE - direct HTML insertion
element.innerHTML = userInput;
// SECURE - use textContent or sanitize
element.textContent = userInput;
// OR for rich content
element.innerHTML = DOMPurify.sanitize(userInput);
```
### Command Injection
```typescript
// VULNERABLE
exec(`convert ${userFilename} output.png`);
// SECURE - parameterized or allowlist
execFile('convert', [userFilename, 'output.png']);
```
### Path Traversal
```typescript
// VULNERABLE
const filePath = `/uploads/${userFileName}`;
// SECURE - validate and normalize
const safeName = path.basename(userFileName);
const filePath = path.join('/uploads', safeName);
if (!filePath.startsWith('/uploads/')) {
throw new Error('Invalid path');
}
```
### XXE (XML External Entity)
```typescript
// VULNERABLE
const parser = new DOMParser();
const doc = parser.parseFromString(xmlInput, 'text/xml');
// SECURE - disable external entities
const parser = new DOMParser({
locator: {},
errorHandler: {},
entityResolver: () => null, // Disable DTD processing
});
```
---
## Authentication & Sessions
### Password Storage
```typescript
// VULNERABLE - plain text or weak hash
const hash = md5(password);
// SECURE - bcrypt/argon2 with salt
const hash = await bcrypt.hash(password, 12);
```
### Session Management
```typescript
// VULNERABLE - predictable session IDs
const sessionId = userId + Date.now();
// SECURE - cryptographically random
const sessionId = crypto.randomBytes(32).toString('hex');
// Security attributes
res.cookie('session', sessionId, {
httpOnly: true,
secure: true, // HTTPS only
sameSite: 'strict',
maxAge: 3600000, // 1 hour
});
```
### JWT Handling
```typescript
// VULNERABLE - no signature verification
const payload = JSON.parse(atob(token.split('.')[1]));
// SECURE - verify signature
const payload = jwt.verify(token, SECRET_KEY, {
algorithms: ['HS256'], // Specify allowed algorithms
issuer: 'your-app',
audience: 'your-api',
});
```
### Password Reset
```typescript
// VULNERABLE - predictable tokens
const resetToken = userId + '-' + Date.now();
// SECURE - cryptographically random with expiry
const resetToken = crypto.randomBytes(32).toString('hex');
await db.execute(
'INSERT INTO reset_tokens (user_id, token, expires_at) VALUES (?, ?, ?)',
[userId, await bcrypt.hash(resetToken, 10), Date.now() + 3600000]
);
```
---
## Authorization
### Broken Access Control
```typescript
// VULNERABLE - client-side only check
if (user.isAdmin) {
// show admin panel
}
// SECURE - server-side enforcement
app.get('/admin/users', requireAdmin, (req, res) => {
if (!req.user?.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
// Admin operation
});
```
### IDOR (Insecure Direct Object Reference)
```typescript
// VULNERABLE - no ownership check
app.get('/api/documents/:id', async (req, res) => {
const doc = await db.getDocument(req.params.id);
res.json(doc);
});
// SECURE - verify ownership
app.get('/api/documents/:id', async (req, res) => {
const doc = await db.getDocument(req.params.id);
if (doc.userId !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(doc);
});
```
### Privilege Escalation
```typescript
// VULNERABLE - role from client input
app.post('/api/users', async (req, res) => {
const user = await createUser({
...req.body, // Includes role: 'admin' from malicious client
});
});
// SECURE - explicit allowlist
app.post('/api/users', async (req, res) => {
const allowedFields = ['name', 'email', 'password'];
const userData = pick(req.body, allowedFields);
const user = await createUser({
...userData,
role: 'user', // Server controls role
});
});
```
---
## Cryptography
### Weak Algorithms
```typescript
// VULNERABLE - deprecated algorithms
const hash = crypto.createHash('md5').update(data).digest('hex');
const cipher = crypto.createCipher('des', key);
// SECURE - modern algorithms
const hash = crypto.createHash('sha256').update(data).digest('hex');
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
```
### Hardcoded Secrets
```typescript
// VULNERABLE
const API_KEY = 'sk-1234567890abcdef';
const DB_PASSWORD = 'admin123';
// SECURE - environment variables
const API_KEY = process.env.API_KEY;
const DB_PASSWORD = process.env.DB_PASSWORD;
if (!API_KEY || !DB_PASSWORD) {
throw new Error('Missing required environment variables');
}
```
### Insufficient Randomness
```typescript
// VULNERABLE - predictable
const token = Math.random().toString(36);
// SECURE - cryptographically secure
const token = crypto.randomBytes(32).toString('hex');
```
---
## Data Exposure
### Sensitive Data in Logs
```typescript
// VULNERABLE
logger.info('User login', { email, password, ssn });
// SECURE - redact sensitive fields
logger.info('User login', {
email,
password: '[REDACTED]',
ssn: '[REDACTED]',
});
```
### Error Message Disclosure
```typescript
// VULNERABLE - exposes internals
catch (err) {
res.status(500).json({ error: err.stack });
}
// SECURE - generic message
catch (err) {
logger.error('Internal error', err);
res.status(500).json({ error: 'Internal server error' });
}
```
### Timing Attacks
```typescript
// VULNERABLE - early exit leaks info
if (user.password !== inputPassword) {
return false;
}
// SECURE - constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(user.password),
Buffer.from(inputPassword)
);
```
---
## Quick Reference
| Category | Vulnerable Pattern | Secure Pattern |
|----------|-------------------|----------------|
| SQL Injection | String concatenation | Parameterized queries |
| XSS | innerHTML with user input | textContent or DOMPurify |
| Command Injection | exec() with user input | execFile() with array args |
| Path Traversal | Direct path concat | path.basename + prefix check |
| Password Storage | MD5/SHA1/plain | bcrypt (cost 12+) or argon2 |
| Session IDs | Predictable (Date.now) | crypto.randomBytes(32) |
| JWT | Skip verification | jwt.verify() with algorithm |
| Access Control | Client-side only | Server-side on every request |
| IDOR | No ownership check | Verify user owns resource |
| Secrets | Hardcoded in code | Environment variables |
| Error Messages | Stack traces to users | Generic error + log details |
```
### references/owasp-top-10.md
```markdown
# OWASP Top 10 (2021) — Detailed Reference
Comprehensive breakdown of each OWASP Top 10 category with CWE mappings, vulnerability patterns, and remediation strategies.
## A01:2021 – Broken Access Control
Access control enforces policy such that users cannot act outside of their intended permissions. Failures typically lead to unauthorized information disclosure, modification, or destruction of data.
### Common Weaknesses
- **Missing Function Level Access Control** — users can access admin functions
- **Missing Resource Level Access Control (IDOR)** — users can access others' resources
- **CORS Misconfiguration** — overly permissive cross-origin policies
- **Force Browsing** — accessing pages/resources by URL guessing
- **Metadata Manipulation** — JWT/cookie tampering to elevate privileges
- **POST-based CSRF** — state-changing operations without CSRF protection
### CWE Mappings
- CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
- CWE-201: Insertion of Sensitive Information Into Sent Data
- CWE-352: Cross-Site Request Forgery (CSRF)
- CWE-359: Exposure of Private Personal Information to an Unauthorized Actor
- CWE-377: Insecure Temporary File
- CWE-402: Transmission of Private Resources into a New Sphere
- CWE-425: Direct Request (Forced Browsing)
- CWE-639: Authorization Bypass Through User-Controlled Key
- CWE-759: Use of a One-Way Hash without a Salt
- CWE-918: Server-Side Request Forgery (SSRF)
- CWE-1275: Sensitive Cookie with Improper SameSite Attribute
### Vulnerability Patterns
**IDOR (Insecure Direct Object Reference)**:
```typescript
// VULNERABLE — sequential IDs, no ownership check
GET /api/invoices/1001
{
"invoice_id": 1001,
"customer_id": 42,
"amount": 1500
}
// ATTACK — iterate through IDs
GET /api/invoices/1002 // Access someone else's invoice
GET /api/invoices/1003
GET /api/invoices/1004
```
**Remediation**:
```typescript
// SECURE — verify ownership before returning
app.get('/api/invoices/:id', authenticate, async (req, res) => {
const invoice = await db.getInvoice(req.params.id);
if (!invoice) {
return res.status(404).json({ error: 'Not found' });
}
// Verify user owns resource or is admin
if (invoice.customerId !== req.user.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(invoice);
});
// BETTER — use UUIDs instead of sequential IDs
const invoiceId = crypto.randomUUID(); // Non-guessable
```
**Missing Function Level Access Control**:
```typescript
// VULNERABLE — client-side check only
function AdminPanel() {
if (!user.isAdmin) {
return <div>Access Denied</div>;
}
return <AdminDashboard />;
}
// Attacker can still call API directly:
fetch('/api/admin/users').then(r => r.json()) // No server-side check!
```
**Remediation**:
```typescript
// SECURE — enforce on server
app.get('/api/admin/users', authenticate, requireAdmin, async (req, res) => {
// Server validates role on every request
if (!req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const users = await db.getAllUsers();
res.json(users);
});
// Middleware
function requireAdmin(req, res, next) {
if (!req.user?.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
}
```
**CORS Misconfiguration**:
```typescript
// VULNERABLE — allows all origins
app.use(cors({
origin: '*',
credentials: true // Allows any site to make authenticated requests!
}));
```
**Remediation**:
```typescript
// SECURE — explicit allowlist
const allowedOrigins = [
'https://app.example.com',
'https://admin.example.com',
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
}));
```
---
## A02:2021 – Cryptographic Failures
Previously known as Sensitive Data Exposure. Focuses on failures related to cryptography which often lead to exposure of sensitive data.
### Common Weaknesses
- **Transmitting data in clear text** — HTTP instead of HTTPS
- **Old/weak cryptographic algorithms** — MD5, SHA1, DES
- **Default/weak keys** — hardcoded or predictable
- **Missing encryption at rest** — sensitive data stored unencrypted
- **Improper certificate validation** — accepting self-signed certs in production
- **Insufficient entropy** — predictable random numbers
### CWE Mappings
- CWE-259: Use of Hard-coded Password
- CWE-327: Use of a Broken or Risky Cryptographic Algorithm
- CWE-331: Insufficient Entropy
### Vulnerability Patterns
**Weak Hashing Algorithm**:
```typescript
// VULNERABLE — MD5 is broken
const hash = crypto.createHash('md5').update(password).digest('hex');
// VULNERABLE — SHA1 is deprecated
const hash = crypto.createHash('sha1').update(password).digest('hex');
// VULNERABLE — no salt (rainbow tables)
const hash = crypto.createHash('sha256').update(password).digest('hex');
```
**Remediation**:
```typescript
// SECURE — bcrypt with sufficient cost
import bcrypt from 'bcrypt';
const saltRounds = 12; // Minimum 10, increase as hardware improves
const hash = await bcrypt.hash(password, saltRounds);
// Verification
const isValid = await bcrypt.compare(inputPassword, storedHash);
// ALTERNATIVE — Argon2 (winner of Password Hashing Competition)
import argon2 from 'argon2';
const hash = await argon2.hash(password, {
type: argon2.argon2id, // Resistant to GPU and side-channel attacks
memoryCost: 2 ** 16, // 64 MiB
timeCost: 3,
parallelism: 1,
});
```
**Hardcoded Secrets**:
```typescript
// VULNERABLE — secrets in code
const API_KEY = 'sk-1234567890abcdef';
const DB_PASSWORD = 'admin123';
const JWT_SECRET = 'mysecret';
// Committed to Git — now in history forever!
```
**Remediation**:
```typescript
// SECURE — environment variables
const API_KEY = process.env.API_KEY;
const DB_PASSWORD = process.env.DB_PASSWORD;
const JWT_SECRET = process.env.JWT_SECRET;
// Validate at startup
if (!API_KEY || !DB_PASSWORD || !JWT_SECRET) {
throw new Error('Missing required environment variables');
}
// .env (add to .gitignore!)
API_KEY=sk-real-key-here
DB_PASSWORD=strong-password-here
JWT_SECRET=long-random-string-here
// .env.example (commit this)
API_KEY=your_api_key_here
DB_PASSWORD=your_db_password_here
JWT_SECRET=your_jwt_secret_here
```
**Weak Encryption Algorithm**:
```typescript
// VULNERABLE — DES is broken
const cipher = crypto.createCipher('des', key);
// VULNERABLE — ECB mode (patterns leak)
const cipher = crypto.createCipheriv('aes-256-ecb', key, null);
// VULNERABLE — no authentication (malleable)
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
```
**Remediation**:
```typescript
// SECURE — AES-256-GCM (authenticated encryption)
const algorithm = 'aes-256-gcm';
const key = crypto.randomBytes(32); // 256 bits
const iv = crypto.randomBytes(16); // 128 bits
// Encryption
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
// Store: iv + authTag + encrypted
// Decryption
const decipher = crypto.createDecipheriv(algorithm, key, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
```
**Insufficient Entropy**:
```typescript
// VULNERABLE — predictable
const sessionId = Math.random().toString(36);
const resetToken = Date.now().toString(36);
const apiKey = userId + '-' + Math.floor(Math.random() * 1000000);
```
**Remediation**:
```typescript
// SECURE — cryptographically secure random
const sessionId = crypto.randomBytes(32).toString('hex'); // 64 hex chars
const resetToken = crypto.randomBytes(32).toString('base64url');
const apiKey = crypto.randomBytes(24).toString('base64url');
// For UUIDs
const uuid = crypto.randomUUID(); // UUIDv4
```
---
## A03:2021 – Injection
Application is vulnerable to injection when user-supplied data is not validated, filtered, or sanitized by the application.
### Common Weaknesses
- **SQL Injection** — malicious SQL in queries
- **NoSQL Injection** — malicious queries in MongoDB, etc.
- **OS Command Injection** — executing shell commands
- **LDAP Injection** — malicious LDAP queries
- **XPath Injection** — malicious XPath queries
- **ORM Injection** — unsafe ORM query construction
### CWE Mappings
- CWE-20: Improper Input Validation
- CWE-74: Improper Neutralization of Special Elements in Output
- CWE-75: Failure to Sanitize Special Elements into a Different Plane
- CWE-77: Improper Neutralization of Special Elements used in a Command
- CWE-78: Improper Neutralization of Special Elements used in an OS Command
- CWE-79: Improper Neutralization of Input During Web Page Generation (XSS)
- CWE-80: Improper Neutralization of Script-Related HTML Tags
- CWE-83: Improper Neutralization of Script in Attributes
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command
- CWE-91: XML Injection
- CWE-93: Improper Neutralization of CRLF Sequences
- CWE-94: Improper Control of Generation of Code
- CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code
- CWE-96: Improper Neutralization of Directives in Statically Saved Code
- CWE-97: Improper Neutralization of Server-Side Includes
- CWE-183: Permissive List of Allowed Inputs
- CWE-184: Incomplete List of Disallowed Inputs
### Vulnerability Patterns
**SQL Injection**:
```sql
-- VULNERABLE — string concatenation
const query = `SELECT * FROM users WHERE email = '${userEmail}' AND password = '${userPassword}'`;
-- ATTACK
userEmail: [email protected]'--
userPassword: anything
-- RESULTS IN
SELECT * FROM users WHERE email = '[email protected]'--' AND password = 'anything'
-- Comment removes password check!
-- ATTACK 2 — data exfiltration
userEmail: ' UNION SELECT password FROM users--
-- ATTACK 3 — blind SQL injection
userEmail: ' OR 1=1--
```
**Remediation**:
```typescript
// SECURE — parameterized queries (prepared statements)
const query = 'SELECT * FROM users WHERE email = ? AND password = ?';
const [rows] = await db.execute(query, [userEmail, passwordHash]);
// PostgreSQL — numbered placeholders
const query = 'SELECT * FROM users WHERE email = $1 AND password = $2';
const result = await pool.query(query, [userEmail, passwordHash]);
// ORM — use safe methods
const user = await User.findOne({
where: {
email: userEmail,
password: passwordHash,
},
});
// NEVER — string interpolation or concatenation in SQL
```
**NoSQL Injection**:
```javascript
// VULNERABLE — object injection
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.collection('users').findOne({
email: email,
password: password,
});
});
// ATTACK — bypass authentication
POST /login
{
"email": { "$gt": "" },
"password": { "$gt": "" }
}
// Query becomes: find where email > "" AND password > ""
// Returns first user!
```
**Remediation**:
```typescript
// SECURE — type validation
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// Ensure inputs are strings
if (typeof email !== 'string' || typeof password !== 'string') {
return res.status(400).json({ error: 'Invalid input' });
}
const user = await db.collection('users').findOne({
email: email,
password: await hashPassword(password),
});
});
// BETTER — schema validation
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
app.post('/login', async (req, res) => {
const result = loginSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ error: 'Invalid input' });
}
const { email, password } = result.data;
// Now guaranteed to be strings
});
```
**OS Command Injection**:
```typescript
// VULNERABLE — user input in shell command
const filename = req.query.file;
exec(`convert ${filename} output.png`, (err, stdout) => {
// Process output
});
// ATTACK
?file=; rm -rf /
// RESULTS IN
convert ; rm -rf / output.png
// Executes rm -rf /!
```
**Remediation**:
```typescript
// SECURE — use parameterized API
import { execFile } from 'child_process';
const filename = req.query.file;
// Validate filename
if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {
return res.status(400).json({ error: 'Invalid filename' });
}
// Use execFile with array arguments (no shell)
execFile('convert', [filename, 'output.png'], (err, stdout) => {
if (err) {
logger.error('Conversion failed', err);
return res.status(500).json({ error: 'Conversion failed' });
}
// Process output
});
// BETTER — use library instead of shell command
import sharp from 'sharp';
await sharp(filename).toFile('output.png');
```
**XSS (Cross-Site Scripting)**:
```html
<!-- VULNERABLE — direct HTML insertion -->
<div id="greeting"></div>
<script>
const name = new URLSearchParams(window.location.search).get('name');
document.getElementById('greeting').innerHTML = `Hello ${name}!`;
</script>
<!-- ATTACK -->
?name=<img src=x onerror=alert(document.cookie)>
<!-- RESULTS IN -->
<div id="greeting">Hello <img src=x onerror=alert(document.cookie)>!</div>
<!-- Executes JavaScript! -->
```
**Remediation**:
```html
<!-- SECURE — use textContent -->
<div id="greeting"></div>
<script>
const name = new URLSearchParams(window.location.search).get('name');
document.getElementById('greeting').textContent = `Hello ${name}!`;
</script>
<!-- For rich content — sanitize -->
<div id="content"></div>
<script>
import DOMPurify from 'dompurify';
const userContent = getUserContent();
const clean = DOMPurify.sanitize(userContent, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href'],
});
document.getElementById('content').innerHTML = clean;
</script>
```
---
## A04:2021 – Insecure Design
New category focusing on risks related to design and architectural flaws. Requires threat modeling, secure design patterns, and reference architectures.
### Common Weaknesses
- **Missing Security Controls** — no rate limiting, no CAPTCHA
- **Business Logic Flaws** — discount code stacking, negative quantities
- **Insufficient Isolation** — multi-tenant data leakage
- **Weak Security Architecture** — no defense in depth
### CWE Mappings
- CWE-209: Generation of Error Message Containing Sensitive Information
- CWE-256: Plaintext Storage of a Password
- CWE-257: Storing Passwords in a Recoverable Format
- CWE-266: Incorrect Privilege Assignment
- CWE-269: Improper Privilege Management
- CWE-280: Improper Handling of Insufficient Permissions
- CWE-311: Missing Encryption of Sensitive Data
- CWE-312: Cleartext Storage of Sensitive Information
- CWE-313: Cleartext Storage in a File or on Disk
- CWE-316: Cleartext Storage of Sensitive Information in Memory
- CWE-419: Unprotected Primary Channel
- CWE-430: Deployment of Wrong Handler
- CWE-434: Unrestricted Upload of File with Dangerous Type
- CWE-444: Inconsistent Interpretation of HTTP Requests
### Vulnerability Patterns
**Missing Rate Limiting**:
```typescript
// VULNERABLE — no rate limiting
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const user = await authenticateUser(email, password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
res.json({ token: generateToken(user) });
});
// ATTACK — brute force attack
// Try thousands of passwords per second
```
**Remediation**:
```typescript
// SECURE — rate limiting with exponential backoff
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per window
skipSuccessfulRequests: true,
standardHeaders: true,
legacyHeaders: false,
handler: (req, res) => {
res.status(429).json({
error: 'Too many login attempts, please try again later',
});
},
});
app.post('/api/login', loginLimiter, async (req, res) => {
// Authentication logic
});
// BETTER — account lockout after failed attempts
const MAX_FAILED_ATTEMPTS = 5;
const LOCKOUT_DURATION = 30 * 60 * 1000; // 30 minutes
app.post('/api/login', async (req, res) => {
const { email, password } = req.body;
const account = await getAccount(email);
// Check if locked
if (account.lockedUntil && account.lockedUntil > Date.now()) {
return res.status(429).json({
error: 'Account locked. Try again later.',
});
}
const user = await authenticateUser(email, password);
if (!user) {
// Increment failed attempts
account.failedAttempts += 1;
if (account.failedAttempts >= MAX_FAILED_ATTEMPTS) {
account.lockedUntil = Date.now() + LOCKOUT_DURATION;
await saveAccount(account);
return res.status(429).json({ error: 'Account locked' });
}
await saveAccount(account);
return res.status(401).json({ error: 'Invalid credentials' });
}
// Reset on success
account.failedAttempts = 0;
account.lockedUntil = null;
await saveAccount(account);
res.json({ token: generateToken(user) });
});
```
**Business Logic Flaw — Race Condition**:
```typescript
// VULNERABLE — time-of-check to time-of-use
app.post('/api/transfer', async (req, res) => {
const { from, to, amount } = req.body;
const balance = await getBalance(from);
if (balance < amount) {
return res.status(400).json({ error: 'Insufficient funds' });
}
// RACE CONDITION — balance could be spent between check and update
await deduct(from, amount);
await credit(to, amount);
res.json({ success: true });
});
// ATTACK — send two transfer requests simultaneously
// Both pass balance check before either updates
```
**Remediation**:
```typescript
// SECURE — atomic transaction
app.post('/api/transfer', async (req, res) => {
const { from, to, amount } = req.body;
const result = await db.transaction(async (trx) => {
// Lock row for update
const account = await trx('accounts')
.where({ id: from })
.forUpdate()
.first();
if (account.balance < amount) {
throw new Error('Insufficient funds');
}
// Atomic debit/credit
await trx('accounts')
.where({ id: from })
.decrement('balance', amount);
await trx('accounts')
.where({ id: to })
.increment('balance', amount);
return { success: true };
});
res.json(result);
});
// Database-level constraint
ALTER TABLE accounts ADD CONSTRAINT positive_balance CHECK (balance >= 0);
```
---
## A05:2021 – Security Misconfiguration
### Common Weaknesses
- **Unnecessary features enabled** — debug mode in production
- **Default accounts** — admin/admin still active
- **Verbose error messages** — stack traces to users
- **Missing security headers** — no CSP, X-Frame-Options
- **Outdated software** — old framework versions
### CWE Mappings
- CWE-2: 7PK - Environment
- CWE-11: ASP.NET Misconfiguration
- CWE-13: ASP.NET Misconfiguration: Password in Configuration File
- CWE-15: External Control of System or Configuration Setting
- CWE-16: Configuration
- CWE-260: Password in Configuration File
- CWE-315: Cleartext Storage of Sensitive Information in a Cookie
- CWE-520: .NET Misconfiguration
- CWE-526: Exposure of Sensitive Information Through Environmental Variables
- CWE-537: Java Runtime Error Message Containing Sensitive Information
- CWE-541: Inclusion of Sensitive Information in an Include File
- CWE-547: Use of Hard-coded, Security-relevant Constants
- CWE-611: Improper Restriction of XML External Entity Reference
- CWE-614: Sensitive Cookie in HTTPS Session Without 'Secure' Attribute
- CWE-756: Missing Custom Error Page
- CWE-776: Improper Restriction of Recursive Entity References in DTDs
### Remediation
**Security Headers**:
```typescript
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
}));
// Additional headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});
```
---
## A06:2021 – Vulnerable and Outdated Components
### Common Weaknesses
- **Known vulnerabilities** — using libs with CVEs
- **Outdated dependencies** — years-old versions
- **No security updates** — never updating packages
- **Unused dependencies** — unnecessary attack surface
### CWE Mappings
- CWE-1035: Using Components with Known Vulnerabilities
- CWE-1104: Use of Unmaintained Third Party Components
### Remediation
```bash
# Audit dependencies
npm audit
npm audit fix
# Update outdated packages
npm outdated
npm update
# Check for known vulnerabilities
npx snyk test
# Automated dependency updates
# Use Dependabot/Renovate for automated PRs
```
---
## A07:2021 – Identification and Authentication Failures
### Common Weaknesses
- **Weak passwords** — no complexity requirements
- **Brute force** — no rate limiting
- **Session fixation** — accepting user-supplied session IDs
- **Credential stuffing** — no breach detection
- **Missing MFA** — single factor only
### CWE Mappings
- CWE-287: Improper Authentication
- CWE-288: Authentication Bypass Using an Alternate Path or Channel
- CWE-290: Authentication Bypass by Spoofing
- CWE-294: Authentication Bypass by Capture-replay
- CWE-295: Improper Certificate Validation
- CWE-297: Improper Validation of Certificate with Host Mismatch
- CWE-300: Channel Accessible by Non-Endpoint
- CWE-302: Authentication Bypass by Assumed-Immutable Data
- CWE-304: Missing Critical Step in Authentication
- CWE-306: Missing Authentication for Critical Function
- CWE-307: Improper Restriction of Excessive Authentication Attempts
- CWE-346: Origin Validation Error
- CWE-384: Session Fixation
- CWE-521: Weak Password Requirements
- CWE-613: Insufficient Session Expiration
- CWE-640: Weak Password Recovery Mechanism for Forgotten Password
- CWE-798: Use of Hard-coded Credentials
- CWE-940: Improper Verification of Source of a Communication Channel
- CWE-1216: Lockout Mechanism Errors
### Remediation
See main SKILL.md for authentication patterns.
---
## A08:2021 – Software and Data Integrity Failures
### Common Weaknesses
- **Unsigned updates** — accepting any code update
- **Insecure deserialization** — unvalidated object deserialization
- **Missing CI/CD security** — compromised build pipeline
### CWE Mappings
- CWE-345: Insufficient Verification of Data Authenticity
- CWE-353: Missing Support for Integrity Check
- CWE-426: Untrusted Search Path
- CWE-494: Download of Code Without Integrity Check
- CWE-502: Deserialization of Untrusted Data
- CWE-565: Reliance on Cookies without Validation and Integrity Checking
- CWE-784: Reliance on Cookies without Validation and Integrity Checking in a Security Decision
- CWE-829: Inclusion of Functionality from Untrusted Control Sphere
### Vulnerability Pattern
**Insecure Deserialization**:
```typescript
// VULNERABLE — deserialize untrusted data
const userData = JSON.parse(req.cookies.user);
const obj = deserialize(req.body.data); // Arbitrary code execution!
```
**Remediation**:
```typescript
// SECURE — validate structure
import { z } from 'zod';
const userSchema = z.object({
id: z.string().uuid(),
role: z.enum(['user', 'admin']),
});
const result = userSchema.safeParse(JSON.parse(req.cookies.user));
if (!result.success) {
throw new Error('Invalid user data');
}
```
---
## A09:2021 – Security Logging and Monitoring Failures
### Common Weaknesses
- **Missing audit logs** — no record of critical operations
- **Insufficient log detail** — can't reconstruct attack
- **No monitoring** — logs not reviewed
- **Insecure log storage** — logs tamper-able
### CWE Mappings
- CWE-117: Improper Output Neutralization for Logs
- CWE-223: Omission of Security-relevant Information
- CWE-532: Insertion of Sensitive Information into Log File
- CWE-778: Insufficient Logging
### Remediation
```typescript
// Log security events
logger.info('User login', {
userId: user.id,
ip: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString(),
});
logger.warn('Failed login attempt', {
email: req.body.email, // Don't log password!
ip: req.ip,
attempts: failedAttempts,
});
logger.error('Unauthorized access attempt', {
userId: req.user.id,
resource: req.path,
method: req.method,
ip: req.ip,
});
// NEVER log sensitive data
logger.info('User data', {
email: user.email,
password: '[REDACTED]',
ssn: '[REDACTED]',
creditCard: '[REDACTED]',
});
```
---
## A10:2021 – Server-Side Request Forgery (SSRF)
### Common Weaknesses
- **Unvalidated URLs** — fetching arbitrary URLs
- **Cloud metadata access** — accessing AWS/GCP metadata endpoints
- **Internal network scanning** — probing internal services
### CWE Mappings
- CWE-918: Server-Side Request Forgery (SSRF)
### Vulnerability Pattern
```typescript
// VULNERABLE — fetch arbitrary URL
app.get('/api/fetch', async (req, res) => {
const url = req.query.url;
const response = await fetch(url);
const data = await response.text();
res.send(data);
});
// ATTACK — access cloud metadata
?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
// ATTACK — scan internal network
?url=http://localhost:6379/ // Redis
?url=http://10.0.0.5:22/ // SSH
```
**Remediation**:
```typescript
// SECURE — allowlist of domains
const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];
app.get('/api/fetch', async (req, res) => {
const url = new URL(req.query.url);
// Validate domain
if (!ALLOWED_DOMAINS.includes(url.hostname)) {
return res.status(403).json({ error: 'Domain not allowed' });
}
// Block private IPs
const ip = await dns.resolve4(url.hostname);
if (isPrivateIP(ip[0])) {
return res.status(403).json({ error: 'Private IP not allowed' });
}
const response = await fetch(url.href);
const data = await response.text();
res.send(data);
});
function isPrivateIP(ip: string): boolean {
return /^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)/.test(ip)
|| ip === '::1'
|| ip.startsWith('169.254.'); // Cloud metadata
}
```
---
## Quick Reference Table
| Category | Key CWEs | Top Mitigations |
|----------|----------|-----------------|
| A01 Broken Access Control | 200, 352, 639, 918 | Server-side checks, ownership validation, CSRF tokens |
| A02 Cryptographic Failures | 259, 327, 331 | TLS, bcrypt, no hardcoded secrets, crypto.randomBytes |
| A03 Injection | 20, 79, 89 | Parameterized queries, input validation, output encoding |
| A04 Insecure Design | 209, 256, 434 | Threat modeling, rate limiting, defense in depth |
| A05 Security Misconfiguration | 16, 611, 614 | Security headers, disable debug, defaults changed |
| A06 Vulnerable Components | 1035, 1104 | npm audit, Dependabot, regular updates |
| A07 Authentication Failures | 287, 307, 521, 798 | Strong passwords, MFA, rate limiting, no defaults |
| A08 Integrity Failures | 502, 494 | Verify signatures, CI/CD hardening, schema validation |
| A09 Logging Failures | 117, 532, 778 | Audit logs, monitoring, redact sensitive data |
| A10 SSRF | 918 | URL allowlist, block private IPs, validate domains |
```
### references/review-checklist.md
```markdown
# Security Review Checklist
Complete checklist for security code review. Check each item before marking review complete.
---
## Authentication
- [ ] Passwords hashed with bcrypt/argon2 (cost >= 12)
- [ ] Session tokens cryptographically random (32+ bytes)
- [ ] Session cookies: httpOnly, secure, sameSite=strict
- [ ] Password reset tokens random + expiring (1 hour max)
- [ ] Rate limiting on login (5 attempts / 15 min)
- [ ] Account lockout after repeated failures
- [ ] MFA available for sensitive accounts
- [ ] JWT: signature verified, algorithm specified
- [ ] JWT: short expiry, refresh token rotation
- [ ] No credentials in URLs or logs
## Authorization
- [ ] All endpoints verify authentication server-side
- [ ] Resource ownership verified before access (no IDOR)
- [ ] Role checks on server, never client-only
- [ ] Principle of least privilege applied
- [ ] Admin functions require admin role server-side
- [ ] API endpoints return 403 for unauthorized, not 404
- [ ] Mass assignment prevented (explicit allowlists)
- [ ] CORS configured with explicit origins (no wildcards with credentials)
## Input Validation
- [ ] All inputs validated (type, length, format)
- [ ] SQL queries use parameterized statements
- [ ] HTML output escaped or sanitized (no raw innerHTML)
- [ ] File uploads validated (type, size, content)
- [ ] File names sanitized (path.basename)
- [ ] Path traversal prevented (prefix check after join)
- [ ] Command injection prevented (execFile, no shell)
- [ ] XML parsing disables external entities
- [ ] JSON schema validation on API inputs
## Cryptography
- [ ] No hardcoded secrets in code
- [ ] Secrets from environment variables
- [ ] Strong algorithms only (AES-256-GCM, SHA-256+)
- [ ] No MD5, SHA1, DES, ECB mode
- [ ] crypto.randomBytes for all tokens
- [ ] No Math.random for security purposes
- [ ] HTTPS enforced (no HTTP endpoints)
- [ ] TLS 1.2+ required
- [ ] Certificate validation not disabled
- [ ] Keys rotated periodically
## Data Protection
- [ ] Sensitive data encrypted at rest
- [ ] TLS 1.2+ for data in transit
- [ ] Sensitive data not logged (passwords, tokens, PII)
- [ ] Error messages generic to users, detailed in logs
- [ ] PII handling complies with regulations (GDPR, CCPA)
- [ ] Database credentials not in code
- [ ] Backups encrypted
- [ ] Data retention policies implemented
## Dependencies
- [ ] All dependencies up to date
- [ ] npm audit / cargo audit clean
- [ ] No known CVEs in dependencies
- [ ] Dependency scanning in CI/CD
- [ ] Package lock files committed
- [ ] Minimal dependency footprint
- [ ] Source verification for dependencies
- [ ] No unused dependencies
## Logging & Monitoring
- [ ] Authentication events logged (success + failure)
- [ ] Authorization failures logged
- [ ] Sensitive operations audited (admin actions, data access)
- [ ] Log entries include timestamp, user ID, IP, action
- [ ] Logs protected from tampering
- [ ] No sensitive data in logs
- [ ] Log injection prevented (sanitize user input in logs)
- [ ] Security events trigger alerts
- [ ] Incident response plan documented
## Infrastructure
- [ ] Security headers configured (helmet or equivalent)
- [ ] Content-Security-Policy
- [ ] X-Content-Type-Options: nosniff
- [ ] X-Frame-Options: DENY
- [ ] Strict-Transport-Security
- [ ] Referrer-Policy
- [ ] Debug mode disabled in production
- [ ] Default accounts/passwords changed
- [ ] Unnecessary features/endpoints disabled
- [ ] Error pages don't reveal stack traces
- [ ] Rate limiting on all public endpoints
## SSRF Prevention
- [ ] URL inputs validated against allowlist
- [ ] Private IPs blocked (10.x, 172.16-31.x, 192.168.x, 127.x, 169.254.x)
- [ ] Cloud metadata endpoints blocked (169.254.169.254)
- [ ] Redirect following disabled or validated
- [ ] DNS rebinding prevented
---
## Quick Pre-Commit Checklist
Minimum checks before any commit touching security-sensitive code:
1. [ ] No hardcoded secrets
2. [ ] Inputs validated
3. [ ] SQL parameterized
4. [ ] Auth checked server-side
5. [ ] Ownership verified for resources
6. [ ] Sensitive data not logged
7. [ ] npm audit clean
```
### references/report-templates.md
```markdown
# Security Report Templates
Templates for documenting security findings and audit reports.
---
## Individual Finding Template
```markdown
## {SEVERITY} {VULNERABILITY_NAME}
**Category**: {OWASP_CATEGORY}
**CWE**: {CWE_IDS}
**Severity**: Critical/High/Medium/Low
### Location
- File: {FILE_PATH}
- Lines: {LINE_RANGE}
- Function: {FUNCTION_NAME}
### Description
{CLEAR_EXPLANATION}
### Impact
{WHAT_ATTACKER_COULD_DO}
### Proof of Concept
{CODE_OR_STEPS_TO_EXPLOIT}
### Remediation
{SPECIFIC_FIX_WITH_CODE}
### References
- OWASP: {URL}
- CWE: {URL}
```
### Severity Indicators
Use these indicators in finding titles:
- **Critical**: Remote code execution, auth bypass, mass data exposure, admin privilege escalation
- **High**: SQL injection, stored XSS, auth weaknesses, sensitive data leaks
- **Medium**: CSRF, reflected XSS, information disclosure, weak crypto
- **Low**: Misconfigurations, missing headers, verbose errors, minor info leaks
---
## Audit Report Template
```markdown
# Security Audit Report
**Date**: {DATE}
**Scope**: {COMPONENTS_REVIEWED}
**Reviewer**: {NAME}
**Version**: {APP_VERSION}
## Executive Summary
{1-2 PARAGRAPH HIGH-LEVEL OVERVIEW}
Overall security posture: {STRONG/ADEQUATE/NEEDS_IMPROVEMENT/CRITICAL}
## Risk Summary
| Severity | Count |
|----------|-------|
| Critical | {N} |
| High | {N} |
| Medium | {N} |
| Low | {N} |
## Key Findings
### 1. {MOST_CRITICAL_FINDING}
Brief description and impact.
### 2. {SECOND_FINDING}
Brief description and impact.
### 3. {THIRD_FINDING}
Brief description and impact.
## Detailed Findings
{FULL_LIST_USING_INDIVIDUAL_FINDING_TEMPLATE}
## Recommendations
### Immediate (Critical/High)
1. {ACTION_ITEM}
2. {ACTION_ITEM}
### Short-term (Medium)
1. {ACTION_ITEM}
### Long-term (Low / Hardening)
1. {ACTION_ITEM}
## Scope & Methodology
### In Scope
- {COMPONENT_1}
- {COMPONENT_2}
### Out of Scope
- {EXCLUDED_ITEM}
### Methodology
- Threat modeling (STRIDE)
- Code review
- Dependency scanning
- {OTHER_METHODS}
## Conclusion
{OVERALL_ASSESSMENT_AND_NEXT_STEPS}
```
---
## Quick Finding Format
For inline documentation or PR comments:
```
[SEVERITY] VULN_TYPE in FILE:LINE
- Issue: {brief description}
- Impact: {what attacker could do}
- Fix: {one-line remediation}
```
Example:
```
[HIGH] SQL Injection in src/api/users.ts:45
- Issue: User email concatenated into query string
- Impact: Attacker can extract/modify database
- Fix: Use parameterized query with db.execute(sql, [email])
```
---
## Risk Matrix
Use for prioritization:
```
IMPACT
Low Med High
Low Low Low Med
LIKELIHOOD Med Low Med High
High Med High Crit
```
Factors affecting likelihood:
- Skill required to exploit
- Access required (unauth vs auth vs admin)
- Attack complexity
- User interaction needed
Factors affecting impact:
- Confidentiality (data exposure)
- Integrity (data modification)
- Availability (service disruption)
- Scope (single user vs all users vs system)
```