---
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) |
