Files
agentic-dev/docs/compliance/templates/incident-runbook.template.md
Danijel Martinek f77e6ea881 chore(template): clean-slate template snapshot from bb4a0c7
Curated, product-agnostic snapshot of the post-story-04 tree: demo
content deleted, auth-only reference feature, web-next shell, all gates
green. Product-specific docs, ADRs 027-029, PRDs/epics/archive, editor
library traces, and product naming are curated out; generic template
repairs (coverage provider devDeps, root test:coverage script, live
lint fixes, root-only release-please) are kept. See TEMPLATE.md for
provenance, curation list, and usage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
2026-07-12 20:40:54 +02:00

265 lines
15 KiB
Markdown

---
status: template
playbook-section: 20
title: "Security Incident & Personal Data Breach Runbook"
gdpr-articles: [Art. 33, Art. 34]
last-reviewed: "[FILL IN: YYYY-MM-DD]"
---
# Security Incident & Personal Data Breach Runbook
> **Template status** — fill every `[FILL IN: …]` marker before use.
> Cross-references below point to shipped code and ADRs; do not change them.
---
## 1. Purpose & Scope
This runbook governs the detection, triage, containment, notification, and post-mortem of security incidents and personal data breaches affecting `[FILL IN: product / organisation name]`.
**In scope:** any event that may have compromised the confidentiality, integrity, or availability of personal data processed by this system, including unauthorized access, exfiltration, accidental exposure, and ransomware.
**Out of scope:** non-PII service disruptions (handle via standard on-call runbook). Escalate to this runbook when PII impact cannot be ruled out.
---
## 2. Severity Classification
| Level | Definition | Example |
| ----------------- | ------------------------------------------------- | ---------------------------------------------------- |
| **P0 — Critical** | Confirmed exfiltration or mass exposure of PII | Database dump on public host |
| **P1 — High** | Suspected breach; blast radius unknown | Anomalous `EXPORT` audit entries for non-admin actor |
| **P2 — Medium** | Contained misconfiguration; no confirmed PII leak | Stale presigned URL exposed to wrong tenant |
| **P3 — Low** | Near-miss; no PII accessed | Failed injection attempt blocked by rate-limit |
---
## 3. Roles & Contacts
| Role | Name | Contact |
| ----------------------- | ------------------------------ | ------------------ |
| Incident Commander | [FILL IN: name] | [FILL IN: contact] |
| Data Protection Officer | [FILL IN: name / DPO provider] | [FILL IN: contact] |
| Legal / Counsel | [FILL IN: name] | [FILL IN: contact] |
| Engineering Lead | [FILL IN: name] | [FILL IN: contact] |
| Communications Lead | [FILL IN: name] | [FILL IN: contact] |
**Supervisory Authority (SA):**
- Authority name: `[FILL IN: e.g. ICO / CNIL / BfDI]`
- Online notification portal: `[FILL IN: URL]`
- Emergency phone: `[FILL IN: number]`
---
## 4. Phase 1 — Detection
### 4.1 Automated signals
This template ships two automated detection surfaces. Check both when an alert fires.
#### Sentry error alerting (ADR-014)
Every unhandled exception in `apps/web-next`, `apps/cms`, and `apps/web-tanstack` is captured via `ILogger.captureException` and routed to Sentry ([`docs/decisions/adr-014-instrumentation-sentry.md`](../../decisions/adr-014-instrumentation-sentry.md)). Configure alert rules in Sentry for:
- Spike in `4xx` / `5xx` on auth or DSR routes
- New issue fingerprints in production environment
- Volume anomaly on `[FILL IN: Sentry project slug]`
**Verify DSNs are configured:**
```bash
# Each app has its own Sentry project
echo $WEB_NEXT_SENTRY_DSN
echo $CMS_SENTRY_DSN
echo $WEB_TANSTACK_SENTRY_DSN # if TanStack Start app is in use
```
All three must be non-empty in production. A missing DSN silently disables capture for that app.
#### Audit log anomalies (ADR-018)
The audit channel ([`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md)) records every `AuditAction` emitted by feature use cases via `AuditLogProtocol.record()`. Actions relevant to breach detection:
| `AuditAction` | Breach signal |
| ------------------- | ----------------------------------------------------- |
| `EXPORT` | Mass export by non-admin actor or from unexpected IP |
| `VIEW` | High-frequency reads on a single `actorId` |
| `PERMISSION_CHANGE` | Privilege escalation outside change-management window |
| `DELETE` | Bulk delete with no corresponding DSR request |
Query the Payload audit logs collection or the aggregated cold archive (`[FILL IN: log aggregation URL, e.g. Vector / Fluent Bit sink]`) for anomalies. Correlate with OTel `correlationId` (trace ID auto-populated by `TraceIdEnrichingAuditLog`).
#### Rate-limit exhaustion (ADR-025 § Epic C)
The rate-limit primitive (`IRateLimit` in `core-shared/rate-limit`, see [`docs/guides/rate-limiting.md`](../../guides/rate-limiting.md)) emits `{ allowed: false, remaining: 0, resetAt }` when a budget is exhausted. Repeated exhaustion on auth or DSR endpoints may indicate credential-stuffing or automated exfiltration.
Check budget tables declared in feature manifests (`rateLimit: { window, budget }`) against observed traffic in `[FILL IN: metrics dashboard URL]`.
#### Security header violations
HSTS, `X-Frame-Options`, and CSP headers are emitted by the security-headers middleware ([`docs/guides/security-headers.md`](../../guides/security-headers.md)). CSP violation reports (if `report-uri` is configured) surface attempted XSS. Check `[FILL IN: CSP report endpoint / dashboard]`.
### 4.2 Manual discovery
If detection was by human observation (e.g., responsible-disclosure report, darknet alert):
1. Log the reporter's contact and discovery timestamp.
2. Do not confirm or deny breach details to the reporter until DPO is engaged.
3. Proceed to Phase 2.
---
## 5. Phase 2 — Triage
**Target time: within [FILL IN: e.g. 2 hours] of detection.**
### 5.1 Triage checklist
- [ ] Page Incident Commander and DPO.
- [ ] Open incident channel: `[FILL IN: e.g. #incident-YYYYMMDD in Slack / Teams]`.
- [ ] Document initial facts: detected at, detected by, affected system(s).
- [ ] Classify severity (§ 2).
- [ ] Determine if personal data is involved (see § 5.2).
- [ ] Initiate 72-hour DPA notification clock if P0 or P1 (§ 7.1).
### 5.2 PII impact assessment
Use the generated data map (`compliance/data-map.yml`, generated by `pnpm compliance:data-map` from Payload `custom.pii` field tags) to enumerate which collections and fields may be affected. Cross-reference with the scope of the incident (endpoint, DB table, S3 bucket, etc.).
Key questions:
- Which `piiCategory` values are in the affected scope? (e.g. `contact-email`, `identification-name`)
- Are any `restrictable: true` subjects affected (i.e. users who invoked Art. 18)?
- Was the `auditLogs` collection itself compromised? (Audit trail integrity is required for Art. 33 notification.)
---
## 6. Phase 3 — Containment
**Target time: within [FILL IN: e.g. 4 hours] of confirmation for P0/P1.**
### 6.1 Immediate actions
- [ ] Revoke or rotate compromised credentials (`[FILL IN: credential management system]`).
- [ ] Block actor at network/WAF level if attack is ongoing (`[FILL IN: WAF / CDN control panel URL]`).
- [ ] If a specific `actorId` is confirmed malicious: suspend account in Payload admin (`apps/cms`, port 3001).
- [ ] If a tRPC or REST route is the attack vector: deploy an emergency rate-limit tightening or disable the route (`[FILL IN: deployment method]`).
- [ ] Preserve evidence: take read-only snapshots of affected tables, CloudWatch / log streams, and network flow logs before any cleanup.
### 6.2 Audit trail preservation
The audit log collection is append-only (`update: () => false` Payload access rule per ADR-018). Do not attempt to delete or modify audit entries. If the audit collection itself is a target, freeze DB-level access and take a snapshot:
```bash
# [FILL IN: adapt to your database provider]
pg_dump --schema-only --table=audit_logs $DATABASE_URL > audit_logs_schema_snapshot.sql
pg_dump --data-only --table=audit_logs $DATABASE_URL > audit_logs_data_snapshot.sql
```
---
## 7. Phase 4 — Notification
### 7.1 Regulatory notification deadlines
| Obligation | Deadline | Trigger |
| -------------------------------------------- | -------------------------------- | -------------------------------------------------------------------------- |
| GDPR Art. 33 — notify Supervisory Authority | **72 hours** from becoming aware | Any breach involving personal data unless unlikely to risk rights/freedoms |
| `[FILL IN: national DPA requirement]` | **[FILL IN: e.g. 24 hours]** | `[FILL IN: condition]` |
| GDPR Art. 34 — notify affected data subjects | Without undue delay | High risk to rights and freedoms |
**Clock start:** the moment the organisation "becomes aware" — typically when the Incident Commander or DPO confirms the incident in § 5.1, not when it was first detected.
### 7.2 Supervisory Authority notification (Art. 33)
Prepare the notification using the SA's online portal (`[FILL IN: URL]`). Required fields per Art. 33(3):
- Nature of the breach (categories and approximate number of records / data subjects)
- Name and contact details of DPO: `[FILL IN: DPO name, email, phone]`
- Likely consequences of the breach
- Measures taken or proposed
Attach:
- Incident timeline (from detection to containment)
- Affected `piiCategories` from `compliance/data-map.yml`
- Audit log extract covering the incident window (redact unrelated subjects)
**If full facts are not available within 72 hours:** submit an initial notification marked "partial" and follow up without delay. GDPR Art. 33(4) explicitly permits phased notification.
### 7.3 Data subject notification (Art. 34)
Threshold: notification is required when the breach is likely to result in **high risk** to the rights and freedoms of individuals (e.g. identity theft, financial loss, discrimination).
- Drafting: `[FILL IN: communications lead]` drafts notice in plain language.
- Channel: `[FILL IN: e.g. in-app banner + email via transactional provider]`
- Content: nature of breach, DPO contact, likely consequences, mitigation steps.
- Timing: `[FILL IN: target send window, e.g. within 48 hours of Art. 33 notification]`
### 7.4 Internal stakeholder notification
| Stakeholder | When | Channel |
| ----------------------- | ---------------------------- | ----------- |
| Executive team | P0/P1: immediately | `[FILL IN]` |
| Customer success | Before customer-facing comms | `[FILL IN]` |
| Board / audit committee | Within `[FILL IN]` hours | `[FILL IN]` |
---
## 8. Phase 5 — Post-mortem
**Target: within [FILL IN: e.g. 5 business days] of containment.**
### 8.1 Post-mortem checklist
- [ ] Timeline reconstruction (detection → triage → containment → notification).
- [ ] Root cause analysis (five-whys or equivalent).
- [ ] Blast radius: confirmed data subjects affected, `piiCategories` exposed, duration of exposure.
- [ ] Audit log review: were anomalous `AuditAction` entries present before detection? Were `from.ipTruncated` values suspicious?
- [ ] Control gaps identified.
- [ ] Remediation actions with owner and deadline.
- [ ] Lessons learned.
### 8.2 Remediation tracking
Document each action in `[FILL IN: issue tracker, e.g. Linear / GitHub Issues]` with:
- Linked ADR or guide (e.g. ADR-018, ADR-014)
- Owner
- Target date
- Verification step (e.g. `pnpm fallow:audit` green, new test added)
### 8.3 Mandatory follow-up with Supervisory Authority
If the Art. 33 notification was submitted as "partial", file the complete notification with the SA by `[FILL IN: agreed follow-up date]`.
---
## 9. Appendix — Command Reference
```bash
# Inspect running apps and their Sentry DSN status
echo $WEB_NEXT_SENTRY_DSN && echo $CMS_SENTRY_DSN
# Run fallow audit to surface dead exports / drift before post-mortem PR
pnpm fallow:audit
# Regenerate data map to confirm affected PII fields
pnpm compliance:data-map
# Re-emit all compliance artifacts (data-map + retention-policy + sub-processors)
pnpm compliance:emit-all
```
---
## 10. Document Control
| Field | Value |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Owner | [FILL IN: DPO or Engineering Lead] |
| Review cycle | [FILL IN: e.g. annual or after every P0/P1] |
| Next review date | [FILL IN: YYYY-MM-DD] |
| Cross-references | [`docs/decisions/adr-018-audit-and-compliance.md`](../../decisions/adr-018-audit-and-compliance.md), [`docs/decisions/adr-014-instrumentation-sentry.md`](../../decisions/adr-014-instrumentation-sentry.md), [`docs/decisions/adr-025-eu-compliance-baseline.md`](../../decisions/adr-025-eu-compliance-baseline.md), [`docs/guides/audit-and-compliance.md`](../../guides/audit-and-compliance.md), [`docs/guides/rate-limiting.md`](../../guides/rate-limiting.md), [`docs/guides/security-headers.md`](../../guides/security-headers.md) |