# Runbook 8.13 — Penetration Testing Checklist

## Scope
External black-box + internal grey-box test of the MercuryPay Fraud & AML
Platform REST API (`gateway_api`) and admin LiveView (`gateway_web`).

## Pre-test Prerequisites
- [ ] Written authorisation from MercuryPay security officer
- [ ] Test environment isolated from production data
- [ ] DB snapshot taken (point-in-time restore available)
- [ ] Tester credentials provisioned (test JWT, test API key)
- [ ] Monitoring dashboards open (Grafana / observer)

---

## OWASP Top 10 Checklist

### A01 Broken Access Control
- [ ] Unauthenticated request to `/api/v1/transactions` → expect 401
- [ ] Low-privilege API key (no `:compliance_export`) to `GET /api/v1/compliance/export` → expect 403
- [ ] Fraud analyst role trying to access `/admin/fraud/rules` (fraud_admin only) → expect 403
- [ ] IDOR: `/api/v1/transactions/<other_tenant_tx>` → expect 404 (tenant isolation)
- [ ] JWT with `exp` in the past → expect 401

### A02 Cryptographic Failures
- [ ] Verify TLS ≥ 1.2 on all endpoints (test with `nmap --script ssl-enum-ciphers`)
- [ ] Verify `feature_snapshot` in DB is stored as `%{"__enc" => "v1:..."}` (not plaintext)
- [ ] Verify `FEATURE_SNAPSHOT_KEY` is not committed to VCS and not logged

### A03 Injection
- [ ] SQL injection in `tenant_id`, `from`, `to` params of compliance export → expect 400 / no SQL error
- [ ] XSS in `transaction_ref` field rendered in CaseManagementLive → no script execution
- [ ] Rule `condition` field with malicious Elixir atom → should not be evaluated (`Code.eval_string` must not be used)

### A04 Insecure Design
- [ ] Rate limiting: > 100 req/min from single IP → expect 429 (mw_router rate_limit)
- [ ] Verify circuit breaker state is not exposed in API error messages

### A05 Security Misconfiguration
- [ ] `/health/live` and `/health/ready` do not expose DB credentials or internal IPs
- [ ] Elixir `Logger` does not log raw JWT tokens or card numbers
- [ ] `config/prod.exs` does not contain hardcoded secrets

### A06 Vulnerable Components
- [ ] Run `mix hex.audit` — 0 known vulnerabilities
- [ ] Run `mix deps.audit` — all deps on latest patch release

### A07 Authentication Failures
- [ ] Brute-force 100× wrong password on `/admin/session` → account not locked (stateless JWT — document this as accepted risk)
- [ ] JWT signed with RS256 — verify `alg: none` attack is rejected

### A08 Software and Data Integrity
- [ ] Verify Oban jobs cannot be injected via public API (no unauthenticated `/oban` endpoint)
- [ ] Sanctions list CSV upload: upload a file with `../../etc/passwd` path traversal → expect validation error

### A09 Security Logging Failures
- [ ] Verify DECLINE decisions are logged with `transaction_ref`, `tenant_id`, `fired_rules`
- [ ] Verify audit log entries exist for rule changes (RulesBuilderLive actions)
- [ ] Log rotation and retention ≥ 90 days

### A10 Server-Side Request Forgery
- [ ] SFTP ingestion path: attempt `sftp://localhost:22/etc/passwd` → blocked by allowlist

---

## Critical Finding Exit Criteria
The release is blocked if any of the following are found:
- [ ] Unauthenticated access to any authenticated endpoint
- [ ] Cross-tenant data leakage
- [ ] Remote code execution via any input field
- [ ] Unencrypted PII stored or transmitted

## Report Deliverable
Penetration test report signed by tester, reviewed by security officer,
stored in: `docs/fraud-extension/security/pentest-<date>.pdf`
