diff --git a/README.md b/README.md
index eaf1106..ffbd1f7 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,7 @@
**A comprehensive security reference distilled from 150+ sources to help LLMs generate safer code**
-[Landing anmd github-pages website](https://arcanum-sec.github.io/sec-context/)
-
-## The Problem
+[Landing and github-pages website](https://arcanum-sec.github.io/sec-context/)
AI coding assistants are everywhere. **97% of developers** now use AI tools, and organizations report **40%+ of their codebase** is AI-generated. But there's a critical gap: AI models consistently reproduce the same dangerous security anti-patterns, with studies showing:
@@ -36,6 +34,20 @@ Deep-dive coverage of the **7 highest-priority vulnerabilities** with:
- Complete mitigation strategies with trade-offs
- Why AI models generate these specific vulnerabilities
+### Modular Pattern Files
+Individual pattern files organized into two directories:
+
+**anti-patterns-breadth/** (61 files)
+- Each security anti-pattern as a standalone markdown file
+- Self-contained with prompts, BAD/GOOD examples, and guidance
+- Quick reference table and navigation included
+- Examples: `sql-injection.md`, `hardcoded-passwords-api-keys.md`, `reflected-xss.md`
+
+**anti-patterns-depth/** (7 files)
+- In-depth coverage of each critical vulnerability
+- Comprehensive examples, edge cases, and testing strategies
+- Examples: `hardcoded-secrets-credential-management.md`, `cross-site-scripting-xss.md`
+
---
## The Top 10 AI Code Anti-Patterns
@@ -89,11 +101,28 @@ Another ideal use case: deploy a dedicated skill in claude code that reviews AI-
- Returns specific vulnerabilities found with remediation steps
- Works as a guardrail between AI code generation and production
+### Option 5: Modular Pattern Files
+Use individual pattern files for targeted security guidance:
+
+**Usage examples:**
+- Working on SQL queries? Load `anti-patterns-breadth/sql-injection.md`
+- Building authentication? Load `anti-patterns-depth/hardcoded-secrets-credential-management.md`
+- API development? Load `anti-patterns-breadth/command-injection.md` and `anti-patterns-breadth/broken-object-level-authorization.md`
+- Deploying an agent? Configure it to retrieve relevant patterns based on code analysis
+
+**Workflow for agents:**
+1. Analyze code to identify security-relevant areas (user input, database queries, authentication, etc.)
+2. Retrieve corresponding pattern files from the modular directories
+3. Apply pattern checks to identified code sections
+4. Return targeted findings with specific remediation guidance
+
```
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────┐
│ AI Code Gen │────>│ Security Review Agent │────>│ Reviewed │
-│ (Copilot, etc.) │ │ + Anti-Patterns Guide │ │ Code Output │
-└─────────────────┘ └──────────────────────┘ └─────────────┘
+│ (Copilot, etc.) │ │ + Modular Pattern │ │ Code Output │
+└─────────────────┘ │ Files (Retrieved │ └─────────────┘
+ │ based on context) │
+ └──────────────────────┘
```
---
@@ -120,7 +149,9 @@ This guide synthesizes findings from **150+ individual sources** across 6 primar
```
├── ANTI_PATTERNS_BREADTH.md # Full coverage, 25+ patterns
-├── ANTI_PATTERNS_DEPTH.md # Deep dive, 7 critical patterns for now
+├── ANTI_PATTERNS_DEPTH.md # Deep dive, 7 critical patterns
+├── anti-patterns-breadth/ # Individual pattern files (61 files)
+└── anti-patterns-depth/ # In-depth individual patterns (7 files)
```
---
@@ -128,7 +159,11 @@ This guide synthesizes findings from **150+ individual sources** across 6 primar
## Get Started
1. **Grab the files**
-2. **Choose your approach** based on your context window and use case
+2. **Choose your approach** based on your context window and use case:
+ - **Large context models** (128K+): Use comprehensive ANTI_PATTERNS files
+ - **Smaller context models** or **targeted tasks**: Use modular pattern files
+ - **Agent deployments**: Configure to retrieve modular patterns based on code context
+ - **Learning**: Explore individual patterns to understand specific vulnerabilities
3. **Integrate** into your AI coding workflow as system prompt, RAG reference, or review agent
4. **Generate safer code**
diff --git a/anti-patterns-breadth/INSTRUCTIONS.md b/anti-patterns-breadth/INSTRUCTIONS.md
new file mode 100644
index 0000000..6f3f6b6
--- /dev/null
+++ b/anti-patterns-breadth/INSTRUCTIONS.md
@@ -0,0 +1,24 @@
+## INSTRUCTIONS FOR AI/LLM
+
+This document contains security anti-patterns you MUST avoid when generating code.
+Each section shows a VULNERABLE pattern (marked BAD) and a SECURE alternative (marked GOOD).
+All examples are in PSEUDOCODE - implement the secure patterns in whatever language
+the developer is using. When in doubt, choose the more secure option.
+
+**Key Statistics:**
+- AI-generated code has an **86% XSS failure rate** (vs 31.6% human code)
+- **5-21% of AI-suggested packages don't exist** (slopsquatting risk)
+- AI code is **2.74x more likely** to have XSS vulnerabilities
+- **21.7% hallucination rate** for package names in some domains
+- SQL injection patterns appeared "thousands of times" in AI training data
+
+**Before generating any code:**
+1. Never hardcode credentials, API keys, or secrets
+2. Always parameterize database queries
+3. Validate and sanitize all user input
+4. Use cryptographically secure random for security tokens
+5. Verify packages exist before suggesting imports
+6. Encode output for the appropriate context (HTML, URL, JS)
+
+---
+
diff --git a/anti-patterns-breadth/README.md b/anti-patterns-breadth/README.md
new file mode 100644
index 0000000..dee0dec
--- /dev/null
+++ b/anti-patterns-breadth/README.md
@@ -0,0 +1,115 @@
+# Anti-Patterns Breadth - Comprehensive Security Coverage
+
+This directory contains a comprehensive collection of security anti-patterns extracted from the original ANTI_PATTERNS_BREADTH.md file. Each pattern is now stored as an individual file for easier reference and use.
+
+## Overview
+
+This collection covers 10 major categories of security vulnerabilities with detailed examples of bad practices (BAD) and secure alternatives (GOOD). Each file includes:
+- Description of the vulnerability
+- Common CWE references
+- Severity indicators
+- BAD code examples showing the anti-pattern
+- GOOD code examples showing secure practices
+- Related security considerations
+
+## Quick Reference
+
+- [Quick Reference Table](quick-reference-table.md) - Overview of all patterns
+- [Instructions for AI/LLM](INSTRUCTIONS.md) - How to use these patterns
+
+## Patterns by Category
+
+### 1. Secrets and Credentials Management
+- [Hardcoded Passwords and API Keys](hardcoded-passwords-api-keys.md)
+- [Credentials in Configuration Files](credentials-in-config-files.md)
+- [Secrets in Client-Side Code](secrets-in-client-side-code.md)
+- [Insecure Credential Storage](insecure-credential-storage.md)
+- [Missing Secret Rotation Considerations](missing-secret-rotation.md)
+
+### 2. Injection Vulnerabilities
+- [SQL Injection](sql-injection.md)
+- [Command Injection](command-injection.md)
+- [LDAP Injection](ldap-injection.md)
+- [XPath Injection](xpath-injection.md)
+- [NoSQL Injection](nosql-injection.md)
+- [Template Injection](template-injection.md)
+
+### 3. Cross-Site Scripting (XSS)
+- [Reflected XSS](reflected-xss.md)
+- [Stored XSS](stored-xss.md)
+- [DOM-Based XSS](dom-based-xss.md)
+- [Missing Content-Security-Policy](missing-content-security-policy.md)
+- [Improper Output Encoding](improper-output-encoding.md)
+
+### 4. Authentication and Session Management
+- [Weak Password Requirements](weak-password-requirements.md)
+- [Missing Rate Limiting on Auth Endpoints](missing-rate-limiting-auth.md)
+- [Insecure Session Token Generation](insecure-session-token-generation.md)
+- [Session Fixation Vulnerabilities](session-fixation.md)
+- [JWT Misuse](jwt-misuse.md)
+- [Missing MFA Considerations](missing-mfa.md)
+- [Insecure Password Reset Flows](insecure-password-reset.md)
+
+### 5. Cryptographic Failures
+- [Using Deprecated Algorithms](deprecated-crypto-algorithms.md)
+- [Hardcoded Encryption Keys](hardcoded-encryption-keys.md)
+- [ECB Mode Usage](ecb-mode-usage.md)
+- [Missing or Weak IVs/Nonces](missing-weak-ivs-nonces.md)
+- [Rolling Your Own Crypto](rolling-your-own-crypto.md)
+- [Insecure Random Number Generation](insecure-random-generation.md)
+- [Improper Key Derivation](improper-key-derivation.md)
+
+### 6. Input Validation
+- [Missing Server-Side Validation](missing-server-side-validation.md)
+- [Improper Type Checking](improper-type-checking.md)
+- [Missing Length Limits](missing-length-limits.md)
+- [Regex Denial of Service (ReDoS)](regex-denial-of-service.md)
+- [Accepting and Processing Untrusted Data](accepting-untrusted-data.md)
+- [Missing Canonicalization](missing-canonicalization.md)
+
+### 7. Configuration and Deployment
+- [Debug Mode in Production](debug-mode-in-production.md)
+- [Verbose Error Messages](verbose-error-messages.md)
+- [Default Credentials](default-credentials.md)
+- [Insecure CORS Configuration](insecure-cors-configuration.md)
+- [Missing Security Headers](missing-security-headers.md)
+- [Exposed Admin Interfaces](exposed-admin-interfaces.md)
+- [Unnecessary Open Ports and Services](unnecessary-open-ports.md)
+
+### 8. Dependency and Supply Chain Security
+- [Using Outdated Packages](outdated-packages.md)
+- [Not Pinning Dependency Versions](not-pinning-dependency-versions.md)
+- [Typosquatting and Slopsquatting Risks](typosquatting-risks.md)
+- [Including Unnecessary Dependencies](unnecessary-dependencies.md)
+- [Missing Integrity Checks](missing-integrity-checks.md)
+- [Trusting Transitive Dependencies Blindly](trusting-transitive-dependencies.md)
+
+### 9. API Security
+- [Missing Authentication on Endpoints](missing-authentication-endpoints.md)
+- [Broken Object-Level Authorization (IDOR)](broken-object-level-authorization.md)
+- [Mass Assignment Vulnerabilities](mass-assignment.md)
+- [Excessive Data Exposure](excessive-data-exposure.md)
+- [Missing Rate Limiting](missing-rate-limiting-api.md)
+- [Improper Error Handling in APIs](improper-error-handling-api.md)
+
+### 10. File Handling
+- [Path Traversal Vulnerabilities](path-traversal.md)
+- [Unrestricted File Uploads](unrestricted-file-uploads.md)
+- [Missing File Type Validation](missing-file-type-validation.md)
+- [Insecure Temporary File Handling](insecure-temporary-file-handling.md)
+- [Symlink Vulnerabilities](symlink-vulnerabilities.md)
+- [Unsafe File Permissions](unsafe-file-permissions.md)
+
+## How to Use This Collection
+
+Each file in this directory is self-contained and includes:
+- Pattern description and context
+- BAD examples showing vulnerable code
+- GOOD examples showing secure alternatives
+- Security considerations and best practices
+
+You can reference individual files based on specific security concerns, or use the entire collection as a comprehensive security reference.
+
+## Source
+
+This content was extracted from the original `ANTI_PATTERNS_BREADTH.md` file to provide better organization and easier access to individual patterns.
diff --git a/anti-patterns-breadth/accepting-untrusted-data.md b/anti-patterns-breadth/accepting-untrusted-data.md
new file mode 100644
index 0000000..f13afc6
--- /dev/null
+++ b/anti-patterns-breadth/accepting-untrusted-data.md
@@ -0,0 +1,139 @@
+# Accepting and Processing Untrusted Data
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Trusting external data sources
+// ========================================
+FUNCTION process_webhook_unsafe(request):
+ // Vulnerable: No signature verification
+ data = json.parse(request.body)
+
+ // Attacker can spoof webhook requests
+ IF data.event == "payment_completed":
+ mark_order_paid(data.order_id) // Dangerous!
+ END IF
+END FUNCTION
+
+FUNCTION fetch_and_process_unsafe(url):
+ // Vulnerable: Processing arbitrary external content
+ response = http.get(url)
+ data = json.parse(response.body)
+
+ // No validation of response structure
+ database.insert("external_data", data)
+END FUNCTION
+
+FUNCTION deserialize_unsafe(serialized_data):
+ // Vulnerable: Pickle/eval deserialization of untrusted data
+ // Allows arbitrary code execution!
+ object = pickle.loads(serialized_data)
+ RETURN object
+END FUNCTION
+
+FUNCTION process_xml_unsafe(xml_string):
+ // Vulnerable: XXE (XML External Entity) attack
+ parser = xml.create_parser()
+ doc = parser.parse(xml_string)
+ // Attacker XML:
+ RETURN doc
+END FUNCTION
+
+// ========================================
+// GOOD: Validate and sanitize external data
+// ========================================
+FUNCTION process_webhook_safe(request):
+ // Verify webhook signature
+ signature = request.headers.get("X-Signature")
+ expected = hmac_sha256(WEBHOOK_SECRET, request.raw_body)
+
+ IF NOT constant_time_compare(signature, expected):
+ log.warning("Invalid webhook signature", {ip: request.ip})
+ RETURN {status: 401, error: "Invalid signature"}
+ END IF
+
+ // Validate payload structure
+ data = json.parse(request.body)
+
+ IF NOT validate_webhook_schema(data):
+ RETURN {status: 400, error: "Invalid payload"}
+ END IF
+
+ // Process verified and validated data
+ IF data.event == "payment_completed":
+ // Additional verification: Check with payment provider
+ IF verify_payment_with_provider(data.payment_id):
+ mark_order_paid(data.order_id)
+ END IF
+ END IF
+END FUNCTION
+
+FUNCTION fetch_and_process_safe(url):
+ // Validate URL is from allowed sources
+ parsed_url = url_parser.parse(url)
+ IF parsed_url.host NOT IN ALLOWED_HOSTS:
+ THROW ValidationError("URL host not allowed")
+ END IF
+
+ // Fetch with timeout and size limits
+ response = http.get(url, timeout=10, max_size=1024*1024)
+
+ // Parse and validate structure
+ TRY:
+ data = json.parse(response.body)
+ CATCH JSONError:
+ THROW ValidationError("Invalid JSON response")
+ END TRY
+
+ // Validate against expected schema
+ validated_data = validate_schema(data, EXPECTED_SCHEMA)
+
+ // Sanitize before storing
+ sanitized = sanitize_object(validated_data)
+ database.insert("external_data", sanitized)
+END FUNCTION
+
+FUNCTION deserialize_safe(data, format):
+ // Never use pickle/eval for untrusted data
+ // Use safe serialization formats
+ IF format == "json":
+ RETURN json.parse(data)
+ ELSE IF format == "msgpack":
+ RETURN msgpack.unpack(data)
+ ELSE:
+ THROW Error("Unsupported format")
+ END IF
+END FUNCTION
+
+FUNCTION process_xml_safe(xml_string):
+ // Disable external entities and DTDs
+ parser = xml.create_parser(
+ resolve_entities=FALSE,
+ load_dtd=FALSE,
+ no_network=TRUE
+ )
+
+ TRY:
+ doc = parser.parse(xml_string)
+ RETURN doc
+ CATCH XMLError as e:
+ log.warning("XML parsing failed", {error: e.message})
+ THROW ValidationError("Invalid XML")
+ END TRY
+END FUNCTION
+
+// Schema validation helper
+FUNCTION validate_schema(data, schema):
+ // Use JSON Schema or similar validation library
+ validator = JsonSchemaValidator(schema)
+
+ IF NOT validator.is_valid(data):
+ errors = validator.get_errors()
+ THROW ValidationError("Schema validation failed: " + errors.join(", "))
+ END IF
+
+ RETURN data
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/broken-object-level-authorization.md b/anti-patterns-breadth/broken-object-level-authorization.md
new file mode 100644
index 0000000..b9ed972
--- /dev/null
+++ b/anti-patterns-breadth/broken-object-level-authorization.md
@@ -0,0 +1,184 @@
+# Broken Object-Level Authorization (IDOR)
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: IDOR vulnerabilities - no ownership check
+// ========================================
+
+// Attacker changes user_id in URL to access others' data
+@route("/api/users/{user_id}/profile")
+@require_auth
+FUNCTION get_user_profile(request, user_id):
+ // VULNERABLE: No check that user_id belongs to authenticated user
+ profile = database.get_profile(user_id)
+ RETURN response(200, profile)
+END FUNCTION
+
+// Attacker can delete any order by changing order_id
+@route("/api/orders/{order_id}")
+@require_auth
+FUNCTION delete_order(request, order_id):
+ // VULNERABLE: Deletes any order regardless of owner
+ database.delete("orders", order_id)
+ RETURN response(200, {status: "deleted"})
+END FUNCTION
+
+// Attacker accesses any document by guessing/incrementing ID
+@route("/api/documents/{doc_id}")
+@require_auth
+FUNCTION get_document(request, doc_id):
+ // VULNERABLE: Sequential IDs make enumeration easy
+ doc = database.get_document(doc_id)
+ RETURN response(200, doc)
+END FUNCTION
+
+// Horizontal privilege escalation via parameter tampering
+@route("/api/transfer")
+@require_auth
+FUNCTION transfer_funds(request):
+ // VULNERABLE: from_account comes from user input
+ from_account = request.body.from_account
+ to_account = request.body.to_account
+ amount = request.body.amount
+
+ execute_transfer(from_account, to_account, amount)
+ RETURN response(200, {status: "transferred"})
+END FUNCTION
+
+// ========================================
+// GOOD: Proper object-level authorization
+// ========================================
+
+// Always verify ownership before access
+@route("/api/users/{user_id}/profile")
+@require_auth
+FUNCTION get_user_profile(request, user_id):
+ // SECURE: Verify user can only access their own profile
+ IF user_id != request.user.id AND request.user.role != "admin":
+ log.security("IDOR attempt blocked", {
+ authenticated_user: request.user.id,
+ attempted_access: user_id
+ })
+ RETURN response(403, {error: "Access denied"})
+ END IF
+
+ profile = database.get_profile(user_id)
+ IF profile IS NULL:
+ RETURN response(404, {error: "Profile not found"})
+ END IF
+
+ RETURN response(200, profile)
+END FUNCTION
+
+// Resource ownership verification
+@route("/api/orders/{order_id}")
+@require_auth
+FUNCTION delete_order(request, order_id):
+ order = database.get_order(order_id)
+
+ IF order IS NULL:
+ RETURN response(404, {error: "Order not found"})
+ END IF
+
+ // SECURE: Verify ownership before action
+ IF order.user_id != request.user.id:
+ log.security("Unauthorized order deletion attempt", {
+ user_id: request.user.id,
+ order_id: order_id,
+ owner_id: order.user_id
+ })
+ RETURN response(403, {error: "Access denied"})
+ END IF
+
+ // Additional business logic check
+ IF order.status == "shipped":
+ RETURN response(400, {error: "Cannot delete shipped orders"})
+ END IF
+
+ database.delete("orders", order_id)
+ RETURN response(200, {status: "deleted"})
+END FUNCTION
+
+// Use UUIDs instead of sequential IDs to prevent enumeration
+FUNCTION create_document(request):
+ doc_id = generate_uuid() // Not sequential, not guessable
+
+ database.insert("documents", {
+ id: doc_id,
+ owner_id: request.user.id,
+ content: request.body.content
+ })
+
+ RETURN response(201, {id: doc_id})
+END FUNCTION
+
+// Implicit ownership from authenticated user
+@route("/api/transfer")
+@require_auth
+FUNCTION transfer_funds(request):
+ // SECURE: from_account MUST belong to authenticated user
+ from_account = database.get_account(request.body.from_account)
+
+ IF from_account IS NULL OR from_account.owner_id != request.user.id:
+ RETURN response(403, {error: "Invalid source account"})
+ END IF
+
+ to_account = database.get_account(request.body.to_account)
+ IF to_account IS NULL:
+ RETURN response(404, {error: "Destination account not found"})
+ END IF
+
+ amount = request.body.amount
+ IF amount <= 0 OR amount > from_account.balance:
+ RETURN response(400, {error: "Invalid amount"})
+ END IF
+
+ execute_transfer(from_account.id, to_account.id, amount)
+
+ log.audit("Funds transfer", {
+ user_id: request.user.id,
+ from: from_account.id,
+ to: to_account.id,
+ amount: amount
+ })
+
+ RETURN response(200, {status: "transferred"})
+END FUNCTION
+
+// Reusable authorization decorator
+FUNCTION authorize_resource(resource_type, id_param):
+ RETURN FUNCTION decorator(handler):
+ RETURN FUNCTION wrapped(request):
+ resource_id = request.params[id_param]
+ resource = database.get(resource_type, resource_id)
+
+ IF resource IS NULL:
+ RETURN response(404, {error: resource_type + " not found"})
+ END IF
+
+ IF NOT can_access(request.user, resource):
+ log.security("Authorization failed", {
+ user_id: request.user.id,
+ resource_type: resource_type,
+ resource_id: resource_id
+ })
+ RETURN response(403, {error: "Access denied"})
+ END IF
+
+ request.resource = resource
+ RETURN handler(request)
+ END FUNCTION
+ END FUNCTION
+END FUNCTION
+
+// Usage
+@route("/api/documents/{doc_id}")
+@require_auth
+@authorize_resource("documents", "doc_id")
+FUNCTION get_document(request, doc_id):
+ RETURN response(200, request.resource) // Already verified
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/command-injection.md b/anti-patterns-breadth/command-injection.md
new file mode 100644
index 0000000..6d0c24d
--- /dev/null
+++ b/anti-patterns-breadth/command-injection.md
@@ -0,0 +1,61 @@
+# Command Injection (Unsanitized Shell Commands)
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Shell command with user input
+// ========================================
+FUNCTION ping_host(hostname):
+ // Vulnerable: User controls shell command
+ command = "ping -c 4 " + hostname
+ RETURN shell.execute(command)
+END FUNCTION
+
+FUNCTION convert_file(input_path, output_format):
+ // Vulnerable: Multiple injection points
+ command = "convert " + input_path + " output." + output_format
+ RETURN shell.execute(command)
+END FUNCTION
+
+// Attack: hostname = "google.com; rm -rf /"
+// Result: ping -c 4 google.com; rm -rf /
+// This executes the ping AND deletes the filesystem
+
+// ========================================
+// GOOD: Use argument arrays, avoid shell
+// ========================================
+FUNCTION ping_host(hostname):
+ // Validate input format first
+ IF NOT is_valid_hostname(hostname):
+ THROW Error("Invalid hostname format")
+ END IF
+
+ // Safe: Arguments passed as array, no shell interpolation
+ RETURN process.execute(["ping", "-c", "4", hostname], shell=FALSE)
+END FUNCTION
+
+FUNCTION convert_file(input_path, output_format):
+ // Validate allowed formats
+ allowed_formats = ["png", "jpg", "gif", "webp"]
+ IF output_format NOT IN allowed_formats:
+ THROW Error("Invalid output format")
+ END IF
+
+ // Validate path is within allowed directory
+ IF NOT path.is_within(input_path, UPLOAD_DIRECTORY):
+ THROW Error("Invalid file path")
+ END IF
+
+ output_path = path.join(OUTPUT_DIR, "output." + output_format)
+ RETURN process.execute(["convert", input_path, output_path], shell=FALSE)
+END FUNCTION
+
+// Helper: Validate hostname format
+FUNCTION is_valid_hostname(hostname):
+ // Only allow alphanumeric, dots, and hyphens
+ pattern = "^[a-zA-Z0-9][a-zA-Z0-9.-]{0,253}[a-zA-Z0-9]$"
+ RETURN regex.match(pattern, hostname)
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/credentials-in-config-files.md b/anti-patterns-breadth/credentials-in-config-files.md
new file mode 100644
index 0000000..246357f
--- /dev/null
+++ b/anti-patterns-breadth/credentials-in-config-files.md
@@ -0,0 +1,48 @@
+# Credentials in Configuration Files
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Credentials in config committed to repo
+// ========================================
+// config.json (tracked in git)
+{
+ "database_url": "postgresql://admin:password123@localhost:5432/mydb",
+ "redis_password": "redis_secret_123",
+ "smtp_password": "mail_password"
+}
+
+FUNCTION connect_database():
+ config = load_json("config.json")
+ connection = database.connect(config.database_url)
+ RETURN connection
+END FUNCTION
+
+// ========================================
+// GOOD: External secret management
+// ========================================
+// config.json (no secrets, safe to commit)
+{
+ "database_host": "localhost",
+ "database_port": 5432,
+ "database_name": "mydb"
+}
+
+FUNCTION connect_database():
+ config = load_json("config.json")
+
+ // Credentials from environment or secret manager
+ db_user = environment.get("DB_USER")
+ db_password = environment.get("DB_PASSWORD")
+
+ IF db_user IS NULL OR db_password IS NULL:
+ THROW Error("Database credentials not configured")
+ END IF
+
+ url = "postgresql://" + db_user + ":" + db_password + "@" +
+ config.database_host + ":" + config.database_port + "/" + config.database_name
+ RETURN database.connect(url)
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/debug-mode-in-production.md b/anti-patterns-breadth/debug-mode-in-production.md
new file mode 100644
index 0000000..34fc810
--- /dev/null
+++ b/anti-patterns-breadth/debug-mode-in-production.md
@@ -0,0 +1,70 @@
+# Debug Mode in Production
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Debug mode enabled in production
+// ========================================
+
+// Mistake 1: Hardcoded debug flag
+CONSTANT DEBUG = TRUE // Never changes between environments
+
+FUNCTION start_application():
+ app.config.debug = TRUE
+ app.config.show_stack_traces = TRUE
+ app.config.enable_profiler = TRUE
+
+ // Exposes: full stack traces, variable values, file paths, database queries
+ app.run()
+END FUNCTION
+
+// Mistake 2: Debug routes left enabled
+app.route("/debug/env", show_environment_variables)
+app.route("/debug/config", show_all_config)
+app.route("/debug/sql", run_arbitrary_sql) // Catastrophic!
+
+// Mistake 3: Development tools in production bundle
+// package.json or requirements with dev dependencies in production
+// React DevTools, Vue DevTools, Django Debug Toolbar exposed
+
+// ========================================
+// GOOD: Environment-based configuration
+// ========================================
+
+FUNCTION start_application():
+ environment = get_environment_variable("APP_ENV", "production")
+
+ IF environment == "production":
+ app.config.debug = FALSE
+ app.config.show_stack_traces = FALSE
+ app.config.enable_profiler = FALSE
+
+ // Ensure debug routes are not registered
+ disable_debug_routes()
+
+ ELSE IF environment == "development":
+ // Only enable debug in development
+ app.config.debug = TRUE
+ register_debug_routes()
+ END IF
+
+ app.run()
+END FUNCTION
+
+FUNCTION disable_debug_routes():
+ // Explicitly remove or disable debug endpoints
+ // Better: Don't register them in production at all
+
+ debug_routes = ["/debug/*", "/test/*", "/__debug__/*", "/profiler/*"]
+ FOR route IN debug_routes:
+ app.remove_route(route)
+ END FOR
+END FUNCTION
+
+// Build process should exclude dev dependencies
+// package.json: use --production flag
+// requirements.txt: separate dev-requirements.txt
+// Dockerfile: multi-stage build without dev tools
+```
diff --git a/anti-patterns-breadth/default-credentials.md b/anti-patterns-breadth/default-credentials.md
new file mode 100644
index 0000000..3bb7065
--- /dev/null
+++ b/anti-patterns-breadth/default-credentials.md
@@ -0,0 +1,116 @@
+# Default Credentials
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Default credentials in code or config
+// ========================================
+
+// Mistake 1: Hardcoded default admin account
+FUNCTION initialize_database():
+ IF NOT user_exists("admin"):
+ create_user({
+ username: "admin",
+ password: "admin", // First thing attackers try
+ role: "administrator"
+ })
+ END IF
+END FUNCTION
+
+// Mistake 2: Default passwords in configuration
+config = {
+ database: {
+ host: "localhost",
+ user: "root",
+ password: "root" // Default MySQL credentials
+ },
+ redis: {
+ password: "" // No password = open to network
+ },
+ admin_panel: {
+ secret_key: "change_me" // Never changed
+ }
+}
+
+// Mistake 3: API keys with placeholder values
+CONSTANT API_KEY = "YOUR_API_KEY_HERE" // Developers forget to change
+CONSTANT WEBHOOK_SECRET = "test123"
+
+// ========================================
+// GOOD: Require explicit configuration, no defaults
+// ========================================
+
+FUNCTION initialize_application():
+ // Require all sensitive config to be explicitly set
+ required_config = [
+ "DATABASE_PASSWORD",
+ "REDIS_PASSWORD",
+ "SECRET_KEY",
+ "API_KEY"
+ ]
+
+ FOR config_name IN required_config:
+ value = get_environment_variable(config_name)
+
+ IF value IS NULL OR value == "":
+ THROW ConfigurationError(
+ config_name + " must be set in environment"
+ )
+ END IF
+
+ // Check for common placeholder values
+ placeholder_patterns = ["change_me", "your_", "test", "example", "xxx"]
+ FOR pattern IN placeholder_patterns:
+ IF value.lower().contains(pattern):
+ THROW ConfigurationError(
+ config_name + " appears to contain a placeholder value"
+ )
+ END IF
+ END FOR
+ END FOR
+END FUNCTION
+
+FUNCTION initialize_database():
+ // Never create default admin accounts automatically
+ // Instead, require explicit admin creation with strong password
+
+ IF NOT admin_exists():
+ IF environment == "development":
+ log.warning("No admin account exists. Run: create_admin_account command")
+ ELSE:
+ log.error("No admin account configured for production")
+ THROW ConfigurationError("Admin account must be created before deployment")
+ END IF
+ END IF
+END FUNCTION
+
+// First-run setup requires strong credentials
+FUNCTION create_initial_admin(username, password):
+ // Validate password strength
+ IF NOT is_strong_password(password):
+ THROW ValidationError("Admin password must meet complexity requirements")
+ END IF
+
+ // Hash password properly
+ hashed = bcrypt.hash(password, rounds=12)
+
+ create_user({
+ username: username,
+ password_hash: hashed,
+ role: "administrator",
+ requires_password_change: TRUE // Force change on first login
+ })
+END FUNCTION
+
+// Service accounts should use key-based auth, not passwords
+FUNCTION configure_service_connections():
+ // Use certificate-based auth for databases where possible
+ database.connect({
+ ssl_cert: load_file(env.get("DB_CLIENT_CERT")),
+ ssl_key: load_file(env.get("DB_CLIENT_KEY")),
+ ssl_ca: load_file(env.get("DB_CA_CERT"))
+ })
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/deprecated-crypto-algorithms.md b/anti-patterns-breadth/deprecated-crypto-algorithms.md
new file mode 100644
index 0000000..cd083fc
--- /dev/null
+++ b/anti-patterns-breadth/deprecated-crypto-algorithms.md
@@ -0,0 +1,58 @@
+# Using Deprecated Algorithms (MD5, SHA1 for Security, DES)
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Deprecated hash algorithms for security
+// ========================================
+FUNCTION hash_password_weak(password):
+ // Vulnerable: MD5 is cryptographically broken
+ RETURN md5(password)
+END FUNCTION
+
+FUNCTION verify_integrity_weak(data):
+ // Vulnerable: SHA-1 has known collision attacks
+ RETURN sha1(data)
+END FUNCTION
+
+FUNCTION encrypt_data_weak(plaintext, key):
+ // Vulnerable: DES uses 56-bit keys (trivially breakable)
+ cipher = DES.new(key, mode=ECB)
+ RETURN cipher.encrypt(plaintext)
+END FUNCTION
+
+// Problems:
+// - MD5: Collisions found in seconds, rainbow tables widely available
+// - SHA-1: Collision attacks demonstrated (SHAttered, 2017)
+// - DES: Brute-forceable in hours with modern hardware
+
+// ========================================
+// GOOD: Modern cryptographic algorithms
+// ========================================
+FUNCTION hash_password_secure(password):
+ // Use bcrypt, Argon2, or scrypt for passwords
+ salt = bcrypt.generate_salt(rounds=12)
+ RETURN bcrypt.hash(password, salt)
+END FUNCTION
+
+FUNCTION verify_integrity_secure(data):
+ // Use SHA-256, SHA-3, or BLAKE2 for integrity
+ RETURN sha256(data)
+END FUNCTION
+
+FUNCTION encrypt_data_secure(plaintext, key):
+ // Use AES-256-GCM or ChaCha20-Poly1305
+ nonce = crypto.secure_random_bytes(12)
+ cipher = AES_GCM.new(key, nonce)
+ ciphertext, tag = cipher.encrypt_and_digest(plaintext)
+ RETURN nonce + tag + ciphertext // Include nonce and auth tag
+END FUNCTION
+
+// Algorithm selection guide:
+// - Password hashing: bcrypt, Argon2id, scrypt (NOT SHA-256 alone)
+// - Symmetric encryption: AES-256-GCM, ChaCha20-Poly1305
+// - Integrity/checksums: SHA-256, SHA-3, BLAKE2
+// - Signatures: Ed25519, ECDSA with P-256, RSA-2048+
+```
diff --git a/anti-patterns-breadth/dom-based-xss.md b/anti-patterns-breadth/dom-based-xss.md
new file mode 100644
index 0000000..07691d0
--- /dev/null
+++ b/anti-patterns-breadth/dom-based-xss.md
@@ -0,0 +1,78 @@
+# DOM-Based XSS (innerHTML, document.write)
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Dangerous DOM manipulation methods
+// ========================================
+FUNCTION display_welcome_message():
+ // Vulnerable: URL parameter into innerHTML
+ params = parse_url_parameters(window.location.search)
+ username = params.get("name")
+
+ document.getElementById("welcome").innerHTML =
+ "Welcome, " + username + "!"
+END FUNCTION
+
+FUNCTION update_content(user_content):
+ // Vulnerable: User content via innerHTML
+ document.getElementById("content").innerHTML = user_content
+END FUNCTION
+
+FUNCTION load_dynamic_script(url):
+ // Dangerous: document.write with external content
+ document.write("")
+END FUNCTION
+
+// Attack: ?name=
+// Result: XSS via event handler, bypasses simple "
+ RETURN script
+END FUNCTION
+
+FUNCTION render_url_parameter(user_input):
+ // Vulnerable: No URL encoding
+ url = "https://example.com/page?data=" + user_input
+ RETURN "Link"
+END FUNCTION
+
+FUNCTION render_css_value(user_color):
+ // Vulnerable: No CSS encoding
+ style = "
Text
"
+ RETURN style
+END FUNCTION
+
+// Attack on JS context: User input = "'; alert(1); //'"
+// Result: var userData = ''; alert(1); //''; - Script injection
+
+// ========================================
+// GOOD: Context-specific encoding
+// ========================================
+
+// JavaScript string context
+FUNCTION js_encode(input):
+ result = input
+ result = result.replace("\\", "\\\\")
+ result = result.replace("'", "\\'")
+ result = result.replace('"', '\\"')
+ result = result.replace("\n", "\\n")
+ result = result.replace("\r", "\\r")
+ result = result.replace("<", "\\x3c") // Prevent breakout
+ result = result.replace(">", "\\x3e")
+ RETURN result
+END FUNCTION
+
+FUNCTION render_javascript_variable(user_input):
+ // Safe: Proper JavaScript encoding
+ safe_for_js = js_encode(user_input)
+
+ script = ""
+ RETURN script
+END FUNCTION
+
+// Better: Use JSON encoding for complex data
+FUNCTION render_javascript_data(user_data):
+ // Safest: JSON encoding handles all edge cases
+ json_data = json_encode(user_data)
+
+ script = ""
+ RETURN script
+END FUNCTION
+
+// URL context
+FUNCTION render_url_parameter(user_input):
+ // Safe: URL encoding
+ encoded_param = url_encode(user_input)
+ url = "https://example.com/page?data=" + encoded_param
+
+ // Also HTML-encode the entire URL for the href attribute
+ RETURN "Link"
+END FUNCTION
+
+// CSS context
+FUNCTION css_encode(input):
+ // Only allow safe CSS values
+ allowed_pattern = "^[a-zA-Z0-9#]+$"
+ IF NOT regex.match(allowed_pattern, input):
+ RETURN "inherit" // Safe default
+ END IF
+ RETURN input
+END FUNCTION
+
+FUNCTION render_css_value(user_color):
+ // Safe: Validate and encode CSS value
+ safe_color = css_encode(user_color)
+ style = "Text
"
+ RETURN style
+END FUNCTION
+
+// HTML attribute context
+FUNCTION render_attribute(attr_name, user_value):
+ // HTML-encode and quote attribute value
+ safe_value = html_encode(user_value)
+ RETURN attr_name + '="' + safe_value + '"'
+END FUNCTION
+```
+
+---
diff --git a/anti-patterns-breadth/improper-type-checking.md b/anti-patterns-breadth/improper-type-checking.md
new file mode 100644
index 0000000..688375d
--- /dev/null
+++ b/anti-patterns-breadth/improper-type-checking.md
@@ -0,0 +1,111 @@
+# Improper Type Checking
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Missing or weak type validation
+// ========================================
+FUNCTION process_payment_weak(request):
+ amount = request.body.amount
+ quantity = request.body.quantity
+
+ // Vulnerable: No type checking
+ total = amount * quantity
+
+ // What if amount = "100" (string)? JavaScript: "100" * 2 = 200 (coerced)
+ // What if amount = [100]? Some languages coerce arrays unexpectedly
+ // What if quantity = {"$gt": 0}? NoSQL injection possible
+
+ charge_card(user, total)
+END FUNCTION
+
+FUNCTION get_user_weak(request):
+ user_id = request.params.id
+
+ // Vulnerable: ID could be array, object, or unexpected type
+ // MongoDB: ?id[$ne]=null returns all users!
+ RETURN database.find_one({id: user_id})
+END FUNCTION
+
+FUNCTION calculate_discount_weak(price, discount_percent):
+ // Vulnerable: No validation of numeric types
+ // discount_percent = "50" → string concatenation in some languages
+ // discount_percent = NaN → NaN propagates through calculations
+ final_price = price - (price * discount_percent / 100)
+ RETURN final_price
+END FUNCTION
+
+// ========================================
+// GOOD: Strict type validation
+// ========================================
+FUNCTION process_payment_safe(request):
+ // Validate amount
+ amount = request.body.amount
+ IF typeof(amount) != "number":
+ THROW ValidationError("Amount must be a number")
+ END IF
+ IF NOT is_finite(amount) OR is_nan(amount):
+ THROW ValidationError("Amount must be a valid number")
+ END IF
+ IF amount <= 0:
+ THROW ValidationError("Amount must be positive")
+ END IF
+
+ // Validate quantity
+ quantity = request.body.quantity
+ IF typeof(quantity) != "number" OR NOT is_integer(quantity):
+ THROW ValidationError("Quantity must be an integer")
+ END IF
+ IF quantity <= 0 OR quantity > 1000:
+ THROW ValidationError("Quantity must be between 1 and 1000")
+ END IF
+
+ // Safe to calculate
+ total = amount * quantity
+
+ // Additional: Prevent floating point issues with currency
+ total_cents = round(total * 100) // Work in cents
+ charge_card(user, total_cents)
+END FUNCTION
+
+FUNCTION get_user_safe(request):
+ user_id = request.params.id
+
+ // Strict type checking
+ IF typeof(user_id) != "string":
+ THROW ValidationError("User ID must be a string")
+ END IF
+
+ // Format validation (e.g., UUID)
+ IF NOT is_valid_uuid(user_id):
+ THROW ValidationError("Invalid user ID format")
+ END IF
+
+ RETURN database.find_one({id: user_id})
+END FUNCTION
+
+// Type coercion helper with explicit validation
+FUNCTION parse_integer_strict(value, min, max):
+ IF typeof(value) == "number":
+ IF NOT is_integer(value):
+ THROW ValidationError("Expected integer, got float")
+ END IF
+ result = value
+ ELSE IF typeof(value) == "string":
+ IF NOT regex.match("^-?[0-9]+$", value):
+ THROW ValidationError("Invalid integer format")
+ END IF
+ result = parse_int(value)
+ ELSE:
+ THROW ValidationError("Expected number or numeric string")
+ END IF
+
+ IF result < min OR result > max:
+ THROW ValidationError("Value out of range: " + min + " to " + max)
+ END IF
+
+ RETURN result
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/insecure-cors-configuration.md b/anti-patterns-breadth/insecure-cors-configuration.md
new file mode 100644
index 0000000..48f99f3
--- /dev/null
+++ b/anti-patterns-breadth/insecure-cors-configuration.md
@@ -0,0 +1,116 @@
+# Insecure CORS Configuration
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Overly permissive CORS settings
+// ========================================
+
+// Mistake 1: Allow all origins
+app.use_cors({
+ origin: "*", // Any website can make requests!
+ credentials: TRUE // With user cookies!
+})
+
+// Mistake 2: Reflecting Origin header (same as allowing all)
+FUNCTION handle_preflight(request):
+ origin = request.get_header("Origin")
+
+ response.set_header("Access-Control-Allow-Origin", origin) // Reflects any origin
+ response.set_header("Access-Control-Allow-Credentials", "true")
+ RETURN response
+END FUNCTION
+
+// Mistake 3: Regex that's too broad
+allowed_origin_pattern = /.*\.example\.com$/ // Matches evil-example.com too!
+allowed_origin_pattern = /example\.com/ // Matches example.com.evil.com
+
+// Mistake 4: Null origin allowed
+IF origin == "null" OR origin IN allowed_origins:
+ // "null" origin used by local files, data: URIs - exploitable!
+ allow_cors(origin)
+END IF
+
+// ========================================
+// GOOD: Strict origin allowlist
+// ========================================
+
+CONSTANT ALLOWED_ORIGINS = [
+ "https://www.example.com",
+ "https://app.example.com",
+ "https://admin.example.com"
+]
+
+// Add development origins only in dev environment
+FUNCTION get_allowed_origins():
+ origins = ALLOWED_ORIGINS.copy()
+
+ IF environment == "development":
+ origins.append("http://localhost:3000")
+ origins.append("http://127.0.0.1:3000")
+ END IF
+
+ RETURN origins
+END FUNCTION
+
+FUNCTION handle_cors(request, response):
+ origin = request.get_header("Origin")
+
+ // Strict allowlist check
+ IF origin IN get_allowed_origins():
+ response.set_header("Access-Control-Allow-Origin", origin)
+ response.set_header("Access-Control-Allow-Credentials", "true")
+ response.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
+ response.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization")
+ response.set_header("Access-Control-Max-Age", "86400") // Cache preflight
+ END IF
+
+ // Vary header for proper caching
+ response.set_header("Vary", "Origin")
+
+ RETURN response
+END FUNCTION
+
+// For APIs that don't need credentials (truly public)
+FUNCTION configure_public_api_cors():
+ app.use_cors({
+ origin: "*",
+ credentials: FALSE, // No cookies, no problem with wildcard
+ methods: ["GET"], // Read-only
+ max_age: 86400
+ })
+END FUNCTION
+
+// Subdomain matching done safely
+FUNCTION is_allowed_subdomain(origin):
+ TRY:
+ parsed = parse_url(origin)
+ host = parsed.hostname.lower()
+
+ // Must use HTTPS in production
+ IF environment == "production" AND parsed.scheme != "https":
+ RETURN FALSE
+ END IF
+
+ // Exact match for apex domain
+ IF host == "example.com":
+ RETURN TRUE
+ END IF
+
+ // Subdomain check - must END with .example.com (not just contain)
+ IF host.ends_with(".example.com"):
+ // Additional: validate it's a known/expected subdomain
+ subdomain = host.replace(".example.com", "")
+ IF subdomain IN ["www", "app", "api", "admin"]:
+ RETURN TRUE
+ END IF
+ END IF
+
+ RETURN FALSE
+ CATCH:
+ RETURN FALSE
+ END TRY
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/insecure-credential-storage.md b/anti-patterns-breadth/insecure-credential-storage.md
new file mode 100644
index 0000000..dcadccb
--- /dev/null
+++ b/anti-patterns-breadth/insecure-credential-storage.md
@@ -0,0 +1,50 @@
+# Insecure Credential Storage
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Storing credentials in plaintext
+// ========================================
+FUNCTION save_user_credentials(username, password):
+ // Dangerous: Plaintext password storage
+ database.insert("credentials", {
+ username: username,
+ password: password // Stored as-is!
+ })
+END FUNCTION
+
+FUNCTION save_api_key(user_id, api_key):
+ // Dangerous: No encryption
+ database.insert("api_keys", {
+ user_id: user_id,
+ key: api_key
+ })
+END FUNCTION
+
+// ========================================
+// GOOD: Proper credential protection
+// ========================================
+FUNCTION save_user_credentials(username, password):
+ // Hash passwords with bcrypt
+ salt = bcrypt.generate_salt(rounds=12)
+ password_hash = bcrypt.hash(password, salt)
+
+ database.insert("credentials", {
+ username: username,
+ password_hash: password_hash
+ })
+END FUNCTION
+
+FUNCTION save_api_key(user_id, api_key):
+ // Encrypt sensitive data at rest
+ encryption_key = secret_manager.get("DATA_ENCRYPTION_KEY")
+ encrypted_key = aes_gcm_encrypt(api_key, encryption_key)
+
+ database.insert("api_keys", {
+ user_id: user_id,
+ encrypted_key: encrypted_key
+ })
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/insecure-password-reset.md b/anti-patterns-breadth/insecure-password-reset.md
new file mode 100644
index 0000000..8b40ee4
--- /dev/null
+++ b/anti-patterns-breadth/insecure-password-reset.md
@@ -0,0 +1,172 @@
+# Insecure Password Reset Flows
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Insecure password reset implementations
+// ========================================
+
+// Mistake 1: Predictable reset tokens
+FUNCTION create_reset_token_weak(user_id):
+ // Vulnerable: MD5 of user_id + timestamp is guessable
+ token = md5(user_id + current_timestamp())
+ database.save_reset_token(user_id, token)
+ RETURN token
+END FUNCTION
+
+// Mistake 2: Token never expires
+FUNCTION request_password_reset_no_expiry(email):
+ user = database.find_user_by_email(email)
+ token = generate_token()
+ // Vulnerable: No expiration set
+ database.save_reset_token(user.id, token)
+ send_email(email, "Reset: " + BASE_URL + "/reset?token=" + token)
+END FUNCTION
+
+// Mistake 3: Token not invalidated after use
+FUNCTION reset_password_reusable(token, new_password):
+ user_id = database.get_user_by_reset_token(token)
+ user = database.get_user(user_id)
+ user.password_hash = hash(new_password)
+ database.save(user)
+ // Vulnerable: Token still valid, can be reused!
+END FUNCTION
+
+// Mistake 4: User enumeration via different responses
+FUNCTION request_reset_enumeration(email):
+ user = database.find_user_by_email(email)
+ IF user IS NULL:
+ RETURN {error: "No account found with this email"} // Reveals info!
+ END IF
+ // ... send reset email
+ RETURN {success: TRUE, message: "Reset email sent"}
+END FUNCTION
+
+// Mistake 5: Sending password in email
+FUNCTION reset_password_insecure(email):
+ user = database.find_user_by_email(email)
+ new_password = generate_random_password()
+ user.password_hash = hash(new_password)
+ // Vulnerable: Password in plaintext email
+ send_email(email, "Your new password is: " + new_password)
+END FUNCTION
+
+// ========================================
+// GOOD: Secure password reset flow
+// ========================================
+FUNCTION request_password_reset(email):
+ // Always return same response to prevent enumeration
+ user = database.find_user_by_email(email)
+
+ IF user IS NOT NULL:
+ // Invalidate any existing reset tokens
+ database.delete_reset_tokens(user.id)
+
+ // Generate cryptographically secure token
+ token_bytes = crypto.secure_random_bytes(32)
+ token = base64_url_encode(token_bytes)
+
+ // Store hashed token with expiration
+ token_hash = sha256(token)
+ database.save_reset_token({
+ user_id: user.id,
+ token_hash: token_hash,
+ expires_at: current_time() + 3600, // 1 hour expiration
+ created_at: current_time()
+ })
+
+ // Send reset email
+ reset_url = BASE_URL + "/reset-password?token=" + token
+ send_email(user.email, "password_reset", {reset_url: reset_url})
+
+ log.info("Password reset requested", {user_id: user.id})
+ END IF
+
+ // Same response whether user exists or not
+ RETURN {
+ success: TRUE,
+ message: "If an account exists, a reset email has been sent"
+ }
+END FUNCTION
+
+FUNCTION validate_reset_token(token):
+ IF token IS NULL OR token.length < 32:
+ RETURN NULL
+ END IF
+
+ token_hash = sha256(token)
+ reset_record = database.find_reset_token(token_hash)
+
+ IF reset_record IS NULL:
+ log.warning("Invalid reset token attempted")
+ RETURN NULL
+ END IF
+
+ // Check expiration
+ IF current_time() > reset_record.expires_at:
+ database.delete_reset_token(token_hash)
+ RETURN NULL
+ END IF
+
+ RETURN reset_record
+END FUNCTION
+
+FUNCTION reset_password(token, new_password):
+ reset_record = validate_reset_token(token)
+
+ IF reset_record IS NULL:
+ RETURN {success: FALSE, error: "Invalid or expired reset link"}
+ END IF
+
+ // Validate new password strength
+ validation = validate_password_strength(new_password)
+ IF NOT validation.is_valid:
+ RETURN {success: FALSE, error: validation.message}
+ END IF
+
+ user = database.get_user(reset_record.user_id)
+
+ // Check if new password is same as old
+ IF bcrypt.verify(new_password, user.password_hash):
+ RETURN {success: FALSE, error: "New password must be different"}
+ END IF
+
+ // Update password
+ user.password_hash = bcrypt.hash(new_password, rounds=12)
+ database.save(user)
+
+ // CRITICAL: Invalidate the reset token
+ database.delete_reset_token(sha256(token))
+
+ // Invalidate all existing sessions (force re-login)
+ session_store.delete_all_user_sessions(user.id)
+
+ // Send confirmation email
+ send_email(user.email, "password_changed", {
+ timestamp: current_time(),
+ ip_address: request.get_client_ip()
+ })
+
+ log.info("Password reset completed", {user_id: user.id})
+
+ RETURN {success: TRUE, message: "Password reset successfully"}
+END FUNCTION
+
+// Additional security: Limit reset requests
+FUNCTION rate_limit_reset_requests(email):
+ key = "password_reset:" + sha256(email)
+ attempts = rate_limiter.get(key, default=0)
+
+ IF attempts >= 3:
+ // Max 3 reset requests per hour
+ RETURN FALSE
+ END IF
+
+ rate_limiter.increment(key, ttl=3600)
+ RETURN TRUE
+END FUNCTION
+```
+
+---
diff --git a/anti-patterns-breadth/insecure-random-generation.md b/anti-patterns-breadth/insecure-random-generation.md
new file mode 100644
index 0000000..0879983
--- /dev/null
+++ b/anti-patterns-breadth/insecure-random-generation.md
@@ -0,0 +1,72 @@
+# Insecure Random Number Generation
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Non-cryptographic RNG for security
+// ========================================
+FUNCTION generate_session_id_weak():
+ // Vulnerable: Math.random() / random.random() is predictable
+ RETURN random.randint(0, 999999999)
+END FUNCTION
+
+FUNCTION generate_token_weak():
+ // Vulnerable: Using random module for security tokens
+ chars = "abcdefghijklmnopqrstuvwxyz0123456789"
+ token = ""
+ FOR i = 0 TO 32:
+ token += chars[random.randint(0, chars.length - 1)]
+ END FOR
+ RETURN token
+END FUNCTION
+
+FUNCTION generate_key_weak():
+ // Vulnerable: Time-based seeding
+ random.seed(current_timestamp())
+ key = random.randbytes(32)
+ RETURN key
+END FUNCTION
+
+// Problems:
+// - Math.random(): Uses predictable PRNG (Mersenne Twister)
+// - Time seed: Attacker can guess seed from approximate time
+// - Internal state: Can be recovered from ~624 outputs
+
+// ========================================
+// GOOD: Cryptographically secure randomness
+// ========================================
+FUNCTION generate_session_id_secure():
+ // Use cryptographically secure random
+ RETURN secrets.token_urlsafe(32) // 256 bits of entropy
+END FUNCTION
+
+FUNCTION generate_token_secure():
+ // Use secrets module (Python) or crypto.randomBytes (Node)
+ RETURN secrets.token_hex(32) // 256 bits as hex string
+END FUNCTION
+
+FUNCTION generate_key_secure():
+ // Use OS entropy source
+ RETURN os.urandom(32) // 256 bits from /dev/urandom or equivalent
+END FUNCTION
+
+FUNCTION generate_password_secure(length):
+ // Secure password generation
+ alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
+ password = ""
+ FOR i = 0 TO length - 1:
+ password += alphabet[secrets.randbelow(alphabet.length)]
+ END FOR
+ RETURN password
+END FUNCTION
+
+// Language-specific secure random:
+// Python: secrets module, os.urandom
+// Node.js: crypto.randomBytes, crypto.randomUUID
+// Java: SecureRandom
+// Go: crypto/rand
+// Ruby: SecureRandom
+// PHP: random_bytes, random_int
+```
diff --git a/anti-patterns-breadth/insecure-session-token-generation.md b/anti-patterns-breadth/insecure-session-token-generation.md
new file mode 100644
index 0000000..56a4b8c
--- /dev/null
+++ b/anti-patterns-breadth/insecure-session-token-generation.md
@@ -0,0 +1,102 @@
+# Insecure Session Token Generation
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Predictable session tokens
+// ========================================
+FUNCTION create_session_weak(user_id):
+ // Vulnerable: Predictable token based on user ID
+ token = "session_" + user_id + "_" + current_timestamp()
+ RETURN token
+END FUNCTION
+
+FUNCTION create_session_sequential():
+ // Vulnerable: Sequential/incremental tokens
+ GLOBAL session_counter
+ session_counter = session_counter + 1
+ RETURN "session_" + session_counter
+END FUNCTION
+
+FUNCTION create_session_weak_random():
+ // Vulnerable: Using Math.random() or similar weak PRNG
+ token = ""
+ FOR i = 1 TO 32:
+ token = token + random_char() // Math.random() based
+ END FOR
+ RETURN token
+END FUNCTION
+
+// Attack: Attacker can predict/enumerate session tokens
+// - Timestamp-based: Try tokens from recent timestamps
+// - Sequential: Try nearby session IDs
+// - Weak random: Seed prediction or insufficient entropy
+
+// ========================================
+// GOOD: Cryptographically secure session tokens
+// ========================================
+FUNCTION create_session(user_id):
+ // Generate cryptographically secure random token
+ // Use 256 bits (32 bytes) minimum for security
+ token_bytes = crypto.secure_random_bytes(32)
+ token = base64_url_encode(token_bytes) // URL-safe encoding
+
+ // Store session with metadata
+ session_data = {
+ user_id: user_id,
+ created_at: current_timestamp(),
+ expires_at: current_timestamp() + SESSION_LIFETIME,
+ ip_address: request.get_client_ip(),
+ user_agent: request.get_user_agent()
+ }
+
+ // Store hashed token (protect against database leaks)
+ token_hash = sha256(token)
+ session_store.set(token_hash, session_data)
+
+ RETURN token
+END FUNCTION
+
+FUNCTION validate_session(token):
+ IF token IS NULL OR token.length < 32:
+ RETURN NULL
+ END IF
+
+ token_hash = sha256(token)
+ session = session_store.get(token_hash)
+
+ IF session IS NULL:
+ RETURN NULL
+ END IF
+
+ // Check expiration
+ IF current_timestamp() > session.expires_at:
+ session_store.delete(token_hash)
+ RETURN NULL
+ END IF
+
+ // Optional: Validate IP/User-Agent consistency
+ IF session.ip_address != request.get_client_ip():
+ log.warning("Session IP mismatch", {
+ expected: session.ip_address,
+ actual: request.get_client_ip()
+ })
+ // Decide whether to invalidate or just log
+ END IF
+
+ RETURN session
+END FUNCTION
+
+// Secure cookie configuration
+FUNCTION set_session_cookie(response, token):
+ response.set_cookie("session", token, {
+ httponly: TRUE, // Prevent JavaScript access
+ secure: TRUE, // HTTPS only
+ samesite: "Strict", // Prevent CSRF
+ max_age: SESSION_LIFETIME,
+ path: "/"
+ })
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/insecure-temporary-file-handling.md b/anti-patterns-breadth/insecure-temporary-file-handling.md
new file mode 100644
index 0000000..ff64051
--- /dev/null
+++ b/anti-patterns-breadth/insecure-temporary-file-handling.md
@@ -0,0 +1,181 @@
+# Insecure Temporary File Handling
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Predictable or insecure temp files
+// ========================================
+
+// Mistake 1: Predictable filename
+FUNCTION create_temp_bad_predictable(data):
+ // VULNERABLE: Attacker can predict and pre-create file
+ temp_path = "/tmp/myapp_" + current_user.id + ".tmp"
+
+ // Race condition: attacker creates symlink before this
+ write_file(temp_path, data)
+
+ RETURN temp_path
+END FUNCTION
+
+// Mistake 2: World-readable permissions
+FUNCTION create_temp_bad_permissions(data):
+ temp_path = "/tmp/myapp_" + random_string(8) + ".tmp"
+
+ // VULNERABLE: Default permissions may be world-readable (0644)
+ write_file(temp_path, data) // Other users can read
+
+ RETURN temp_path
+END FUNCTION
+
+// Mistake 3: Not cleaning up
+FUNCTION process_upload_bad_cleanup(uploaded_data):
+ temp_path = "/tmp/upload_" + generate_uuid()
+ write_file(temp_path, uploaded_data)
+
+ TRY:
+ result = process_file(temp_path)
+ // VULNERABLE: Temp file remains on disk if exception occurs elsewhere
+ RETURN result
+ CATCH Error as e:
+ // Temp file leaked!
+ THROW e
+ END TRY
+END FUNCTION
+
+// Mistake 4: Using system temp without isolation
+FUNCTION create_temp_bad_shared(data):
+ // VULNERABLE: Shared /tmp can be accessed by other users/processes
+ temp_path = temp_directory() + "/" + random_string(8)
+ write_file(temp_path, data)
+ RETURN temp_path
+END FUNCTION
+
+// ========================================
+// GOOD: Secure temporary file handling
+// ========================================
+
+// Use language's secure temp file creation
+FUNCTION create_temp_secure(data, suffix=".tmp"):
+ // mkstemp equivalent: creates file with random name and 0600 permissions
+ temp_file = create_secure_temp_file(
+ prefix="myapp_",
+ suffix=suffix,
+ dir="/var/app/tmp" // App-specific temp directory
+ )
+
+ // Write data to already-open file handle (no race condition)
+ temp_file.write(data)
+ temp_file.flush()
+
+ RETURN temp_file
+END FUNCTION
+
+// Process with guaranteed cleanup
+FUNCTION process_upload_secure(uploaded_data):
+ temp_file = NULL
+
+ TRY:
+ // Create secure temp file
+ temp_file = create_secure_temp_file(
+ prefix="upload_",
+ suffix=get_safe_extension(uploaded_data.filename),
+ dir=APPLICATION_TEMP_DIR
+ )
+
+ // Write with explicit permissions
+ temp_file.write(uploaded_data.content)
+ temp_file.flush()
+
+ // Process the file
+ result = process_file(temp_file.path)
+
+ RETURN result
+
+ FINALLY:
+ // Always clean up, even on exception
+ IF temp_file IS NOT NULL:
+ TRY:
+ temp_file.close()
+ delete_file(temp_file.path)
+ CATCH:
+ log.warning("Failed to clean up temp file", {path: temp_file.path})
+ END TRY
+ END IF
+ END TRY
+END FUNCTION
+
+// Context manager pattern for automatic cleanup
+FUNCTION with_temp_file(data, callback):
+ temp_file = create_secure_temp_file(prefix="ctx_")
+
+ TRY:
+ temp_file.write(data)
+ temp_file.flush()
+
+ RETURN callback(temp_file.path)
+
+ FINALLY:
+ temp_file.close()
+ secure_delete(temp_file.path) // Overwrite before delete for sensitive data
+ END TRY
+END FUNCTION
+
+// Usage:
+result = with_temp_file(sensitive_data, FUNCTION(path):
+ RETURN external_processor.process(path)
+END FUNCTION)
+
+// Secure temp directory per-request
+FUNCTION create_temp_directory_secure():
+ // Create directory with random name and 0700 permissions
+ temp_dir = create_secure_temp_directory(
+ prefix="session_",
+ dir=APPLICATION_TEMP_DIR
+ )
+
+ // Set restrictive permissions
+ set_permissions(temp_dir, 0o700)
+
+ RETURN temp_dir
+END FUNCTION
+
+// Application startup: ensure temp directory security
+FUNCTION initialize_temp_directory():
+ temp_dir = APPLICATION_TEMP_DIR
+
+ // Create if doesn't exist
+ IF NOT directory_exists(temp_dir):
+ create_directory(temp_dir, permissions=0o700)
+ END IF
+
+ // Verify permissions
+ current_perms = get_permissions(temp_dir)
+ IF current_perms != 0o700:
+ set_permissions(temp_dir, 0o700)
+ END IF
+
+ // Verify ownership
+ IF get_owner(temp_dir) != get_current_user():
+ THROW SecurityError("Temp directory has incorrect ownership")
+ END IF
+
+ // Clean up old temp files on startup
+ cleanup_old_temp_files(temp_dir, max_age_hours=24)
+END FUNCTION
+
+// Secure delete for sensitive data
+FUNCTION secure_delete(file_path):
+ IF file_exists(file_path):
+ // Overwrite with random data before deletion
+ file_size = get_file_size(file_path)
+ random_data = crypto.random_bytes(file_size)
+ write_file(file_path, random_data)
+ sync_to_disk(file_path)
+
+ // Now delete
+ delete_file(file_path)
+ END IF
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/jwt-misuse.md b/anti-patterns-breadth/jwt-misuse.md
new file mode 100644
index 0000000..786d57b
--- /dev/null
+++ b/anti-patterns-breadth/jwt-misuse.md
@@ -0,0 +1,139 @@
+# JWT Misuse (None Algorithm, Weak Secrets, Sensitive Data)
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Common JWT security mistakes
+// ========================================
+
+// Mistake 1: Not verifying algorithm (none algorithm attack)
+FUNCTION verify_jwt_vulnerable(token):
+ // Vulnerable: Accepts whatever algorithm is in the header
+ decoded = jwt.decode(token, SECRET_KEY) // Attacker sets alg: "none"
+ RETURN decoded
+END FUNCTION
+
+// Mistake 2: Weak or short secret key
+CONSTANT JWT_SECRET = "secret123" // Easily brute-forced
+
+FUNCTION create_jwt_weak(user_id):
+ payload = {user_id: user_id, exp: current_time() + 86400}
+ RETURN jwt.encode(payload, JWT_SECRET, algorithm="HS256")
+END FUNCTION
+
+// Mistake 3: Sensitive data in payload (JWTs are base64, not encrypted!)
+FUNCTION create_jwt_exposed(user):
+ payload = {
+ user_id: user.id,
+ email: user.email,
+ ssn: user.social_security_number, // PII in token!
+ credit_card: user.card_number, // Sensitive data exposed!
+ password_hash: user.password_hash, // Never put this in JWT!
+ exp: current_time() + 86400
+ }
+ RETURN jwt.encode(payload, SECRET_KEY)
+END FUNCTION
+
+// Mistake 4: No expiration or very long expiration
+FUNCTION create_jwt_no_expiry(user_id):
+ payload = {user_id: user_id} // No exp claim!
+ RETURN jwt.encode(payload, SECRET_KEY)
+END FUNCTION
+
+// ========================================
+// GOOD: Secure JWT implementation
+// ========================================
+
+// Use a strong secret (256+ bits for HS256)
+CONSTANT JWT_SECRET = environment.get("JWT_SECRET") // From secret manager
+
+FUNCTION initialize_jwt():
+ // Validate secret strength at startup
+ IF JWT_SECRET IS NULL OR JWT_SECRET.length < 32:
+ THROW Error("JWT_SECRET must be at least 256 bits")
+ END IF
+END FUNCTION
+
+FUNCTION create_jwt_secure(user_id):
+ now = current_time()
+
+ payload = {
+ // Standard claims
+ sub: user_id, // Subject
+ iat: now, // Issued at
+ exp: now + 3600, // Expiration (1 hour max for access tokens)
+ nbf: now, // Not before
+
+ // Custom claims (non-sensitive only!)
+ role: user.role // Roles are OK
+ // Never include: passwords, PII, payment info
+ }
+
+ // Explicitly specify algorithm
+ RETURN jwt.encode(payload, JWT_SECRET, algorithm="HS256")
+END FUNCTION
+
+FUNCTION verify_jwt_secure(token):
+ TRY:
+ // CRITICAL: Explicitly specify allowed algorithms
+ decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"])
+
+ // Additional validation
+ IF decoded.exp < current_time():
+ THROW Error("Token expired")
+ END IF
+
+ IF decoded.nbf > current_time():
+ THROW Error("Token not yet valid")
+ END IF
+
+ RETURN decoded
+
+ CATCH JWTError as e:
+ log.warning("JWT verification failed", {error: e.message})
+ RETURN NULL
+ END TRY
+END FUNCTION
+
+// For sensitive applications, use asymmetric keys (RS256)
+FUNCTION create_jwt_asymmetric(user_id):
+ private_key = load_private_key("jwt_private.pem")
+
+ payload = {
+ sub: user_id,
+ iat: current_time(),
+ exp: current_time() + 3600
+ }
+
+ // Sign with private key
+ RETURN jwt.encode(payload, private_key, algorithm="RS256")
+END FUNCTION
+
+FUNCTION verify_jwt_asymmetric(token):
+ public_key = load_public_key("jwt_public.pem")
+
+ // Verify with public key (can be shared safely)
+ RETURN jwt.decode(token, public_key, algorithms=["RS256"])
+END FUNCTION
+
+// Implement refresh token pattern for long-lived sessions
+FUNCTION create_token_pair(user_id):
+ // Short-lived access token (15 minutes)
+ access_token = create_jwt_secure(user_id, expiry=900)
+
+ // Long-lived refresh token (7 days) - store in DB for revocation
+ refresh_token = crypto.secure_random_bytes(32).to_base64()
+ database.insert("refresh_tokens", {
+ token_hash: sha256(refresh_token),
+ user_id: user_id,
+ expires_at: current_time() + 604800
+ })
+
+ RETURN {
+ access_token: access_token,
+ refresh_token: refresh_token
+ }
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/ldap-injection.md b/anti-patterns-breadth/ldap-injection.md
new file mode 100644
index 0000000..e3bf7c4
--- /dev/null
+++ b/anti-patterns-breadth/ldap-injection.md
@@ -0,0 +1,60 @@
+# LDAP Injection
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Unescaped LDAP filters
+// ========================================
+FUNCTION find_user_by_name(username):
+ // Vulnerable: User input in LDAP filter
+ filter = "(uid=" + username + ")"
+ RETURN ldap.search("ou=users,dc=example,dc=com", filter)
+END FUNCTION
+
+FUNCTION authenticate_ldap(username, password):
+ // Vulnerable: Both fields injectable
+ filter = "(&(uid=" + username + ")(userPassword=" + password + "))"
+ results = ldap.search(BASE_DN, filter)
+ RETURN results.count > 0
+END FUNCTION
+
+// Attack: username = "*)(uid=*))(|(uid=*"
+// Result: (uid=*)(uid=*))(|(uid=*)
+// This can return all users or bypass authentication
+
+// ========================================
+// GOOD: Escape LDAP special characters
+// ========================================
+FUNCTION escape_ldap(input):
+ // Escape LDAP special characters: * ( ) \ NUL
+ result = input
+ result = result.replace("\\", "\\5c") // Backslash first
+ result = result.replace("*", "\\2a")
+ result = result.replace("(", "\\28")
+ result = result.replace(")", "\\29")
+ result = result.replace("\0", "\\00")
+ RETURN result
+END FUNCTION
+
+FUNCTION find_user_by_name(username):
+ // Safe: Input is escaped before use
+ safe_username = escape_ldap(username)
+ filter = "(uid=" + safe_username + ")"
+ RETURN ldap.search("ou=users,dc=example,dc=com", filter)
+END FUNCTION
+
+FUNCTION authenticate_ldap(username, password):
+ // Better: Use LDAP bind for authentication instead of filter
+ user_dn = "uid=" + escape_ldap(username) + ",ou=users,dc=example,dc=com"
+
+ TRY:
+ connection = ldap.bind(user_dn, password)
+ connection.close()
+ RETURN TRUE
+ CATCH LDAPError:
+ RETURN FALSE
+ END TRY
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/mass-assignment.md b/anti-patterns-breadth/mass-assignment.md
new file mode 100644
index 0000000..f61908b
--- /dev/null
+++ b/anti-patterns-breadth/mass-assignment.md
@@ -0,0 +1,174 @@
+# Mass Assignment Vulnerabilities
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Mass assignment - accepting all user input
+// ========================================
+
+// Attacker sends: {"name": "John", "role": "admin", "balance": 999999}
+@route("/api/users/update")
+@require_auth
+FUNCTION update_user(request):
+ // VULNERABLE: Directly assigns all request body fields
+ user = database.get_user(request.user.id)
+
+ FOR field, value IN request.body:
+ user[field] = value // Attacker can set ANY field!
+ END FOR
+
+ database.save(user)
+ RETURN response(200, user)
+END FUNCTION
+
+// ORM auto-mapping vulnerability
+@route("/api/users")
+@require_auth
+FUNCTION create_user(request):
+ // VULNERABLE: ORM creates user from all request fields
+ user = User.create(request.body) // Includes role, isAdmin, etc.!
+ RETURN response(201, user)
+END FUNCTION
+
+// Nested object mass assignment
+@route("/api/orders")
+@require_auth
+FUNCTION create_order(request):
+ // VULNERABLE: Nested payment object can set price
+ order = Order.create({
+ user_id: request.user.id,
+ items: request.body.items,
+ payment: request.body.payment // Attacker sets payment.amount = 0
+ })
+ RETURN response(201, order)
+END FUNCTION
+
+// ========================================
+// GOOD: Explicit field allowlisting
+// ========================================
+
+// Define what fields can be updated
+CONSTANT USER_UPDATABLE_FIELDS = ["name", "email", "phone", "address"]
+CONSTANT USER_ADMIN_FIELDS = ["role", "status", "verified"]
+
+@route("/api/users/update")
+@require_auth
+FUNCTION update_user_secure(request):
+ user = database.get_user(request.user.id)
+
+ // SECURE: Only update explicitly allowed fields
+ FOR field IN USER_UPDATABLE_FIELDS:
+ IF field IN request.body:
+ user[field] = sanitize(request.body[field])
+ END IF
+ END FOR
+
+ database.save(user)
+
+ // Return only safe fields
+ RETURN response(200, user.to_public_dict())
+END FUNCTION
+
+// Admin with different field permissions
+@route("/api/admin/users/{user_id}")
+@require_admin
+FUNCTION admin_update_user(request, user_id):
+ user = database.get_user(user_id)
+
+ // Admins can update more fields, but still allowlisted
+ allowed_fields = USER_UPDATABLE_FIELDS + USER_ADMIN_FIELDS
+
+ FOR field IN allowed_fields:
+ IF field IN request.body:
+ user[field] = request.body[field]
+ END IF
+ END FOR
+
+ log.audit("Admin user update", {
+ admin_id: request.user.id,
+ user_id: user_id,
+ fields_changed: request.body.keys()
+ })
+
+ database.save(user)
+ RETURN response(200, user)
+END FUNCTION
+
+// Use DTOs (Data Transfer Objects) for input
+CLASS UserUpdateDTO:
+ name: String (max_length=100)
+ email: String (email_format, max_length=255)
+ phone: String (phone_format, optional)
+ address: String (max_length=500, optional)
+
+ FUNCTION from_request(body):
+ dto = UserUpdateDTO()
+ dto.name = validate_string(body.name, max_length=100)
+ dto.email = validate_email(body.email)
+ dto.phone = validate_phone(body.phone) IF body.phone ELSE NULL
+ dto.address = validate_string(body.address, max_length=500) IF body.address ELSE NULL
+ RETURN dto
+ END FUNCTION
+END CLASS
+
+@route("/api/users/update")
+@require_auth
+FUNCTION update_user_dto(request):
+ TRY:
+ dto = UserUpdateDTO.from_request(request.body)
+ CATCH ValidationError as e:
+ RETURN response(400, {error: e.message})
+ END TRY
+
+ user = database.get_user(request.user.id)
+ user.apply_dto(dto) // Only applies DTO fields
+ database.save(user)
+
+ RETURN response(200, user.to_public_dict())
+END FUNCTION
+
+// Nested objects with strict validation
+CLASS OrderCreateDTO:
+ items: Array of OrderItemDTO
+ shipping_address_id: UUID
+ // payment calculated server-side, NOT from request
+
+ FUNCTION from_request(body, user):
+ dto = OrderCreateDTO()
+ dto.items = [OrderItemDTO.from_request(item) FOR item IN body.items]
+
+ // Verify address belongs to user
+ address = database.get_address(body.shipping_address_id)
+ IF address IS NULL OR address.user_id != user.id:
+ THROW ValidationError("Invalid shipping address")
+ END IF
+ dto.shipping_address_id = address.id
+
+ RETURN dto
+ END FUNCTION
+END CLASS
+
+@route("/api/orders")
+@require_auth
+FUNCTION create_order_secure(request):
+ dto = OrderCreateDTO.from_request(request.body, request.user)
+
+ // Calculate payment server-side from validated items
+ total = 0
+ FOR item IN dto.items:
+ product = database.get_product(item.product_id)
+ total += product.price * item.quantity // Price from DB, not request!
+ END FOR
+
+ order = Order.create({
+ user_id: request.user.id,
+ items: dto.items,
+ shipping_address_id: dto.shipping_address_id,
+ total: total // Server-calculated
+ })
+
+ RETURN response(201, order.to_dict())
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/missing-authentication-endpoints.md b/anti-patterns-breadth/missing-authentication-endpoints.md
new file mode 100644
index 0000000..ee0c8b9
--- /dev/null
+++ b/anti-patterns-breadth/missing-authentication-endpoints.md
@@ -0,0 +1,126 @@
+# Missing Authentication on Endpoints
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Unprotected API endpoints
+// ========================================
+
+// No authentication - anyone can access
+@route("/api/users")
+FUNCTION get_all_users():
+ RETURN database.query("SELECT * FROM users")
+END FUNCTION
+
+// Admin functionality without auth check
+@route("/api/admin/delete-user/{id}")
+FUNCTION admin_delete_user(id):
+ database.execute("DELETE FROM users WHERE id = ?", [id])
+ RETURN {status: "deleted"}
+END FUNCTION
+
+// Sensitive data exposed without auth
+@route("/api/orders/{order_id}")
+FUNCTION get_order(order_id):
+ RETURN database.get_order(order_id)
+END FUNCTION
+
+// "Security through obscurity" - hidden endpoint still accessible
+@route("/api/internal/debug-info")
+FUNCTION get_debug_info():
+ RETURN {
+ database_connection: DB_STRING,
+ api_keys: LOADED_KEYS,
+ server_config: CONFIG
+ }
+END FUNCTION
+
+// ========================================
+// GOOD: Authentication on all protected endpoints
+// ========================================
+
+// Middleware to enforce authentication
+FUNCTION require_auth(handler):
+ RETURN FUNCTION wrapped(request):
+ token = request.headers.get("Authorization")
+
+ IF token IS NULL:
+ RETURN response(401, {error: "Authentication required"})
+ END IF
+
+ user = verify_token(token)
+ IF user IS NULL:
+ RETURN response(401, {error: "Invalid or expired token"})
+ END IF
+
+ request.user = user
+ RETURN handler(request)
+ END FUNCTION
+END FUNCTION
+
+// Middleware for admin-only routes
+FUNCTION require_admin(handler):
+ RETURN require_auth(FUNCTION wrapped(request):
+ IF request.user.role != "admin":
+ log.security("Unauthorized admin access attempt", {
+ user_id: request.user.id,
+ endpoint: request.path
+ })
+ RETURN response(403, {error: "Admin access required"})
+ END IF
+
+ RETURN handler(request)
+ END FUNCTION)
+END FUNCTION
+
+// Protected endpoints with proper auth
+@route("/api/users")
+@require_admin // Only admins can list all users
+FUNCTION get_all_users(request):
+ // Return only non-sensitive fields
+ users = database.query("SELECT id, name, email, created_at FROM users")
+ RETURN response(200, {users: users})
+END FUNCTION
+
+// Admin endpoint with proper protection
+@route("/api/admin/delete-user/{id}")
+@require_admin
+FUNCTION admin_delete_user(request, id):
+ // Audit log before action
+ log.audit("User deletion", {
+ admin_id: request.user.id,
+ target_user_id: id
+ })
+
+ database.soft_delete("users", id) // Soft delete for audit trail
+ RETURN response(200, {status: "deleted"})
+END FUNCTION
+
+// Never expose internal/debug endpoints in production
+IF environment != "production":
+ @route("/api/internal/debug-info")
+ @require_admin
+ FUNCTION get_debug_info(request):
+ RETURN {config: get_safe_config()} // Sanitized config only
+ END FUNCTION
+END IF
+
+// Default deny - explicitly define allowed public endpoints
+PUBLIC_ENDPOINTS = [
+ "/api/auth/login",
+ "/api/auth/register",
+ "/api/public/status",
+ "/api/public/docs"
+]
+
+FUNCTION global_auth_middleware(request):
+ IF request.path IN PUBLIC_ENDPOINTS:
+ RETURN next(request)
+ END IF
+
+ // All other routes require authentication by default
+ RETURN require_auth(next)(request)
+END FUNCTION
+```
diff --git a/anti-patterns-breadth/missing-canonicalization.md b/anti-patterns-breadth/missing-canonicalization.md
new file mode 100644
index 0000000..534d792
--- /dev/null
+++ b/anti-patterns-breadth/missing-canonicalization.md
@@ -0,0 +1,181 @@
+# Missing Canonicalization
+
+
+```
+// PSEUDOCODE - Implement in your target language
+
+// ========================================
+// BAD: Validation without canonicalization
+// ========================================
+FUNCTION check_path_unsafe(requested_path):
+ // Vulnerable: Path not canonicalized before validation
+ IF requested_path.starts_with("/uploads/"):
+ // Bypass: "../../../etc/passwd" doesn't start with /uploads/
+ // But resolves to outside the directory!
+ RETURN read_file(requested_path)
+ END IF
+ THROW AccessDenied("Invalid path")
+END FUNCTION
+
+FUNCTION check_url_unsafe(url):
+ // Vulnerable: URL manipulation bypasses check
+ // Blocked: "http://internal-server"
+ // Bypass: "http://internal-server%00.example.com"
+ // Bypass: "http://0x7f000001" (127.0.0.1 in hex)
+ // Bypass: "http://localhost" vs "http://LOCALHOST" vs "http://127.0.0.1"
+
+ IF url.contains("internal-server"):
+ THROW AccessDenied("Internal URLs not allowed")
+ END IF
+
+ RETURN http.get(url)
+END FUNCTION
+
+FUNCTION validate_filename_unsafe(filename):
+ // Vulnerable: Unicode normalization bypass
+ // Blocked: "config.php"
+ // Bypass: "config.php" with full-width characters (config.php)
+ // Bypass: "config.php\x00.txt" (null byte injection)
+
+ IF filename.ends_with(".php"):
+ THROW AccessDenied("PHP files not allowed")
+ END IF
+
+ save_file(filename)
+END FUNCTION
+
+FUNCTION check_html_unsafe(content):
+ // Vulnerable: Case-sensitive blacklist
+ // Blocked: ""
+ html += "