#24A01 · A02 · API3 BOPLA

Data Privacy / PII

PII inventory, redaction, retention, and over-returned response fields.

Download .md

How to use this prompt

  1. 1
    Install SecureNow in your project (then optionally npx securenow login):
    $ npm install securenow
  2. 2Copy the prompt below and paste it into your AI coding agent (Claude Code, Cursor, Codex…) opened at the root of your project.
  3. 3It generates four files into threat/24-data-privacy-pii/ — open data-privacy-pii-code-findings.html (the audit) and data-privacy-pii-detection-mitigation.html (the defenses) in your browser.

🔒Runs entirely in your environment — your codebase is never uploaded or shared. The generated HTML reports are self-contained and work offline.

The prompt

# Data Privacy / PII Threat Model — Generator Prompt

A **copy-paste prompt** for customers. Paste the entire prompt below into an AI coding agent
(Claude Code, Cursor, Codex, …) opened at the root of **any project** that has the `securenow`
CLI installed and logged in. The agent will inventory where personal & sensitive data is
collected, flows, is logged, and is returned; build an exhaustive **data-privacy / PII** threat
model mapped to **OWASP A01:2021, A02:2021, A08:2021, API3:2023 (BOPLA)** and the **GDPR / CCPA
data-protection principles**; audit the code for PII collection/leak/retention flaws; and emit a
SecureNow-branded set of reports in **Markdown + self-contained HTML** across two tracks — a
**Detection & Mitigation** runbook and a **Code Findings & Recommendations** audit — including
the detection rules to create (each grounded in the **installed** SDK and given as a
**ready-to-copy** SQL → save → create → dry-run unit), the mitigation commands to run, how to
test each one, the code-level findings (audited, **not** fixed), and which threats still need the
SecureNow team.

This is the **data-governance lens**. It owns the *lifecycle* of personal data: what is
collected (inventory & minimization), where it leaks (URLs / logs / telemetry / responses /
third parties), how it is redacted, and how it is retained/deleted. It is **not** a record-level
access-control model and it is **not** a storage-internals model. It **references and defers**
the neighboring models and detects only the *observable abuse* of privacy data (bulk PII pulls,
export-flow abuse, anomalous response sizes). Run the deferred siblings too — together they cover
the full data-protection picture.

> SecureNow is fundamentally an **API / traffic** security layer with a built-in **PII
> redaction** pipeline. For this domain its native coverage is **MEDIUM**: the redaction engine
> (Authorization/Cookie/emails/tokens/cards out of telemetry) is a genuine, shipping control;
> traffic detection catches **bulk-PII pulls, data-export abuse, and anomalous response sizes**;
> and the audit surfaces capture points & over-returned response fields. But **retention,
> consent, data residency, re-identification, and sub-processor governance are app/process
> fixes** — SecureNow contains the *abuser of* a privacy flow at the edge; it cannot fix a
> missing retention policy or a non-consented tracking pixel. Pair every edge-containment row
> with the app/process fix; many rows here are **governance-audit findings**, not detection rules.

Requirements on the customer machine: `npm i -g securenow && securenow login` (admin auth +
app runtime connected). Everything else is discovered by the agent.

---

<!-- ════════════════ COPY EVERYTHING BELOW THIS LINE ════════════════ -->

# Generate a Data Privacy / PII Threat Model Report (SecureNow)

You are a senior application-security & privacy engineer. Produce an **exhaustive data-privacy /
PII threat model for THIS codebase**, organized along **OWASP A01:2021 (Broken Access Control),
A02:2021 (Cryptographic Failures), A08:2021 (Software & Data Integrity Failures), API3:2023
(Broken Object Property Level Authorization / BOPLA)**, and the **GDPR / CCPA data-protection
principles** (lawfulness, purpose limitation, data minimization, accuracy, storage limitation,
integrity & confidentiality, accountability; CCPA right-to-know / delete / opt-out). Map every
threat to **SecureNow** detections and mitigations, with a ready-to-run action plan **and** a
code-level audit of every PII-handling flaw you find. You write **four** deliverables into
`threat/24-data-privacy-pii/` (create the folder if needed) — two tracks, each as Markdown + a
**self-contained** HTML page (inline CSS + offline copy buttons, no network requests):

1. `data-privacy-pii-detection-mitigation.md` — the **operational runbook**: what to run in
   SecureNow (rules, mitigations, tests, runbooks). Every detection rule is grounded in the
   **installed** SDK and emitted as a **ready-to-copy** unit (SQL → save → create → dry-run).
2. `data-privacy-pii-detection-mitigation.html` — the same runbook as a self-contained HTML page
   with **copy buttons** on every command block.
3. `data-privacy-pii-code-findings.md` — the **code audit**: PII-handling issues in the codebase
   + recommended fixes (described, never applied).
4. `data-privacy-pii-code-findings.html` — the same audit as a self-contained HTML page.

The two tracks **cross-link** each other: the gaps/instrumentation rows in the detection report
link to the relevant code finding, and each code finding links back to the detection row it backs.

Work in the seven phases below, in order. **Never invent facts**: if something is not in the
codebase or not returned by a CLI command, say "not found" — do not guess. **Do not modify
application code.** You are auditing: every code-level fix is *described in the report*, never
applied to the repo.

**Scope discipline.** This model owns the **data-governance lifecycle**: PII inventory &
minimization, leakage paths (URLs/query/Referer/logs/telemetry/pixels/third-party calls),
redaction in the SDK/log pipeline, over-returned response attributes (**BOPLA**), retention &
deletion, DSAR / export / right-to-be-forgotten flows, re-identification / pseudonymization,
data residency / cross-border transfer, sub-processor sharing, and consent / purpose limitation.
It does **not** re-derive these neighbors — list them in the "Deferred to sibling models"
subsection, link them, and model only their **privacy-observable** symptoms:

- **Record-level access control** (who may read a given person's record) →
  [`../02-authorization/`](../02-authorization/authorization-threat-model-prompt.md). Here, model
  only the *observable* bulk-read / enumeration of PII, not the per-object authz logic.
- **Tenant data boundaries** (cross-customer data bleed) →
  [`../03-tenant-isolation/`](../03-tenant-isolation/tenant-isolation-threat-model-prompt.md).
- **At-rest encryption & log mechanics** (bucket ACLs, log injection, retention of *logs*) →
  [`../23-storage-and-logs/`](../23-storage-and-logs/storage-and-logs-threat-model-prompt.md).
  Here, model *what PII the logs/telemetry contain*; defer *how the log store is secured*.

---

## Phase 0 — Verify SecureNow tooling

Run and record (use `--json` where supported):

```bash
securenow doctor              # connectivity must be healthy
securenow whoami              # admin auth + runtime app
securenow status --json       # app key(s), environment, firewall state
securenow alerts rules --json # detection rules that already exist (incl. system signature rules)
securenow automation --json   # blocklist automations that already exist
securenow challenge list --json   # CAPTCHA / proof-of-work challenge rules
securenow env --json          # resolved SDK config (service name, endpoints, redaction config)
```

If the CLI is missing or not logged in, **stop** and tell the user to run
`npm i -g securenow && securenow login`, then re-run this prompt. Capture the **app key**
(UUID) — every rule and command in the report must use it. If multiple apps exist, ask the user
which app this codebase maps to before continuing. From `securenow env --json` note the
**telemetry redaction configuration** in particular — the default sensitive-field set, any
`capture.sensitiveFields` extras, and the `SECURENOW_SENSITIVE_FIELDS` env var (this is the
center of the PII pipeline and is referenced throughout Phase 4).

---

## Phase 0.5 — Ground every rule & command in the INSTALLED SDK

Before writing any SQL or CLI, read the SecureNow SDK that is actually installed in this repo so
every alert rule and command is correct for THIS version — never guess flags, subcommands, event
names, or SQL columns:

```bash
cat node_modules/securenow/package.json    # installed SDK version (record it in both reports)
ls node_modules/securenow                  # exported modules: events, sessions, register, run, …
ls node_modules/securenow/dist 2>/dev/null # built entrypoints / bundled CLI
npx securenow --help                       # top-level commands available in this version
npx securenow alerts rules --help          # exact create flags: --name/--sql/--apps/--severity/--schedule/--nlp
npx securenow event --help                 # `event send` shape for synthetic tests
npx securenow ratelimit --help; npx securenow challenge --help
npx securenow blocklist --help; npx securenow automation --help; npx securenow trusted --help
```

If `node_modules/securenow` is absent, run `npm ls securenow`; if still missing, tell the user to
`npm i securenow` (or `npm i -g securenow`) and stop. EVERY command, flag, `track('…')` event
name, and SQL column you emit MUST be one the installed SDK/CLI actually exposes. If the installed
version lacks a capability this prompt references, emit the rule but annotate it
`# requires securenow >= <version>` instead of a broken command. Record the resolved version in
the appendix of BOTH reports.

This domain's redaction references are a real, shipping part of the SDK — `securenow redact`,
the `SECURENOW_SENSITIVE_FIELDS` env var, and `securenow config set capture.sensitiveFields` —
but **verify them against the installed version here** (confirm `npx securenow redact --help`
and that `config set capture.sensitiveFields` exists in `npx securenow config --help`). In
Phase 4 and Phase 5, treat `node_modules/securenow` + `--help` as the source of truth: the
`securenow/events` `track()` signatures, the `securenow alerts rules` SQL columns, the redaction
field-name set, and every mitigation subcommand are discoverable there. Cross-check before
emitting.

---

## Phase 1 — Inventory the personal-data surface (codebase analysis)

Data privacy starts with a **PII inventory & data-flow map** — what personal data is *actually*
collected, where it travels, where it is recorded, and where it is returned. Document what
exists, not what the privacy policy claims. Cover at minimum:

- **PII / sensitive-data inventory** — enumerate every field of personal data the app collects
  or stores: direct identifiers (name, email, phone, address, username), government IDs (SSN,
  passport, tax ID, driver's licence), financial (card/PAN, IBAN, account/routing), health,
  biometric, precise geolocation, device IDs / IPs, and any **special-category** data (race,
  religion, sexual orientation, political, union, criminal). For each, record: **source**
  (form/import/third-party), **store** (table/collection/bucket), **purpose**, and **owner**.
  Fields with no documented purpose or owner are **inventory drift** (GDPR accountability).
- **Data-minimization posture** — flag fields collected/retained that the stated feature does
  not need (full DOB where age-band suffices, full address where postcode suffices, raw IP where
  truncated suffices, free-text fields harvesting incidental PII). (GDPR data minimization.)
- **PII in URLs / query strings / path params** — find personal data placed in `GET` URLs
  (`?email=`, `/users/<email>`, tokens, `?ssn=`), which then leaks into **access logs, proxy
  logs, browser history, Referer headers, CDN logs, and SecureNow traces** (`http.target`).
- **Referer / analytics / pixel leakage** — third-party analytics, tag managers, ad pixels,
  session-replay (FullStory/Hotjar/LogRocket), error trackers (Sentry), and any client script
  that can capture form fields, URLs, or DOM containing PII; whether they run before consent.
- **Outbound / third-party / sub-processor calls** — every place PII leaves the system to a
  third party (payment processor, email/SMS, CRM, enrichment, LLM provider, maps, support tool,
  data warehouse). For each: what fields are sent, under what contract/DPA, in what region.
- **Over-returned response fields (BOPLA)** — for each read endpoint, compare the response DTO
  to what the client actually needs. Flag responses that serialize the whole DB row (internal
  flags, other users' emails/phones, password hashes, tokens, soft-deleted records, admin-only
  attributes, audit fields). (OWASP API3 / BOPLA.)
- **Telemetry & log PII capture** — what the SecureNow SDK and app loggers actually write:
  request bodies, headers (`Authorization`/`Cookie`), URLs, response bodies, event attributes,
  error stacks containing user objects. Confirm whether the **SecureNow redaction pipeline** is
  engaged and what its sensitive-field set covers (see Phase 4a). Anything PII reaching telemetry
  **unredacted** is a finding.
- **Retention & deletion** — for each PII store, is there a documented TTL / retention period?
  Is there a deletion path? When a user is "deleted", does the data actually disappear from
  **primary store, backups, caches, search indexes (Elasticsearch/Algolia), analytics, data
  warehouse, and third parties**, or only soft-deleted? (GDPR storage limitation / erasure.)
- **DSAR / export / erasure flows** — locate any "download my data", "export", "delete my
  account", or admin data-export endpoint. Note: authentication strength, rate/volume limits,
  whether one user can request another's data, and what the export *contains* (over-broad dumps).
- **Anonymization / pseudonymization** — find any "anonymized"/"aggregated" dataset or
  pseudonymized identifier (hashed email, tokenized id). Assess re-identification risk: unsalted
  hashes, low-cardinality quasi-identifiers, reversible tokens, mapping tables kept alongside.
- **Data residency / cross-border** — where is PII stored & processed (DB region, bucket region,
  CDN edge, backup region, third-party region)? Any EU PII flowing to US/other regions without a
  transfer mechanism (SCCs/adequacy)? Hard-coded regions / endpoints reveal this.
- **Consent & purpose limitation** — is there a consent mechanism (cookie banner, marketing
  opt-in, ToS acceptance)? Is data used only for the purpose consented to? Do analytics/marketing
  scripts or third-party shares fire **before** consent? Is consent recorded & revocable?
- **SecureNow instrumentation already present** — `securenow/register` / `securenow run` /
  `securenow init` (traffic spans + redaction engine automatically), any `securenow/events`
  `track()` calls, and whether redaction config is customized. This determines what works
  *today* vs *after instrumentation*.

Output of this phase = the report's **Personal-data surface & inventory** section: the **PII
inventory table** (field / source / store / purpose / owner / special-category?), the
**data-flow map** (collect → store → process → share → delete, with third-party egress), the
**PII-in-URL/log list**, the **over-returned-response list** (endpoint / leaked attributes), the
**telemetry redaction status**, the **retention/deletion map**, the **DSAR/export flow notes**,
the **residency map**, the **consent posture**, and a short paragraph naming the real
privacy attack surface for this stack.

---

## Phase 2 — Enumerate threats (exhaustive catalog)

Evaluate **every** threat below against the discovered surface. Each item is either **modeled**
(a row in the threat matrix) or **explicitly N/A** (one line in an "Out of scope" subsection
with the reason — e.g. "Special-category data: N/A, app stores no health/biometric data"). Never
silently drop an item. Add stack-specific threats you discover that are not listed — this catalog
is the floor, not the ceiling. Tag each modeled row with its framework code: **A01 / A02 / A08**
(OWASP 2021), **API3** (BOPLA), and/or the relevant **GDPR/CCPA principle**
(`GDPR-min` minimization, `GDPR-purpose` purpose limitation, `GDPR-store` storage limitation,
`GDPR-erase` erasure, `GDPR-transfer` cross-border, `GDPR-account` accountability,
`GDPR-consent`, `CCPA-optout`), or "—".

**A. PII inventory drift & over-collection (GDPR accountability / minimization)**
1. Sensitive fields collected/stored with no documented owner or purpose (inventory drift)
2. Over-collection — PII gathered that the feature does not need (full DOB/SSN/address where less suffices)
3. Free-text / catch-all fields silently harvesting incidental PII (notes, metadata, custom attrs)
4. Special-category data (health/biometric/race/etc.) stored without the heightened legal basis
5. Shadow PII copies — the same person duplicated across stores/exports/spreadsheets with no inventory

**B. PII in transit through observable channels (A02 / GDPR confidentiality)**
6. PII in `GET` URLs / query strings (`?email=`, `?ssn=`, `/users/<email>`) → access/proxy/CDN logs
7. PII / tokens in path params surfacing in `http.target` of traces & logs
8. PII leaking via `Referer` header to third parties (URL contains PII, page links out)
9. PII captured by analytics / tag-manager / ad pixels / session-replay scripts
10. PII sent to third parties / sub-processors / LLM providers without a DPA or field-level control
11. PII in error messages / stack traces returned to the client or shipped to error trackers

**C. Over-returned response fields — BOPLA (API3)**
12. Read endpoint serializes the whole DB row (internal flags, other users' PII, audit fields)
13. List/search endpoint returns more attributes per item than the client needs (bulk attribute leak)
14. Admin-only / cross-user attributes returned to a normal user in the same response
15. Soft-deleted / archived records still serialized in responses
16. Mass-assignment inverse — response exposes fields that should be write-only or server-only

**D. Telemetry / log redaction gaps (A02 / A08 / GDPR confidentiality)**
17. `Authorization` / `Cookie` / `Set-Cookie` / `X-API-Key` captured unredacted by telemetry
18. Emails / phone / SSN / card / IBAN in request bodies captured unredacted
19. Tokens / JWTs / API keys / secrets in free-form string values captured unredacted
20. Sensitive response bodies captured by telemetry without redaction
21. App-level logger (winston/pino/console) writing user objects / PII to stdout / log files
22. PII becoming a SecureNow **event attribute** value (raw email/token in `track()` attrs)

**E. Retention, deletion & "deleted" data persistence (GDPR storage limitation / erasure)**
23. No retention period / TTL on a PII store (data kept indefinitely)
24. "Deleted" user data persisting in backups / snapshots after erasure
25. "Deleted" data persisting in caches / Redis / CDN after erasure
26. "Deleted" data persisting in search indexes (Elasticsearch/Algolia) / data warehouse / analytics
27. Soft-delete only — record flagged deleted but PII remains fully populated & queryable
28. PII still flowing through old / deprecated / unmonitored endpoints after a migration

**F. DSAR / export / right-to-be-forgotten abuse (A01 / CCPA / GDPR erasure)**
29. DSAR / data-export endpoint abused for **exfiltration** (one account pulls everything in scope)
30. DSAR export accessible across users (request another person's data) — BOLA on a privacy flow
31. Erasure / delete-account flow abused for **denial** (mass-delete, deleting another's account)
32. Export flow with no rate/volume limit → bulk scraping of personal data at scale
33. Unauthenticated / weakly-authenticated "verify identity" step on a DSAR (impersonation)

**G. Re-identification & weak pseudonymization (A02 / GDPR)**
34. "Anonymized"/"aggregated" dataset re-identifiable via quasi-identifiers (zip+DOB+gender)
35. Unsalted / fast-hash pseudonyms (raw SHA-256 of email) reversible by dictionary
36. Reversible tokenization with the mapping table stored alongside the pseudonymized data
37. Free-text or rare values defeating k-anonymity in an "anonymized" export

**H. Data residency / cross-border transfer (GDPR Chapter V)**
38. EU/regulated PII stored or processed in a non-adequate region without a transfer mechanism
39. Backups / DR / logs replicated to a different-region store than the primary data
40. Third-party / sub-processor processes PII in a region the customer did not consent to
41. CDN edge / cache storing PII responses outside the contracted region

**I. Third-party / sub-processor sharing & consent (GDPR purpose & consent / CCPA opt-out)**
42. Analytics / marketing / ad scripts firing **before** consent is obtained
43. PII shared with a sub-processor not under a DPA / not in the processor register
44. Purpose creep — data collected for X used for Y (e.g. support email used for marketing) without basis
45. No opt-out / "do not sell or share" honoring (CCPA) for tracking & data sharing
46. Consent not recorded / not revocable / not re-collected after policy change

**J. Observable abuse (what telemetry actually catches — the workhorse rules)**
47. Bulk-PII pull — one client retrieving an anomalous **count** of person-records (scraping)
48. Anomalous large response sizes from a single client (mass data extraction)
49. Data-export / DSAR endpoint hit at abnormal rate or volume by one IP/user
50. One IP touching an anomalous count of distinct user-record paths (PII enumeration)
51. Spike of access to a privacy-sensitive endpoint (export/profile/admin-user) from a new source
52. Repeated access to deprecated / shadow paths still carrying PII (inventory drift in traffic)

**K. Negative-space & evasion**
53. PII exfiltration paced under any rate limit (low-and-slow scraping evades volumetric rules)
54. PII pull spread across many IPs / one ASN to dodge per-IP detection
55. Redaction bypass — PII in an unexpected field/encoding the redactor doesn't match (base64, nested, custom key)
56. Export requested via an alternate/legacy endpoint not covered by detection

**L. Deferred — modeled in sibling models (reference, do not re-derive)**
57. Record-level / object-level access control on a person's data (who may read record N) → [`../02-authorization/`](../02-authorization/authorization-threat-model-prompt.md)
58. Cross-tenant PII bleed (customer A reads customer B's people) → [`../03-tenant-isolation/`](../03-tenant-isolation/tenant-isolation-threat-model-prompt.md)
59. At-rest encryption, bucket ACLs, log-store security & log injection → [`../23-storage-and-logs/`](../23-storage-and-logs/storage-and-logs-threat-model-prompt.md)

> For 57–59, add **one** matrix row each marked *"deferred — see linked model"*, and only note
> the privacy-observable symptom (e.g. bulk-read of person records, export spikes) here. The full
> detection/mitigation lives in the other reports — do not duplicate it.

> Every catalog item A1–L59 must be either a matrix row (with its framework tag) or an explicit
> N/A line with a reason — do not silently drop any item. Where a threat emits **no traffic and
> no event** (e.g. a missing retention policy, a non-consented pixel, an undocumented PII field),
> it is a **governance-audit finding**, not a detection rule: model it as a row whose "Detection"
> column is the explicit string *"governance-audit finding"* and whose mitigation is the
> **app/process fix**. Do not fabricate a detection rule for it.

---

## Phase 3 — Audit the code (findings only — do not fix)

For **each** modeled threat that maps to real code, locate the responsible code and record a
**finding** for the report's "Code-level findings" section. A finding is:

- **Location** — `file:line` (clickable), the route/handler/serializer/migration/script name.
- **Pattern** — quote the 1–8 relevant lines. State the missing control precisely. Cite, in
  particular, **where PII is collected, where it is logged, and where it is returned**:
  - *Collected:* a form/import/DTO that captures a field with no purpose (`ssn`, `dob`,
    free-text `notes`) → "collected without documented purpose/owner (inventory drift)".
  - *Logged / telemetered:* `logger.info(user)`, `console.log(req.body)`, `?email=` in a
    constructed URL, raw email/token passed to `track(...attributes)` → "PII reaches telemetry
    unredacted" / "PII placed in GET URL → leaks to logs & Referer".
  - *Returned:* `res.json(userRow)`, `SELECT *` serialized, `findMany` returning full records →
    "response serializes whole row incl. `<fields>` the client doesn't need (BOPLA)".
- **Why exploitable / why a violation** — the concrete request an attacker sends and what PII
  they obtain, **or** the concrete privacy principle breached (e.g. "any authenticated user's
  `/api/me` response includes `internalRiskScore` and `referrerEmail` of another user").
- **Severity** — critical / high / medium / low (sensitivity × volume × reachability).
- **Recommended fix (described, not applied)** — the specific change: e.g. "serialize an
  explicit response DTO / field allowlist, never the raw row"; "stop placing `email` in the URL,
  move it to the POST body or use an opaque id"; "add the field to the SecureNow redaction set
  and to `logger` redaction"; "set a retention TTL and a hard-delete job covering backups/cache/
  index"; "salt + slow-hash the pseudonym, drop the mapping table"; "gate the analytics script
  on recorded consent"; "rate-limit the export endpoint and bind it to the requesting user".
  Reference the secure pattern, not a code diff. **You must not edit the codebase.**

If a control exists and is correct (response DTO allowlist present, redaction engaged with the
right fields, retention job running, consent gating analytics), note it as a **strength** — the
posture must be honest. Absence of a control where PII exists is itself a finding ("no retention
policy on the `users` table", "no redaction config — defaults only — and the app stores `ssn`").

For each modeled threat **A1–L59** that maps to real code, record a finding the same way
(`file:line`, 1–8 quoted lines, why-violation, severity, described fix — **no code edits**). Look
specifically for:

**Inventory / minimization flaws** — schemas/migrations adding PII columns with no purpose;
forms/DTOs capturing more than the feature uses; free-text/JSON `metadata` columns; CSV/exports
that fan PII into uninventoried copies. *Recommended fixes must mention* documenting purpose &
owner per field, dropping/aggregating unneeded fields, and a maintained data inventory.

**Leakage-path flaws** — PII in constructed `GET` URLs / query strings / path params; PII in
links that leak via `Referer`; analytics/pixel/session-replay scripts loaded unconditionally;
PII sent to third parties/LLMs without field filtering; user objects / `req.body` in logger or
`console` calls; PII in error responses or error-tracker payloads. *Recommended fixes must
mention* moving PII out of URLs, `Referrer-Policy: no-referrer`, consent-gated/PII-scrubbed
analytics, field-level egress filtering, and logger redaction.

**BOPLA / over-return flaws** — handlers doing `res.json(row)` / `SELECT *` / `findMany()` with
no response DTO; serializers exposing internal/admin/other-user attributes; list endpoints
returning full objects; soft-deleted rows still serialized. *Recommended fixes must mention*
explicit response-field allowlists / DTOs, per-role serialization, excluding internal & soft-
deleted fields, and never reflecting the raw ORM object.

**Redaction-pipeline flaws** — telemetry/log capture without the SecureNow redaction engine
engaged; PII fields not covered by the default sensitive-field set and not added via
`SECURENOW_SENSITIVE_FIELDS` / `capture.sensitiveFields`; raw PII passed into `track()`
attribute values; custom field names (e.g. `taxNumber`, `memberId`) the default regex misses.
*Recommended fixes must mention* engaging redaction, extending the sensitive-field set to cover
this app's PII keys, hashing/omitting PII before it becomes an event attribute, and verifying
with `securenow redact`.

**Retention / deletion flaws** — no TTL/retention on PII stores; soft-delete only; delete paths
that miss backups/cache/search-index/warehouse/third-parties; no scheduled purge job; backups
kept forever. *Recommended fixes must mention* per-store retention periods, a hard-delete that
propagates to all copies (incl. backups & indexes & sub-processors), and a verifiable erasure job.

**DSAR / export flaws** — export/delete endpoints with weak auth, no per-user binding, no
rate/volume limit, or returning over-broad dumps; "verify identity" steps that are skippable.
*Recommended fixes must mention* binding the request to the authenticated subject, strong
identity verification, rate/volume limits, scoping the export to the subject's own data, and
audit-logging every DSAR.

**Anonymization / pseudonymization flaws** — unsalted/fast hashes of identifiers; reversible
tokens with adjacent mapping tables; "anonymized" exports retaining quasi-identifiers.
*Recommended fixes must mention* salted+slow hashing or keyed tokenization with the key held
separately, k-anonymity/generalization on exports, and removing the mapping table from the
shared dataset.

**Residency / transfer flaws** — hard-coded regions sending EU PII to non-adequate regions;
backups/logs/CDN in a different region than the primary store; sub-processors in unconsented
regions. *Recommended fixes must mention* region-pinning PII stores/backups/CDN, a transfer
mechanism (SCCs), and documenting each processor's processing region.

**Consent / purpose flaws** — analytics/marketing/ad scripts firing before consent; no consent
record or revocation; data reused beyond its collection purpose; no CCPA opt-out honoring.
*Recommended fixes must mention* consent-gating all non-essential trackers, recording &
honoring consent/opt-out, and limiting use to the consented purpose.

---

## Phase 4 — Map every modeled threat to SecureNow detection + mitigation

Classify each threat with exactly one coverage badge:

- 🟢 **COVERED** — detectable + mitigable with SecureNow today. For this domain that means: the
  **telemetry PII redaction** engine contains a leak class (D17–D22), or a **traffic rule** you
  provide the SQL for catches the *observable abuse* of a privacy flow (bulk-PII pull, export
  abuse, anomalous response size, PII-enumeration — catalog J). Honesty rule: redaction + the
  observable-abuse rules are the **only** truly native-covered classes here.
- 🟡 **PARTIAL** — works after the customer adds instrumentation (`track('pii.*')` /
  `track('dsar.*')` events), or SecureNow can only **contain the abuser at the edge** while the
  real fix is app/process-level (most leakage, BOPLA, and export-abuse rows — the detection
  contains the actor, the app fix removes the leak). Pair the control with the app/process fix.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this today: retention, residency, consent,
  re-identification, sub-processor governance, inventory drift with no traffic. **Still include
  it**: give the app/process fix, then add the line *"Requires SecureNow team — contact your
  SecureNow account contact (or in-dashboard support) to request support for this threat."*
  Collect all gaps in the report's "Known gaps & SecureNow feature requests" section.

> **Be honest about what is edge-detectable vs an app/process fix.** SecureNow sees **traffic**
> and **events** and it **redacts** what it captures; it contains actors via firewall /
> rate-limit / challenge / block / signature instant-block. It **cannot** see a missing
> retention policy, a non-consented pixel, an over-returned field that no attacker has yet
> abused, EU data sitting in the wrong region, or a reversible pseudonym — those are **app /
> process / governance fixes**. SecureNow detects the *abuse* of a privacy flow (the scrape, the
> export-flood, the enumeration) and contains the source; the missing governance control is the
> **primary fix**. A privacy flaw that emits no traffic and no event is a **governance-audit
> finding** (Detection = *"governance-audit finding"*) and stays 🟡/🔴 until the app emits the
> event in Phase 3's recommended fix.

Use **only** the SecureNow building blocks below. Never invent CLI flags, event names, or SQL
columns.

**Ready-to-copy command unit (required for every detection rule).** Each detection becomes a
**complete, copyable unit** — never a fragment. For each rule emit, in order: (1) the SQL, (2) a
line saving it to `rules/<name>.sql`, (3) the full `securenow alerts rules create …` command, and
(4) the dry-run test. In Markdown each is its own fenced block so it copies cleanly. The exact
flags must match `securenow alerts rules --help` from Phase 0.5; save each rule's SQL to
`rules/<name>.sql` so `--sql @rules/<name>.sql` works. Example unit:

````markdown
**Rule: PII — bulk person-record pull (single IP)** · 🟢 COVERED · API3 · high

```sql
-- rules/pii-bulk-pull.sql
WITH coalesce(nullIf(attributes_string['http.client_ip'],''), nullIf(attributes_string['net.peer.ip'],'')) AS client_ip
SELECT client_ip AS ip, count() AS requests, uniqExact(attributes_string['http.target']) AS distinct_paths
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
  AND timestamp >= now64(9) - INTERVAL 15 MINUTE
  AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
  AND kind = 2
GROUP BY ip HAVING ip != '' AND distinct_paths >= 200 AND requests >= 300
```

```bash
securenow alerts rules create \
  --name "PII: bulk person-record pull (single IP)" \
  --sql @rules/pii-bulk-pull.sql \
  --apps <APP_KEY> --severity high --schedule "*/5 * * * *" \
  --nlp "single IP pulling 200+ distinct person-record paths in 15 minutes"

securenow alerts rules test <RULE_ID> --mode dry_run --wait     # validate before it runs live
```
````

Note pre-existing / system rules (from Phase 0) instead of duplicating them. Injection-class
rows reference the **system signature rules + `instant.block`**, not duplicate pattern SQL. Keep
the SQL conventions, the **PII-redaction-engine centerpiece**, the **bulk-export / anomalous-
response** detections, the **governance-audit findings** framing, and the **app/process fix as
primary** that the rest of this phase specifies.

### 4a. Instrumentation — the PII redaction pipeline is the centerpiece

**The single most important SecureNow control for this domain is the built-in PII redaction
engine in the telemetry pipeline.** Once the app runs under `securenow run` /
`securenow/register` / `securenow init`, HTTP traffic is captured automatically (status, method,
path, client IP, response size) **and every captured value passes through redaction before
ingest** — so secrets/PII never reach the SecureNow store. Center the report's telemetry section
on this and verify its coverage against this app's actual PII fields.

**What the redaction engine scrubs by default** (verify via `securenow env --json` /
`securenow redact`):

- **Sensitive key names** (key-name match, substring, case-insensitive): `authorization`,
  `proxy-authorization`, `cookie`, `set-cookie`, `x-api-key`, `api_key`/`apikey`, `auth_token`,
  `access_token`, `refresh_token`, `id_token`, `token`, `jwt`, `session`/`sessionid`/`sid`,
  `password`/`passwd`/`pwd`, `secret`, `client_secret`, `private_key`, `credential(s)`, plus
  financial (`card`/`cardnumber`, `cvc`/`cvv`/`ccv`, `iban`, `account(number)`, `routing`,
  `sortcode`, `taxid`, `stripeToken`) and PII (`ssn`, `pin`, `email`/`e_mail`, `phone`/`mobile`,
  `dob`/`birthdate`, `firstname`/`lastname`/`fullname`, `address`, `postcode`/`zip`, `passport`,
  `license`, `otp`/`mfa`/`totp`). Matching is substring-wise (`card` also catches `creditCard`).
- **Value-shape redactors** (scrub secrets/PII in free-form string *values* even under a benign
  key): JWTs (`eyJ…`), `Bearer`/`Basic` header values, `sk_/pk_/rk_/snk_(live|test)_…` API keys,
  **email addresses**, and **Luhn-valid 13–19 digit card numbers**.
- **Headers & bodies** are redacted by `redactHeaders` / `redactBody`; values are capped in
  length; structured JSON is walked recursively (bounded depth).

**Extend it to THIS app's PII** — the default set is broad but app-specific keys (e.g.
`memberId`, `taxNumber`, `policyNumber`, `patientId`) need adding. Two equivalent ways:

```bash
# Env var (comma-separated extra field-name fragments, added to the built-in defaults):
export SECURENOW_SENSITIVE_FIELDS="memberid,taxnumber,policynumber,patientid"

# Or persisted in SecureNow app config:
securenow config set capture.sensitiveFields memberid,taxnumber,policynumber,patientid

# Verify redaction does what you expect on a sample payload (does not touch the app):
securenow redact '{"email":"a@b.com","memberId":"M-123","note":"call 4111 1111 1111 1111"}' \
  --fields memberid --json
```

> The redaction engine is the difference between SecureNow *helping* privacy and *becoming a new
> PII store*. Every PII field discovered in Phase 1 must be covered by the default set **or**
> added here. Any uncovered field is a **high-severity finding** (Phase 3) and the fix is this
> command.

Add `securenow/events` `track()` only for app-internal privacy signals traffic can't see (never
throws). Hash or omit any PII **before** it becomes an attribute value — attributes are *not*
re-redacted the way bodies are; a raw email in an attribute is a new leak:

```js
const { track } = require('securenow/events');

// A privacy-sensitive read returned person-records (feeds bulk-PII-pull detection):
track('pii.records.read', { userId, ip, attributes: { route: '/api/customers', count: '500', kind: 'list' } });

// The app's own redaction/guard stripped PII before it left (high-signal hygiene):
track('pii.redacted', { ip, attributes: { route: '/api/profile', field_class: 'email|phone|ssn|card', sink: 'log|telemetry|response|third_party' } });

// A data-subject-access / export flow executed (feeds export-abuse detection):
track('dsar.export.requested', { userId, ip, attributes: { subject_id_hash: '<hash>', scope: 'self|admin', bytes: '<number_as_string>' } });
track('dsar.export.completed', { userId, ip, attributes: { subject_id_hash: '<hash>', records: '<number_as_string>' } });

// A right-to-be-forgotten / erasure ran (feeds mass-delete detection):
track('dsar.erasure.requested', { userId, ip, attributes: { subject_id_hash: '<hash>', scope: 'self|admin' } });

// PII was shared with a third party / sub-processor (purpose & consent auditing):
track('pii.thirdparty.shared', { userId, ip, attributes: { processor: 'stripe|sendgrid|llm|crm', field_class: 'email|phone|address', consent: 'granted|missing' } });

// A consent decision was recorded / a non-essential tracker was gated:
track('consent.recorded', { userId, ip, attributes: { purpose: 'analytics|marketing|share', decision: 'granted|denied|revoked' } });
track('consent.blocked_tracker', { ip, attributes: { tracker: 'analytics|ads|replay', reason: 'no_consent' } });
```

Recommended PII event taxonomy — rules match these **exact strings**:

| Event | Emit when |
|---|---|
| `pii.records.read` | a read returns person-records (count feeds bulk-pull detection) |
| `pii.redacted` | the app's own guard stripped PII before a sink (log/telemetry/response/third-party) |
| `dsar.export.requested` | a data-subject export / "download my data" flow starts |
| `dsar.export.completed` | an export finishes (records/bytes feed volume detection) |
| `dsar.erasure.requested` | a right-to-be-forgotten / delete-account flow runs |
| `pii.thirdparty.shared` | PII is sent to a sub-processor / third party / LLM |
| `consent.recorded` | a consent grant/deny/revoke is recorded |
| `consent.blocked_tracker` | a non-essential tracker was blocked for lack of consent |

Custom `attributes` become queryable as `attributes_string['<key>']` (e.g.
`attributes_string['field_class']`). Ingest enriches every IP with **ASN/org** (`client.asn`,
`client.as_org`) — enabling datacenter-origin / scraper detection on bulk-pull rules with no
extra code.

### 4b. Detection rules — test mode for false-positive-prone rules

Alert rules have a lifecycle **mode**: `test` = **detect-only, NO mitigation** vs `prod` = full
(mitigation / auto-action armed) — plus a **status** (`Active | Disabled | Paused`). Manage with:

```bash
securenow alerts rules update <RULE_ID> --mode test     # detect-only: fires notifications, takes NO action
# …observe real traffic for several days; tune the threshold; add securenow fp exclusions for any FPs…
securenow alerts rules update <RULE_ID> --mode prod      # promote: arm the mitigation / auto-action
securenow alerts rules update <RULE_ID> --status Paused  # or --enable / --disable / --pause shortcuts
```

**Rule of thumb:** any detection that can **false-positive** — heuristic thresholds (bulk-pull /
scrape / enumeration counts, anomalous response-size, export-volume), broad patterns, anomaly /
volume rules, anything tuned to YOUR traffic — must ship in **`--mode test` first**. Run it
detect-only for **3–7 days of real traffic**, review what it flags (legitimate bulk exporters,
internal ETL, scheduled reporting jobs), raise/lower the threshold and add `securenow fp`
exclusions for legitimate hits, then `--mode prod` to arm mitigation. Only **high-precision** rules
(exploit-signature SQLi/XSS/RCE matches dumping a PII table, exact-match IoCs, known-bad ASN hits)
may go straight to `prod`. In the report, **tag each rule `test-first` or `prod-ready`** and say
why. (`securenow alerts rules test <id> --mode dry_run --wait` is the separate one-off *query*
validation — run it before either.) For this domain, the workhorse traffic/volume rules (bulk-PII
pull, anomalous response size, PII-record enumeration, export/DSAR volume, mass-erasure) are almost
all **`test-first`**; only the injection-signature `instant.block` path is **`prod-ready`**.

### 4b-bis. Detection rules — SQL conventions

Two query shapes. Both **must** keep the tenant scope and **must** select an `ip` column
(per-IP aggregation is what remediation/auto-block keys on). **The tenant-scope column differs by
table** — using the wrong one fails with `UNKNOWN_IDENTIFIER`:

- **logs/events** (`signoz_logs.distributed_logs_v2`) → `resources_string['service.name'] IN (__USER_APP_KEYS__)`
- **traces/HTTP** (`signoz_traces.distributed_signoz_index_v3`) → `` `resource_string_service$$name` IN (__USER_APP_KEYS__) ``

When grouping by `ip`, add `HAVING ip != '' AND …` so rows with no client IP don't aggregate
into an empty-key bucket. Traffic columns proven available: `response_status_code`, `kind`
(server span = 2), `ts_bucket_start`, `attributes_string['http.target']`, and the `client_ip`
coalesce below. Confirm any other column (`http.method`, response-size attribute) with a
`--mode dry_run` before relying on it; OTEL SDK versions vary
(`http.response.body.size` vs `http.response_content_length`).

**Traffic-based — bulk-PII pull / scraping of person-records (one IP, many record paths), no events:**

```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
       count() AS requests,
       uniqExact(attributes_string['http.target']) AS distinct_paths
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
  AND timestamp >= now64(9) - INTERVAL 15 MINUTE
  AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
  AND kind = 2
  AND (attributes_string['http.target'] LIKE '/api/users%' OR attributes_string['http.target'] LIKE '/api/customers%' OR attributes_string['http.target'] LIKE '/api/profile%')
GROUP BY ip
HAVING ip != '' AND distinct_paths >= 200 AND requests >= 300
```

**Traffic-based — anomalous large response sizes (mass PII extraction):**

```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
       count() AS responses,
       sum(toUInt64OrZero(attributes_string['http.response_content_length'])) AS total_bytes
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
  AND timestamp >= now64(9) - INTERVAL 15 MINUTE
  AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
  AND kind = 2
GROUP BY ip
HAVING ip != '' AND total_bytes >= 50000000
```

> Confirm the response-size attribute name with `--mode dry_run` first
> (`http.response_content_length` vs `http.response.body.size`); if neither is present on this
> app's spans, fall back to the request-count bulk-pull rule above and note response-size
> detection as 🟡 pending the attribute.

**Traffic-based — PII-record enumeration (many distinct user-id paths from one IP):**

```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
       uniqExact(attributes_string['http.target']) AS distinct_records,
       count() AS requests
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
  AND timestamp >= now64(9) - INTERVAL 15 MINUTE
  AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
  AND kind = 2
  AND attributes_string['http.target'] LIKE '/api/users/%'
GROUP BY ip
HAVING ip != '' AND distinct_records >= 100
```

**Traffic-based — access to deprecated / shadow paths still carrying PII (inventory drift):**

```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
       count() AS hits,
       uniqExact(attributes_string['http.target']) AS paths
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
  AND timestamp >= now64(9) - INTERVAL 30 MINUTE
  AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 30 MINUTE)) - 1800
  AND kind = 2
  AND (attributes_string['http.target'] LIKE '/v1/export%' OR attributes_string['http.target'] LIKE '%/legacy/users%')
GROUP BY ip
HAVING ip != '' AND hits >= 1
```

**Events-based — data-export / DSAR abuse (volume per client; query the logs table):**

```sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['scope']          AS scope,
  count() AS exports,
  sum(toUInt64OrZero(attributes_string['records'])) AS total_records
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] IN ('dsar.export.requested','dsar.export.completed')
  AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY ip, scope
HAVING ip != '' AND (exports >= 5 OR total_records >= 10000)
```

**Events-based — mass erasure / delete abuse (denial via right-to-be-forgotten):**

```sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['scope']          AS scope,
  count() AS erasures
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'dsar.erasure.requested'
  AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, scope
HAVING ip != '' AND erasures >= 10
```

**Events-based — PII shared with a third party without consent (purpose/consent auditing):**

```sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['processor']      AS processor,
  attributes_string['field_class']    AS field_class,
  count() AS shares
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'pii.thirdparty.shared'
  AND attributes_string['consent'] = 'missing'
  AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY ip, processor, field_class
HAVING ip != '' AND shares >= 1
```

The other catalog events follow the **same logs-table shape** — swap the `event.type` filter and
threshold: `pii.records.read` (sum the `count` attribute, ≥ a bulk threshold → app-level
bulk-pull confirmation), `consent.blocked_tracker` (≥1 → notify-only, route to the consent
runbook), `pii.redacted` (use to *confirm* the redaction guard is firing, notify-only).

**Injection / payload-borne PII theft (e.g. SQLi dumping a PII table) — use the SecureNow system
signature rules, don't write SQL.** SQLi/XSS/RCE detection ships as **system signature rules**
with synchronous **`instant.block`**. Confirm they're present and enabled for this app via
`securenow alerts rules --json`; enable `instant.block` rather than authoring duplicate pattern
SQL. (The *exfiltration* of PII via injection is detected here; the injection vector itself is
modeled in [`../06-injection/`](../06-injection/) — reference, don't re-derive.)

Useful attributes/columns: `event.type`, `http.client_ip`, `http.target`,
`response_status_code`, `kind`, `client.asn`, `client.as_org`, and your PII attributes
(`route`, `count`, `field_class`, `scope`, `processor`, `consent`, `records`).

Create + validate each rule as the **ready-to-copy unit** (save the SQL to `rules/<name>.sql`,
then create, then dry-run); flags must match `securenow alerts rules --help` from Phase 0.5:

```bash
securenow alerts rules create \
  --name "PII: bulk person-record pull (single IP)" \
  --sql @rules/pii-bulk-pull.sql \
  --apps <APP_KEY> \
  --severity high \
  --schedule "*/5 * * * *" \
  --nlp "single IP pulling 200+ distinct person-record paths in 15 minutes"

securenow alerts rules test <RULE_ID> --mode dry_run --wait
```

### 4c. The full SecureNow mitigation toolbox (select per threat)

For PII abuse, SecureNow **contains the actor at the edge** and **redacts** what telemetry
captures; the **app / process / governance fix** removes the underlying weakness. **The app/
process fix is the PRIMARY mitigation for this domain** — pair it with edge containment on every
leakage / BOPLA / export / retention / consent row. Once a threat is confirmed, **choose the
narrowest effective mitigation(s) from ALL of these** and combine them (e.g. rate-limit the
`/api/export` route + block the worst scraper IPs + challenge a NAT egress, while the redaction
engine keeps captured PII out of the store). Re-check every command/flag against the installed SDK
in Phase 0.5 (`securenow <cmd> --help`); annotate `# requires securenow >= <ver>` if absent. Scope
by **app / env / route / method / IP / duration** to avoid hitting real users. The SDK-verify note
and PII-redaction references throughout this phase still apply: `securenow redact`,
`SECURENOW_SENSITIVE_FIELDS`, and `securenow config set capture.sensitiveFields` are the core
native control and must be confirmed against the installed version.

| # | Mitigation | Command (ready-to-copy) | Use / scope |
|---|---|---|---|
| 1 | **Free firewall (network)** | `securenow firewall enable --app <APP_KEY> --env production` · `securenow run --firewall-only` · test `securenow firewall test-ip <ip> --path /x --method GET` | 500k+ known-bad IPs, hourly refresh; drop scanners/scrapers before they reach a PII endpoint. No app change. |
| 2 | **Exploit-signature instant block** | enable the `instant` config on the system SQLi/XSS/RCE signature rules (dashboard / MCP `securenow_alert_rule_instant_update`); custom rule → create with `--execution-mode instant` | synchronous ~2.6s block of an injection request attempting to dump a PII table. Don't duplicate pattern SQL. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "..."` | confirmed PII-exfiltration source, all routes. |
| 4 | **IP block — scoped to route (+ method)** | `securenow blocklist add <ip> --route /api/export* --mode prefix --method ALL --app <APP_KEY> --env production --reason "..."` (`--mode exact\|prefix\|regex`, `--method GET\|POST\|…\|ALL`) | block a scraper only on the privacy-sensitive paths (export/customers/users); least collateral. |
| 5 | **IP block — temporary / time-boxed** | `securenow blocklist add <ip> --duration 24h --reason "..."` (`30m`,`24h`,`7d`) · reverse `securenow blocklist unblock <id> --reason "..."` | auto-expiring containment of a bulk-pull source; audit-preserving unblock. |
| 6 | **Rate limit — per IP** | `securenow ratelimit add <ip> --limit 100 --window 1m --duration 24h --reason "..."` | throttle one scraping client across the app. |
| 7 | **Rate limit — per route (all clients, per-IP budget)** | `securenow ratelimit add --route /api/customers --mode prefix --method GET --limit 60 --window 1m --key-by ip` | cap an expensive/abusable PII-list endpoint for everyone, budgeted per IP. |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /api/export --mode exact --method POST --limit 5 --window 1m --duration 24h` · NL `securenow ratelimit from-text "rate limit /api/export to 5/min for 24h" --yes` · test `securenow ratelimit test <ip> --path /api/export --method POST` | precise throttle of one client on the DSAR/export route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /api/export --difficulty 16 --clearance 30m` (route-wide) **or** `securenow challenge add <ip> --route /api/customers --difficulty 18 --clearance 30m` · test `securenow challenge test <ip> --path /api/export --method GET` | bot scraping / export-flow abuse from **shared / NAT / CGNAT** egress — a human passes once, a script can't. Prefer over a hard block when real users share the IP. |
| 10 | **Auto-block (risk-scored)** | `securenow automation defaults --yes` (≥95→7d, 90–94→72h, 85–89→24h) · custom `securenow automation create --conditions '[...]' --actions '[...]'` · preview `securenow automation dry-run <id>` | hands-off blocking by risk score; actions include block / rate_limit / requireCaptcha. |
| 11 | **Session revocation** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | a stolen session exfiltrating PII — kill the session, not the IP. |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "Data-warehouse ETL / partner batch / monitor"` | stop false positives from known-good exporters/internal ETL — suppresses detection **and** mitigation. NOT deny-by-default. |
| 13 | **Allowlist (deny-by-default)** | `securenow allowlist add <ip> --label "..." --reason "..."` ⚠️ once any entry exists, ONLY listed IPs reach the app | lockdown of an internal/admin-only data-export surface. Never for a public app. |
| 14 | **False-positive exclusion** | `securenow fp create --conditions '[...]' --rule-scope this_rule --reason "..."` · `securenow fp mark <notification-id> <ip> --rule-scope this_rule` · preview `securenow fp dry-run --conditions '[...]'` | keep a noisy bulk-export / analytics-batch rule quiet without weakening it. |
| 15 | **App / config / governance fix (PRIMARY for root cause)** | *described in the Code-Findings report, never auto-applied* | the actual fix: response-DTO allowlist (BOPLA), move PII out of URLs, **extend redaction** (`SECURENOW_SENSITIVE_FIELDS` / `capture.sensitiveFields`), set retention TTL + propagating hard-delete, region-pin data, consent-gate trackers, DPA & processor register, salted/keyed pseudonyms. SecureNow contains/redacts; this fix removes. |

> The **PII redaction engine** (`securenow run` + `SECURENOW_SENSITIVE_FIELDS` / `securenow config
> set capture.sensitiveFields …`, verified with `securenow redact '<json>'`) is the core native
> control for the leakage class (D17–D22) — it is delivered through row 15's redaction-extension
> work and the telemetry pipeline in §4a, keeping emails/tokens/cards/PII out of the SecureNow
> store before any of the edge mitigations above apply.

**Choosing per threat** — by **confidence**: exploit-signature/exact IoC dumping a PII table →
instant-block or block; probable scraper/bot on shared egress → **challenge**; noisy/legit-mixed
bulk-export or list traffic → **rate-limit (test-mode first)**; session compromise exfiltrating
PII → **revoke**; known-good exporters/ETL noise → **trusted / fp**. By **blast radius**: always
scope to the narrowest `route`/`method`/`IP`/`duration` that stops the abuse; on NAT/CGNAT/shared
IPs prefer challenge/rate-limit over a hard block. For **bulk-export / DSAR abuse** specifically,
combine a per-route+IP rate-limit (row 8) or challenge (row 9) on `/api/export` with a temporary
block (row 5) of confirmed exfiltration sources — but always pair it with the **app/process fix
(row 15)**: SecureNow can only contain the *abuser of* a privacy flow at the edge, so for the many
governance classes (retention, consent, residency, re-identification, inventory drift,
sub-processor sharing) row 15 is the *entire* fix. Always pair an edge mitigation with the
app/config/governance fix (Code-Findings report) when SecureNow can only contain the actor.

Tier guidance: **challenge** sits between rate-limit and block — prefer it over a hard block when
the abusing IP may be a shared/NAT/CGNAT egress carrying real users. Reserve hard block for
confirmed PII-exfiltration or scanner traffic. Recommend **notify-only** (no auto action) for
false-positive-prone signals: legitimate bulk exporters, internal ETL, scheduled reporting jobs,
the `pii.redacted` / `consent.*` confirmation events — say so explicitly with the runbook command
the human runs after confirming. For the many **governance-audit findings** (retention,
residency, consent, re-identification, inventory drift, sub-processor sharing), there is **no
edge mitigation** — the entire fix is the app/process change; mark these rows accordingly and
route them to the gaps/action-plan, not to auto-block.

### 4d. Testing every detection and mitigation

Only test against apps/environments the user owns; prefer `--env local`/staging. For synthetic
source IPs use TEST-NET ranges (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`). Use only
**synthetic / fake** PII in tests — never real person data.

```bash
# Verify the redaction engine scrubs this app's PII (no app changes; pure local check):
securenow redact '{"email":"victim@example.com","memberId":"M-9","card":"4111 1111 1111 1111","jwt":"eyJabc.def.ghi"}' \
  --fields memberid --json
#   expect: email / card / jwt / memberId all [REDACTED]

# Synthetic DSAR/export abuse — exercise the export-volume rule end to end:
for i in $(seq 1 6); do
  securenow event send dsar.export.requested --ip 203.0.113.60 \
    --attrs scope=self,subject_id_hash=abc123,records=5000,test=true
done

# Synthetic mass-erasure (denial via right-to-be-forgotten):
for i in $(seq 1 12); do
  securenow event send dsar.erasure.requested --ip 203.0.113.60 \
    --attrs scope=admin,subject_id_hash=abc123,test=true
done

# Synthetic non-consented third-party share:
securenow event send pii.thirdparty.shared --ip 203.0.113.61 \
  --attrs processor=llm,field_class=email,consent=missing,test=true

# Validate a rule query without waiting for the schedule:
securenow alerts rules test <RULE_ID> --mode dry_run --wait

# Traffic-based rules (bulk-pull / enumeration / large-response) — generate spans, then check:
securenow test-span "threat-model.pii.smoke"
securenow forensics "requests and response sizes by IP to /api/customers in the last hour" --env production

# Mitigation verification:
securenow ratelimit test 203.0.113.60 --path /api/export --method GET
securenow challenge test 203.0.113.60 --path /api/export --method GET
securenow firewall test-ip 203.0.113.60 --app <APP_KEY> --env production

# Confirm + clean up:
securenow notifications list --limit 10
securenow blocklist list      # then: securenow blocklist unblock <id> --reason "threat-model test"
securenow challenge list      # then: securenow challenge remove <id>
```

Every 🟢/🟡 threat row in the report must have a concrete test recipe (commands + expected
outcome: which rule fires, which notification appears, what the mitigation does — or, for the
redaction control, what `securenow redact` returns). **Governance-audit findings (🔴) have no
detection test** — instead give the *verification step* (e.g. "query the `users` table for rows
older than the retention period", "load the page with consent denied and confirm no analytics
request fires in the network tab").

---

## Phase 5 — Write the FOUR reports (two tracks)

Write **four** files into `threat/24-data-privacy-pii/` — two tracks, each as Markdown + a
self-contained HTML page:

- `data-privacy-pii-detection-mitigation.md` / `.html` — the **operational runbook** (track A).
- `data-privacy-pii-code-findings.md` / `.html` — the **code audit** (track B).

The two tracks **cross-link**: each gaps / instrumentation row in the detection report links to
the relevant code finding, and each code finding links back to the detection row it backs. Keep
the title/subtitle **"Data Privacy / PII Threat Model"** on both.

### 5a. Detection & Mitigation report — sections (both .md and .html), in order

1. **Executive summary** — stats line (N threats modeled · N covered · N partial · N gaps ·
   N rules to create · N mitigations), top 3 **detectable** privacy risks for this stack, the
   installed `securenow` version + app key + firewall state, and a one-line framework-coverage
   note (which of A01/A02/A08/API3 + GDPR/CCPA principles are owned here vs deferred to siblings).
2. **SDK & environment** — installed SDK version (from `node_modules/securenow`), app key(s),
   environment, firewall state, existing rules / automations / challenge rules (from Phase 0), the
   system signature rules present, and the **telemetry redaction configuration in effect** (default
   sensitive-field set + `capture.sensitiveFields` / `SECURENOW_SENSITIVE_FIELDS` extras).
3. **Threat → Detection → Mitigation matrix** — one row per modeled threat:
   `# | Threat | Framework (A0x/API3/GDPR) | Coverage 🟢/🟡/🔴 | Detection (rule name / "governance-audit finding" / "—") | Signal (threshold+window) | Schedule | Sev | Mode (test-first/prod-ready) | Mitigation`.
   Severity ∈ {critical, high, medium, low}. Each row's **Mitigation** cell must name **specific,
   scoped mitigation(s) chosen from the §4c toolbox** (e.g. "rate-limit `/api/export` per IP+route
   (row 8) + temp block confirmed scrapers (row 5) + DTO allowlist app-fix (row 15)") — never a
   generic "block the IP." Each rule-backed row also carries a **`test-first` / `prod-ready`** tag
   in the Mode column (governance-audit / deferred rows use "—"). Include the deferred rows (57–59)
   pointing to the sibling models, then the "Out of scope" N/A list.
4. **Detection rules to create** — each as the **ready-to-copy unit** from Phase 4 (SQL → save to
   `rules/<name>.sql` → full `securenow alerts rules create …` → dry-run test). **Mark each rule
   `test-first` or `prod-ready`** (per §4b): every FP-prone rule (bulk-pull / scrape / enumeration /
   response-size / export-volume / mass-erasure) is created and shown with the `--mode test` →
   observe (3–7 days) → `--mode prod` promotion step; only the high-precision injection-signature
   path is `prod-ready`. Injection-class rows reference the **system signature rules +
   `instant.block`**, not duplicate SQL. Note rules that already exist (from Phase 0) instead of
   duplicating them.
5. **Instrumentation the detections need** — only the `track('pii.*')` / `dsar.*` / `consent.*`
   events the rules above consume, each as a copyable snippet; point to the **code-findings
   report** for *where* in the codebase to add them.
6. **Mitigation mechanisms** — render the **full §4c 15-row mitigation toolbox table verbatim**
   (firewall · exploit-signature instant-block · IP block [global / route+method / temporary] ·
   rate-limit [IP / route / IP+route] · challenge · auto-block · session revocation · trusted ·
   allowlist · fp · app/config/governance fix), the **PII-redaction-engine** centerpiece note, and
   the "Choosing per threat" paragraph. Then for each modeled threat give the **specific, scoped**
   ready-to-copy mitigation command(s) it selects from the toolbox, and reversibility. Make
   explicit that the **app/process fix (row 15) is primary** for the governance classes (retention,
   consent, residency, BOPLA, re-identification) and SecureNow is redaction + containment.
7. **Telemetry pipeline** — center on the **redaction engine**: what is captured today, the
   default sensitive-field set vs this app's PII fields, the `SECURENOW_SENSITIVE_FIELDS` /
   `capture.sensitiveFields` extras the customer must add (with the exact field names from Phase
   1/3, cross-linked to the code findings), then the `track()` flow — emit → **redact** → enrich
   → store → query.
8. **Action plan (copy-paste, ordered)** — ① engage `securenow run` + extend the redaction set to
   cover this app's PII fields, ② enable signature instant-block + firewall, ③ add the PII/DSAR
   event instrumentation where flows are blind, ④ create rules — **FP-prone rules created in
   `--mode test`** (detect-only) with an explicit **"promote to `--mode prod` after N days
   (3–7)"** step once thresholds are tuned and `securenow fp` exclusions added; only the
   high-precision injection-signature path armed straight to `prod`, ⑤ enable automations /
   challenge rules, ⑥ test, ⑦ verify in dashboard, ⑧ schedule the app/process governance work (from
   the code report: DTO allowlists, retention TTLs, consent gating, residency, DPAs). Real commands
   only, `<APP_KEY>` already substituted.
9. **Testing & validation** — per-threat test recipes (4d), incl. the `securenow redact`
   verification and the governance-finding verification steps (TEST-NET IPs
   192.0.2/198.51.100/203.0.113, synthetic PII only).
10. **Response runbooks** — for each notification type: what fired, how to confirm a true positive
    (e.g. is this a legitimate ETL exporter?), the exact command to respond (rate-limit /
    challenge / block), the exact command to reverse, and for consent/share findings the process
    step (revoke the tracker, sign the DPA).
11. **Known gaps & SecureNow feature requests** — every 🔴 threat (retention, residency, consent,
    re-identification, inventory drift, sub-processor governance): why it's not coverable today,
    the interim app/process fix (linked to the code report), and the "contact the SecureNow team"
    line.
12. **Appendix** — resolved SDK/CLI version, app key, environment, firewall state, redaction
    configuration in effect, rule IDs created, date, and a link to the code-findings report.

### 5b. Code Findings & Recommendations report — sections (both .md and .html), in order

State at the top: *"Findings only — no application code was modified."*

1. **Executive summary** — findings by severity (critical / high / medium / low), top 3 code
   risks for this stack, one-paragraph posture verdict.
2. **Personal-data surface & inventory** — the Phase 1 PII inventory table + data-flow map +
   PII-in-URL/log list + over-returned-response list + telemetry redaction status +
   retention/deletion map + DSAR/export notes + residency map + consent posture.
3. **Threat catalog** — the exhaustive Phase 2 catalog (A1–L59, grouped, each tagged
   A01/A02/A08/API3 and/or a GDPR/CCPA principle, modeled or explicit N/A).
4. **Code-level findings (audit — not applied)** — the Phase 3 findings: a table
   `# | Location (file:line) | Threat | Framework | Sev | Issue | Recommended fix`, each with the
   quoted 1–8 line snippet and the described fix, explicitly noting **where PII is collected /
   logged / returned**. Each finding cross-links the detection-report row it backs.
5. **Strengths** — controls already present and correct (response DTO allowlist, redaction
   engaged with the right fields, retention job running, consent gating analytics) — honest posture.
6. **App / config / governance fixes (primary remediation)** — the config/code/policy changes that
   remove the root cause (described, never applied): response-DTO allowlists (BOPLA), move PII out
   of URLs, extend redaction, set retention TTL + propagating hard-delete, region-pin data,
   consent-gate trackers, DPA & processor register, salted/keyed pseudonyms — each linked to the
   detection-report row it backs.
7. **Instrumentation recommendations** — the `track('pii.*')` / `dsar.*` / `consent.*` calls to
   add and the exact `file:line` to add them, so the detection rules light up; hash/omit PII
   before it becomes an attribute value.
8. **Appendix** — files reviewed, resolved SDK version, date, and a link to the
   detection-mitigation report.

### HTML reports — two self-contained skeletons (offline; inline CSS + copy JS; no network)

Both HTML files share the `<head>` below (brand tokens + copy-button styles) and the copy
`<script>` at the end of `<body>`. Change only the `<title>`, the sidebar subtitle, and the
section content — **track A** (Detection & Mitigation) gets sections 5a, **track B** (Code
Findings) gets sections 5b. Wrap **every** command/SQL/code block as a `.cmd` so it gets a Copy
button. Keep the existing `.m.redact` mitigation token.

```html
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title><!-- "Detection & Mitigation — Data Privacy / PII — SecureNow" OR "Code Findings — Data Privacy / PII — SecureNow" --></title>
<style>
  :root{--bg:#0f1419;--panel:#161c24;--panel2:#1b2330;--border:#26303d;--txt:#dbe3ec;--muted:#8b97a7;
    --accent:#3ea6ff;--accent2:#16c79a;--crit:#ff5c6c;--high:#ff9f43;--med:#f7c948;--low:#8b97a7;
    --ok:#16c79a;--info:#3ea6ff;--rev:#b388ff;}
  *{box-sizing:border-box}html{scroll-behavior:smooth}
  body{margin:0;background:var(--bg);color:var(--txt);font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif}
  a{color:var(--accent);text-decoration:none}
  code{background:#0b0f14;border:1px solid var(--border);border-radius:5px;padding:.08em .4em;font:13px/1.4 ui-monospace,"SF Mono",Menlo,Consolas,monospace;color:#9fe0c0}
  .wrap{display:grid;grid-template-columns:240px 1fr;max-width:1280px;margin:0 auto}
  nav{position:sticky;top:0;align-self:start;height:100vh;overflow:auto;padding:28px 18px;border-right:1px solid var(--border);background:var(--panel)}
  nav .brand{font-weight:700;font-size:15px;letter-spacing:.3px}nav .brand span{color:var(--accent)}
  nav .sub{color:var(--muted);font-size:12px;margin-bottom:22px}
  nav a{display:block;color:var(--muted);padding:7px 10px;border-radius:7px;font-size:13.5px}
  nav a:hover{background:var(--panel2);color:var(--txt)}
  main{padding:36px 40px 80px;min-width:0}
  header.top h1{margin:0 0 6px;font-size:26px}header.top p{margin:0;color:var(--muted)}
  .pill{display:inline-block;font-size:11px;font-weight:600;padding:3px 9px;border-radius:999px;border:1px solid var(--border);color:var(--muted);background:var(--panel)}
  .stats{display:grid;grid-template-columns:repeat(5,1fr);gap:14px;margin:26px 0 34px}
  .stat{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:16px 18px}
  .stat .n{font-size:26px;font-weight:700}.stat .l{color:var(--muted);font-size:12.5px;margin-top:2px}
  section{margin:0 0 40px}
  h2{font-size:18px;margin:0 0 14px;padding-bottom:8px;border-bottom:1px solid var(--border)}
  h2 .num{color:var(--accent);font-weight:700;margin-right:8px}
  table{width:100%;border-collapse:collapse;font-size:13.5px;background:var(--panel);border:1px solid var(--border);border-radius:12px;overflow:hidden}
  th,td{text-align:left;padding:11px 13px;border-bottom:1px solid var(--border);vertical-align:top}
  th{background:var(--panel2);color:var(--muted);font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.4px}
  tr:last-child td{border-bottom:none}tr:hover td{background:#19212c}
  .rid{font:12px ui-monospace,Menlo,Consolas,monospace;color:#7fd1ff;white-space:nowrap}
  .b{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;white-space:nowrap}
  .b.crit{background:rgba(255,92,108,.15);color:var(--crit);border:1px solid rgba(255,92,108,.35)}
  .b.high{background:rgba(255,159,67,.13);color:var(--high);border:1px solid rgba(255,159,67,.32)}
  .b.med{background:rgba(247,201,72,.13);color:var(--med);border:1px solid rgba(247,201,72,.32)}
  .b.low{background:rgba(139,151,167,.13);color:var(--low);border:1px solid rgba(139,151,167,.32)}
  .c{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:6px;white-space:nowrap}
  .c.cov{background:rgba(22,199,154,.13);color:var(--ok);border:1px solid rgba(22,199,154,.35)}
  .c.part{background:rgba(247,201,72,.13);color:var(--med);border:1px solid rgba(247,201,72,.32)}
  .c.gap{background:rgba(255,92,108,.15);color:var(--crit);border:1px solid rgba(255,92,108,.35)}
  .owasp,.cwe{display:inline-block;font:11px ui-monospace,Menlo,Consolas,monospace;color:var(--accent);border:1px solid rgba(62,166,255,.3);border-radius:6px;padding:1px 6px;white-space:nowrap}
  .cwe{color:var(--rev);border-color:rgba(179,136,255,.3)}
  .m{display:inline-block;font-size:11px;font-weight:600;padding:2px 8px;border-radius:6px;border:1px solid var(--border)}
  .m.block{color:var(--crit);border-color:rgba(255,92,108,.35)}.m.rate{color:var(--info);border-color:rgba(62,166,255,.35)}
  .m.challenge{color:var(--accent2);border-color:rgba(22,199,154,.35)}.m.firewall{color:var(--ok);border-color:rgba(22,199,154,.35)}
  .m.signature{color:var(--crit);border-color:rgba(255,92,108,.35)}.m.redact{color:var(--accent2);border-color:rgba(22,199,154,.35)}
  .m.notify{color:var(--muted)}.m.appfix{color:var(--high);border-color:rgba(255,159,67,.35)}
  .card{background:var(--panel);border:1px solid var(--border);border-radius:12px;padding:18px 20px}
  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
  pre{background:#0b0f14;border:1px solid var(--border);border-radius:10px;padding:14px 16px;overflow:auto;font:13px ui-monospace,Menlo,Consolas,monospace;color:#cfe8da;margin:0}
  .cmd{position:relative;margin:10px 0}
  .copy{position:absolute;top:8px;right:8px;font:11px ui-monospace,Menlo,Consolas,monospace;color:var(--muted);background:var(--panel2);border:1px solid var(--border);border-radius:6px;padding:3px 9px;cursor:pointer}
  .copy:hover{color:var(--txt);border-color:var(--accent)}.copy.done{color:var(--ok);border-color:var(--ok)}
  .flow{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:6px 0 14px}
  .flow .step{background:var(--panel2);border:1px solid var(--border);border-radius:9px;padding:8px 12px;font-size:13px}.flow .arr{color:var(--accent);font-weight:700}
  .note{border-left:3px solid var(--high);background:rgba(255,159,67,.06);padding:10px 14px;border-radius:0 8px 8px 0;color:#e7d3bd;font-size:13.5px;margin:10px 0}
  footer{color:var(--muted);font-size:12px;border-top:1px solid var(--border);padding-top:18px;margin-top:30px}
  @media(max-width:880px){.wrap{grid-template-columns:1fr}nav{display:none}.stats,.grid2{grid-template-columns:1fr 1fr}main{padding:24px 18px}}
</style></head>
<body>
<div class="wrap">
  <nav>
    <div class="brand">Secure<span>Now</span></div>
    <div class="sub"><!-- "Detection & Mitigation · Data Privacy / PII" OR "Code Findings · Data Privacy / PII" --></div>
    <!-- one <a href="#…"> per section of THIS track -->
  </nav>
  <main>
    <header class="top"><h1>Data Privacy / PII Threat Model</h1>
      <p><code><!-- app name / domain --></code> · <span class="pill">securenow <!-- installed version --></span></p></header>
    <div class="stats"><!-- 5 .stat cards; numbers MUST equal the table/finding counts of THIS track --></div>
    <!-- <section id="…"> blocks mirroring the Markdown sections of THIS track (5a or 5b) -->
    <footer>Generated by the SecureNow data-privacy / PII threat-model prompt · <!-- date --> · securenow <!-- version --> · app <code><!-- APP_KEY --></code></footer>
  </main>
</div>
<script>
document.querySelectorAll('.copy').forEach(function(b){b.addEventListener('click',function(){
  var pre=b.parentElement.querySelector('pre'); if(!pre)return; var t=pre.innerText;
  function done(){b.textContent='Copied';b.classList.add('done');setTimeout(function(){b.textContent='Copy';b.classList.remove('done');},1500);}
  function fb(){var ta=document.createElement('textarea');ta.value=t;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.focus();ta.select();try{document.execCommand('copy');}catch(e){}document.body.removeChild(ta);done();}
  if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(t).then(done,fb);}else{fb();}
});});
</script>
</body></html>
```

**Track A — stats cards** (Detection & Mitigation): threats modeled · covered · partial · gaps —
SecureNow team · rules to create. **Track B — stats cards** (Code Findings): critical · high ·
medium · low · total findings. Each track's five numbers MUST equal that track's table/finding
row counts.

Every SQL/command block in the **Detection & Mitigation** HTML uses the copyable wrapper:

```html
<div class="cmd"><button class="copy" type="button">Copy</button><pre>securenow alerts rules create \
  --name "..." --sql @rules/&lt;name&gt;.sql --apps &lt;APP_KEY&gt; --severity high \
  --schedule "*/5 * * * *" --nlp "..."</pre></div>
```

Badge usage: severity → `<span class="b crit|high|med|low">`, coverage →
`<span class="c cov|part|gap">COVERED|PARTIAL|GAP</span>`, framework tag →
`<span class="owasp">API3</span>` / `<span class="owasp">A02</span>` / `<span class="owasp">GDPR-store</span>`,
mitigation type → `<span class="m redact|firewall|signature|rate|challenge|block|notify|appfix">`,
rule IDs → `<span class="rid">`. Wrap every SQL/command/code block in `.cmd` with a Copy button.
The Code-Findings HTML may omit copy buttons on prose, but still wraps any example/fix command in
`.cmd`. Stats numbers must equal the matrix/findings row counts.

---

## Quality bar (the report is rejected if any of these fail)

- Every catalog item A1–L59 is either a matrix row or an explicit N/A line; each modeled row
  carries its framework tag (A01/A02/A08/API3 and/or a GDPR/CCPA principle, or "—").
- The redaction-gap class (D17–D22) is modeled, mapped to the **PII redaction engine**, and the
  report names *this app's* PII fields and whether they are covered by the default set or need
  `SECURENOW_SENSITIVE_FIELDS` / `capture.sensitiveFields`.
- BOPLA / over-returned-response items (C12–C16) are each modeled with the **app/process fix
  (response DTO allowlist) as primary** and edge containment as secondary.
- The governance classes that emit no traffic/event (retention E23–E28, residency H38–H41,
  consent I42–I46, re-identification G34–G37, inventory drift A1–A5) are modeled as
  **governance-audit findings** (Detection column = *"governance-audit finding"*), not fabricated
  detection rules, each with an app/process fix.
- Record-level access control, tenant boundaries, and at-rest/log mechanics are **deferred** to
  [`../02-authorization/`](../02-authorization/), [`../03-tenant-isolation/`](../03-tenant-isolation/),
  and [`../23-storage-and-logs/`](../23-storage-and-logs/) (rows present, linked, not re-derived).
- Every matrix row has a concrete signal (threshold + window) **or** the explicit
  "governance-audit finding" marker, plus severity and a mitigation — no "monitor for suspicious
  activity" filler.
- Every code finding in section 4 has a `file:line`, the quoted snippet, names where PII is
  collected/logged/returned, and a described fix — and **no application code was modified**.
- Every detection SQL keeps `__USER_APP_KEYS__` scoping (correct table column), selects an `ip`
  column, and `HAVING ip != ''`; traffic queries keep the `ts_bucket_start` + `kind = 2` guards
  and the `client_ip` coalesce.
- Injection-driven PII exfiltration references the **system signature rules + `instant.block`**,
  not duplicate pattern SQL; the injection vector itself is deferred to `../06-injection/`.
- Only commands, flags, events, and SQL columns from this prompt's building blocks appear
  (including `securenow redact`, `SECURENOW_SENSITIVE_FIELDS`, `securenow config set
  capture.sensitiveFields`, `challenge`, `firewall`, and the `pii.*` / `dsar.*` / `consent.*`
  events).
- Detection vs. fix is honest: where SecureNow can only contain the actor at the edge or only
  redact telemetry, the row pairs the control with the **app/process fix as primary**.
- Every 🔴 gap appears in the gaps section with an interim app/process fix **and** the "contact
  the SecureNow team" line.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted in, and step ① is extending
  the redaction set to this app's PII fields.
- **Phase 0.5 ran**: the resolved installed `securenow` version appears in BOTH reports'
  appendix, and no command/flag/event/column is emitted that the installed SDK/CLI does not
  expose (else it is annotated `# requires securenow >= <version>`). `securenow redact`,
  `SECURENOW_SENSITIVE_FIELDS`, and `securenow config set capture.sensitiveFields` were verified
  against the installed version.
- **Every detection rule is a complete copyable unit** (SQL → `rules/<name>.sql` → full
  `securenow alerts rules create …` → dry-run test); flags match `alerts rules --help`.
- **Four** files are written to `threat/24-data-privacy-pii/` (detection-mitigation .md+.html,
  code-findings .md+.html); the two tracks cross-link each other; both HTML files are
  self-contained (inline CSS/JS, no CDN/fonts/network) and **every command block in the detection
  HTML has a working Copy button** (`.cmd` + `<script>`).
- The split is honest: SecureNow-runnable detections/mitigations live in the Detection report;
  code/config/governance changes live in the Code-Findings report; nothing security-relevant is
  dropped; each track's stats cards equal that track's table/finding row counts.
- The Detection report's mitigation section presents the **full toolbox** (§4c: firewall ·
  instant-block · block [global / route / method / temporary] · rate-limit [IP / route / IP+route] ·
  challenge · auto-block · revoke · trusted · allowlist · fp · app-fix), and **each modeled threat's
  matrix row selects specific, scoped mitigation(s) from it** — never a generic "block the IP."
- **Every false-positive-prone rule is tagged `test-first`** and carries the `--mode test` →
  observe (3–7 days) → `--mode prod` promotion workflow; only high-precision rules
  (injection-signature `instant.block`, exact-match IoCs) are `prod-ready`. The action plan creates
  the test-first rules in `--mode test` and has an explicit "promote after N days" step.
- A one-line summary is printed back: per-track file paths, threat counts, rules-to-create count,
  code findings by severity, gaps, framework coverage, and the resolved SDK version.

<!-- ════════════════ END OF PROMPT ════════════════ -->