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
265 lines
15 KiB
Markdown
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) |
|