DOMAIN:SECURITY:OWASP_TESTING¶
OWNER: pol
ALSO_USED_BY: victoria, hugo, koen, marije, ashley
UPDATED: 2026-03-24
SCOPE: all client projects — penetration testing and security assessment
PREREQUISITE: domains/security/index.md (OWASP Top 10 overview)
STACK: Next.js + Hono + PostgreSQL + Keycloak + k3s
CORE_PRINCIPLE¶
RULE: automated scanning catches <30% of real vulnerabilities — manual testing is MANDATORY
RULE: test against GE's specific stack, not generic checklists
RULE: every finding needs a proof-of-concept — no theoretical vulnerabilities
RULE: report findings with reproduction steps, impact assessment, and remediation guidance
A01:BROKEN_ACCESS_CONTROL — TESTING_PROCEDURES¶
STANDARD: OWASP Testing Guide — OTG-AUTHZ-001 through OTG-AUTHZ-004
SEVERITY: CRITICAL-HIGH
MANUAL_TESTS¶
TEST 1: Horizontal privilege escalation (IDOR)
1. authenticate as User A
2. capture request to access User A's resource (e.g., GET /api/users/123/profile)
3. change ID to User B's ID (e.g., GET /api/users/456/profile)
4. IF response contains User B's data THEN CRITICAL finding
TEST 2: Vertical privilege escalation
1. authenticate as regular user
2. enumerate admin endpoints (GET /api/admin/*, POST /api/users/*/role)
3. attempt each endpoint with regular user token
4. IF any admin endpoint responds with data THEN CRITICAL finding
TEST 3: Missing function-level access control
1. map all API endpoints from source code or OpenAPI spec
2. categorize by required role (public, user, admin, super-admin)
3. test each endpoint with: no auth, user auth, admin auth
4. IF any endpoint accessible without proper role THEN HIGH finding
TEST 4: Path traversal
1. find endpoints accepting file paths or resource names
2. inject: ../../../etc/passwd, ..%2f..%2f..%2fetc/passwd, ....//....//etc/passwd
3. IF file contents returned THEN CRITICAL finding
NEXT_JS_SPECIFIC¶
CHECK: are Next.js API routes behind middleware auth?
CHECK: can server actions be called directly without session validation?
CHECK: are getServerSideProps/server components checking auth?
TEST: Next.js middleware bypass
1. identify middleware.ts matchers
2. test paths NOT covered by matcher (e.g., /_next/*, /api/auth/*)
3. test with path manipulation: /admin vs /Admin vs /admin/ vs /admin%00
4. IF any bypass found THEN CRITICAL finding
HONO_SPECIFIC¶
CHECK: is auth middleware applied to route GROUP, not individual routes?
CHECK: are new routes automatically protected?
// ANTI_PATTERN: per-route auth — easy to forget on new route
app.get('/api/data', authMiddleware, handler)
app.get('/api/new-endpoint', handler) // FORGOT AUTH
// FIX: group-level auth
const api = app.basePath('/api')
api.use('*', authMiddleware) // all routes in group protected
api.get('/data', handler)
api.get('/new-endpoint', handler) // automatically protected
POSTGRESQL_RLS¶
-- TEST: RLS bypass
-- 1. connect with application role
-- 2. attempt: SELECT * FROM tenants WHERE tenant_id != current_tenant
-- 3. IF rows returned from other tenants THEN CRITICAL
-- TEST: RLS on ALL operations
-- check INSERT, UPDATE, DELETE — not just SELECT
-- common miss: RLS on SELECT but not on UPDATE
-- TOOL: verify RLS policies exist for every tenant-scoped table
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public' AND rowsecurity = false;
-- IF any tenant-scoped table has rowsecurity=false THEN HIGH finding
AUTOMATED_TOOLS¶
TOOL: Burp Suite Autorize extension — automated access control testing
RUN: configure two sessions (admin + regular user), replay admin requests as regular user
TOOL: OWASP ZAP Access Control plugin
TOOL: custom script — export all routes, test each with different auth levels
A02:CRYPTOGRAPHIC_FAILURES — TESTING_PROCEDURES¶
STANDARD: OWASP Testing Guide — OTG-CRYPST-001, OTG-CRYPST-002
MANUAL_TESTS¶
TEST 1: TLS configuration
TOOL: testssl.sh --full https://target.com
CHECK: no TLS 1.0/1.1
CHECK: no weak ciphers (RC4, DES, 3DES, export ciphers)
CHECK: HSTS header present with max-age >= 31536000
CHECK: certificate chain valid, not self-signed in production
TEST 2: Password storage
1. review source code for password hashing
2. CHECK: bcrypt (cost >= 12), scrypt, or Argon2id used
3. CHECK: salt is unique per password (not global)
4. IF MD5, SHA-1, SHA-256, or plaintext found THEN CRITICAL
TEST 3: Sensitive data in transit
1. proxy all application traffic through Burp Suite
2. search for PII in HTTP (non-HTTPS) requests
3. check API responses for unnecessary sensitive data
4. IF passwords, SSN, or credit cards in non-TLS flow THEN CRITICAL
TEST 4: Token/nonce randomness
1. collect 100+ tokens (session IDs, CSRF tokens, reset tokens)
2. analyze for patterns (sequential, timestamp-based, low entropy)
TOOL: Burp Sequencer — automated entropy analysis
3. IF predictable pattern found THEN HIGH
NEXT_JS_SPECIFIC¶
CHECK: are API keys in NEXT_PUBLIC_ env vars? (exposed to client)
CHECK: is next.config.js poweredByHeader set to false?
CHECK: are cookies set with Secure flag in production?
A03:INJECTION — TESTING_PROCEDURES¶
STANDARD: OWASP Testing Guide — OTG-INPVAL-005 (SQL), OTG-INPVAL-001 (XSS)
SQL_INJECTION¶
TEST 1: Error-based SQLi
1. for each parameter accepting user input, inject: ' OR 1=1--
2. inject: ' UNION SELECT null,null,null--
3. inject: '; DROP TABLE users--
4. monitor for SQL error messages in response
5. IF SQL error or unexpected data returned THEN CRITICAL
TEST 2: Blind SQLi
1. inject time-based: ' OR SLEEP(5)-- (MySQL), ' OR pg_sleep(5)-- (PostgreSQL)
2. measure response time
3. IF response delayed by injected time THEN CRITICAL
TEST 3: Drizzle ORM bypass
CHECK: any use of sql`` tagged template with string concatenation?
CHECK: any raw SQL queries with user input?
CHECK: dynamic column/table names from user input?
// ANTI_PATTERN: string interpolation in Drizzle raw SQL
const result = await db.execute(sql`SELECT * FROM ${tableName} WHERE id = ${id}`)
// tableName is NOT parameterized by Drizzle — only values are
// FIX: whitelist table/column names
const ALLOWED_TABLES = ['users', 'orders', 'products'] as const
if (!ALLOWED_TABLES.includes(tableName)) throw new Error('Invalid table')
XSS_TESTING¶
TEST 1: Reflected XSS
1. inject in all URL parameters: <script>alert(1)</script>
2. inject encoded variants: %3Cscript%3Ealert(1)%3C/script%3E
3. inject event handlers: " onmouseover="alert(1)
4. check if input reflected in HTML response unescaped
5. IF script executes THEN HIGH
TEST 2: Stored XSS
1. inject payloads in all stored fields (names, descriptions, comments)
2. navigate to pages that display stored data
3. IF script executes when viewing stored data THEN CRITICAL
TEST 3: DOM-based XSS
1. search client-side JS for: innerHTML, outerHTML, document.write, eval
2. trace data flow from URL/input to these sinks
3. IF user input reaches sink without sanitization THEN HIGH
NEXT_JS_SPECIFIC_XSS¶
CHECK: any use of dangerouslySetInnerHTML?
CHECK: are user inputs rendered in JSX without escaping? (React auto-escapes, but...)
CHECK: is user input used in href attributes? (javascript: protocol)
CHECK: is user input used in style attributes? (CSS injection)
// ANTI_PATTERN: user input in href
<a href={userInput}>Click</a> // javascript:alert(1) works
// FIX: validate URL protocol
const isValidUrl = (url: string) => {
try {
const parsed = new URL(url)
return ['http:', 'https:'].includes(parsed.protocol)
} catch { return false }
}
COMMAND_INJECTION¶
TEST: if application executes system commands
1. inject: ; ls -la
2. inject: | cat /etc/passwd
3. inject: $(whoami)
4. inject: `id`
5. IF command output returned THEN CRITICAL
RULE: GE projects should NEVER use child_process.exec with user input
FIX: use child_process.execFile with argument array (no shell interpretation)
A04:INSECURE_DESIGN — TESTING_PROCEDURES¶
BUSINESS_LOGIC_TESTS¶
TEST 1: Rate limiting
1. send 100 requests to login endpoint in 10 seconds
2. IF no rate limiting response (429) after ~10 attempts THEN HIGH
TEST 2: Race conditions
TOOL: Burp Suite Turbo Intruder — send parallel requests
1. identify state-changing operations (purchase, transfer, coupon apply)
2. send 50 concurrent identical requests
3. IF operation succeeds multiple times THEN HIGH (TOCTOU vulnerability)
TEST 3: Account enumeration
1. submit login with valid username + wrong password
2. submit login with invalid username + wrong password
3. IF responses differ (timing, message, status code) THEN MEDIUM
NOTE: timing difference >100ms often indicates valid user lookup
TEST 4: Password reset flow
1. request password reset
2. check token: length >= 32 chars, not sequential, expires in <= 1 hour
3. use token, then try to use it again
4. IF token reusable THEN HIGH
5. IF token doesn't expire THEN HIGH
A05:SECURITY_MISCONFIGURATION — TESTING_PROCEDURES¶
HEADER_TESTING¶
TOOL: curl -I https://target.com
REQUIRED HEADERS:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY (or SAMEORIGIN if iframes needed)
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
X-XSS-Protection: 0 (disabled — CSP supersedes, old header causes issues)
IF any required header missing THEN MEDIUM
IF CSP missing THEN HIGH (primary XSS defense layer)
NEXT_JS_HEADERS¶
// next.config.js — REQUIRED security headers
const securityHeaders = [
{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'"
}
]
INFORMATION_LEAKAGE¶
TEST 1: Error handling
1. trigger 404, 500 errors
2. send malformed requests (invalid JSON, wrong content-type)
3. IF stack trace, file paths, or framework version in response THEN MEDIUM
TEST 2: Debug endpoints
CHECK: /_next/data/* accessible? (Next.js data routes)
CHECK: /api/health or /api/debug endpoints exposing internal info?
CHECK: /.env, /config.json, /package.json accessible?
TEST 3: HTTP methods
TOOL: curl -X OPTIONS https://target.com/api/endpoint
CHECK: unnecessary methods enabled (TRACE, PUT, DELETE on read-only endpoints)?
A06:VULNERABLE_COMPONENTS — TESTING_PROCEDURES¶
TOOL: trivy fs . --severity CRITICAL,HIGH
TOOL: npm audit --production
TOOL: snyk test (with reachability analysis)
CHECK: npm audit findings — are they in production dependencies or devDependencies?
CHECK: are vulnerable dependencies actually imported and reachable?
CHECK: container base image CVEs (trivy image <image>)
RULE: CRITICAL CVE with confirmed reachability = block deployment
RULE: HIGH CVE = fix within 7 days
RULE: update lockfile regularly — stale lockfile accumulates vulnerabilities
A07:AUTHENTICATION_FAILURES — TESTING_PROCEDURES¶
SESSION_MANAGEMENT¶
TEST 1: Session fixation
1. note session ID before login
2. authenticate
3. IF session ID unchanged after login THEN HIGH
TEST 2: Session timeout
1. authenticate, wait idle for configured timeout period
2. attempt action after timeout
3. IF session still valid THEN MEDIUM
TEST 3: Concurrent sessions
1. authenticate on device A
2. authenticate on device B with same credentials
3. check if both sessions active (expected behavior varies by requirement)
TEST 4: Cookie security
CHECK: HttpOnly flag set (prevents JS access)
CHECK: Secure flag set (HTTPS only)
CHECK: SameSite=Lax or Strict (CSRF protection)
CHECK: reasonable expiry (not years)
TOOL: browser DevTools → Application → Cookies
KEYCLOAK_SPECIFIC¶
TEST: Keycloak realm configuration
CHECK: brute force detection enabled (Realm Settings → Security Defenses)
CHECK: password policy configured (length >= 12, breach database check)
CHECK: required actions include email verification
CHECK: session timeouts configured (SSO Session Idle, SSO Session Max)
CHECK: unnecessary identity providers disabled
CHECK: admin console NOT publicly accessible
CHECK: user registration disabled unless explicitly needed
A08_A09_A10:SUPPLEMENTARY_TESTS¶
A08:DATA_INTEGRITY¶
TEST: Subresource Integrity
1. inspect HTML for external script/style tags
2. CHECK: integrity attribute present with SHA-384/SHA-512 hash
3. IF loading scripts from CDN without SRI THEN MEDIUM
A09:LOGGING¶
TEST: security event logging
1. trigger: failed login, successful login, password change, permission change
2. verify each event appears in logs with: timestamp, user, action, IP, result
3. IF security events not logged THEN HIGH
TEST: log injection
1. submit input with newlines and log-format strings: \n2026-03-24 INFO Success
2. check if injected text appears as separate log entry
3. IF log injection possible THEN MEDIUM
A10:SSRF¶
TEST: Server-Side Request Forgery
1. identify endpoints that fetch external URLs (webhooks, image upload by URL, PDF gen)
2. inject: http://127.0.0.1, http://169.254.169.254/latest/meta-data/
3. inject: http://[::1], http://0x7f000001, http://2130706433
4. IF internal service or cloud metadata accessible THEN CRITICAL
AUTOMATED_SCANNING_PLAYBOOK¶
OWASP_ZAP¶
# Quick scan (5-10 min)
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
-t https://target.com \
-r report.html
# Full scan (30-60 min)
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-full-scan.py \
-t https://target.com \
-r report.html
# API scan with OpenAPI spec
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \
-t https://target.com/api/openapi.json \
-f openapi \
-r report.html
# Authenticated scan (use ZAP context with auth script)
# Export ZAP session → automate in CI
NUCLEI¶
# Run with all templates
nuclei -u https://target.com -t nuclei-templates/
# Targeted: only critical and high severity
nuclei -u https://target.com -severity critical,high
# Technology-specific
nuclei -u https://target.com -tags nextjs,nodejs
# CI integration — JSON output
nuclei -u https://target.com -severity critical,high -json -o results.json
BURP_SUITE_WORKFLOW¶
1. Configure browser proxy → Burp Suite (127.0.0.1:8080)
2. Spider/crawl the application (map all endpoints)
3. Manual exploration of auth flows, business logic
4. Active scan on discovered endpoints
5. Run Autorize extension for access control testing
6. Use Intruder for parameter fuzzing
7. Use Repeater for manual request manipulation
8. Export findings to report
TESTING_SCHEDULE¶
PER_PROJECT_LIFECYCLE¶
| phase | testing type | tools | who |
|---|---|---|---|
| design | threat model review | threat-modeling.md | victoria |
| development (each PR) | SAST + SCA | semgrep, trivy, eslint-security | CI pipeline |
| pre-staging | DAST baseline scan | OWASP ZAP baseline | pol (automated) |
| staging | full DAST + manual | ZAP full + Burp Suite manual | pol |
| pre-production | penetration test | full manual test | pol |
| production | continuous monitoring | nuclei scheduled, dependency alerts | automated |
| quarterly | re-test + new vulns | full assessment | pol |
REPORT_TEMPLATE¶
# Security Assessment Report: {client} - {project}
**Date:** {date}
**Tester:** pol
**Scope:** {URLs, endpoints, components tested}
**Methodology:** OWASP Testing Guide v4.2, OWASP Top 10 2021
## Executive Summary
{1-2 paragraphs: overall posture, critical findings count, recommendation}
## Findings
### {FINDING_ID}: {Title}
- **Severity:** CRITICAL | HIGH | MEDIUM | LOW | INFO
- **Category:** {OWASP Top 10 category}
- **Affected:** {URL/endpoint/component}
- **Description:** {what the vulnerability is}
- **Proof of Concept:** {exact steps to reproduce}
- **Impact:** {what an attacker could do}
- **Remediation:** {specific fix with code example if applicable}
- **References:** {OWASP, CWE, CVE if applicable}
## Summary Table
| ID | title | severity | status |
|---|---|---|---|
## Methodology
{tools used, scope, limitations}
SELF_CHECK¶
BEFORE_SUBMITTING_SECURITY_ASSESSMENT:
- [ ] tested ALL OWASP Top 10 categories (not just favorites)?
- [ ] manual testing done in addition to automated scans?
- [ ] every finding has proof-of-concept with reproduction steps?
- [ ] severity ratings consistent across findings?
- [ ] remediation guidance is specific and actionable (not "fix the vulnerability")?
- [ ] tested with multiple user roles (unauthenticated, user, admin)?
- [ ] checked both API and UI for same vulnerabilities?
- [ ] false positives filtered out with justification?
READ_ALSO: domains/security/index.md, domains/security/tools.md, domains/security/threat-modeling.md