# Secret & API-Key Leakage & Hygiene 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 **everywhere secret material lives and flows**
(source, config, git history, build output, client bundles, logs/telemetry/traces, URLs, CI logs,
container images), build an exhaustive **secret-leakage & hygiene** threat model mapped to **OWASP
A02:2021 Cryptographic Failures** (with **A05 Security Misconfiguration** and **A07 Identification &
Authentication Failures**) and **CWE-798 / CWE-312 / CWE-532 / CWE-200** (plus **PCI DSS 3.x** key
management), audit the code/build for exposed secrets, and emit a SecureNow-branded report in
**Markdown + self-contained HTML** — including the detection rules to create, the mitigation
commands to run, how to test each one, the code-level findings (audited, **not** fixed and **never
printed in full**), and which threats still need the SecureNow team.

This domain covers the **credentials your app depends on** — API keys, OAuth/PAT tokens, signing &
webhook secrets, private keys, DB/connection URIs, service-account blobs — and the **hygiene &
detection** that keeps them from leaking and catches them when they do. The defining property of
this surface is that **most of the controls are app/build/CI/cloud-config fixes** — secret scanning
(pre-commit / CI / repo-history), vaulting / secret managers, `.gitignore` discipline, short-lived
scoped keys, and rotation automation — none of which SecureNow applies for you.

> **Honesty up front: this is a MOSTLY app/build-fix domain.** SecureNow is an **API / traffic**
> security layer. It **cannot scan your git history, your built bundle, your container images, or
> your CI logs** — that is what gitleaks/trufflehog + a pre-commit/CI gate do, and that is the
> primary fix on most rows. What SecureNow *can* do: (1) ingest **secret-scanner findings** and
> **runtime exposure events** you emit (`secret.scan.finding` / `secret.exposure.detected`) and
> turn them into alerts; (2) detect a **secret value appearing in traffic it ingests** (request/
> response bodies, headers, query strings, logs) via its **redaction** posture; (3) detect and
> contain **abuse of a leaked/exposed key** in traffic (`secret.key.abuse`) — block the abusing IP,
> revoke the stolen session/token; (4) **strip secrets from telemetry** so SecureNow itself never
> becomes a leak path. Pair the edge control with the app/build/CI fix on **every** row, and mark a
> row 🔴 honestly when neither helps.

> **CRITICAL — do not exfiltrate the secret you find.** This audit *locates* exposed secrets; it must
> **never print a full secret value** into a report, a log, a commit, an event attribute, or an LLM
> prompt — that would *be* the leak. Quote at most a **masked prefix** (e.g. `sk_live_4eC…` /
> `AKIA…XYZ`) and the **file:line**. Hash or fingerprint before emitting any event. Do **not** paste
> a found secret into a search box, a third-party service, or this report.

This model **does not re-derive its siblings.** It references and defers to:
[../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/) for **cloud-IAM blast-radius** (role/policy
escalation, `PassRole`/assume-role chains, IMDS-SSRF → credential theft, cross-account trust, KMS/STS);
[../22-supply-chain-cicd/](../22-supply-chain-cicd/) for **CI/CD pipeline secret handling & artifact
integrity**; [../23-storage-and-logs/](../23-storage-and-logs/) for **at-rest storage & log mechanics**;
[../05-client-side-supply-chain/](../05-client-side-supply-chain/) for **client-bundle secret specifics**;
[../24-data-privacy-pii/](../24-data-privacy-pii/) for **PII (vs secret) governance**; and
[../07-oauth-sso-integrations/](../07-oauth-sso-integrations/) + [../14-api-security/](../14-api-security/)
for **API-token / PAT / OAuth issuance & SSO**.

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 Secret & API-Key Leakage & Hygiene Threat Model Report (SecureNow)

You are a senior application-security engineer specializing in secret management and credential
hygiene (hardcoded keys, git-history leaks, bundle/log exposure, exposed-key abuse, rotation &
vaulting). Produce an **exhaustive secret & API-key leakage threat model for THIS codebase**,
organized along **OWASP A02:2021 Cryptographic Failures** (primary), with **A05:2021 Security
Misconfiguration** and **A07:2021 Identification & Authentication Failures**, and mapped to
**CWE-798** (hardcoded credentials), **CWE-312/315** (cleartext storage), **CWE-532** (secret in log),
**CWE-200** (exposure), plus **PCI DSS 3.x** (key management). Map every threat to **SecureNow**
detections and mitigations, with a ready-to-run action plan **and** a code-level audit of every
exposed/poorly-handled secret you find. Every rule, flag, event name, and SQL column you emit must be
**grounded in the SecureNow SDK actually installed in this repo** (Phase 0.5), and every detection
must be a **ready-to-copy command unit** (SQL → `rules/<name>.sql` → full `securenow alerts rules
create …` → dry-run test). You write **four** deliverables — **two tracks**, each as Markdown + a
self-contained HTML page with **offline copy buttons** — into `threat/26-secrets-and-api-key-hygiene/`
(create the folder if needed):

1. `secrets-and-api-key-hygiene-detection-mitigation.md` — the **operational runbook**: what to run
   in SecureNow (detection rules, mitigation commands, instrumentation, action plan, tests).
2. `secrets-and-api-key-hygiene-detection-mitigation.html` — the same runbook as a **self-contained**
   HTML page (inline CSS + copy JS, no network requests), with a **Copy button on every command**.
3. `secrets-and-api-key-hygiene-code-findings.md` — the **code audit**: the exposed/mishandled secrets
   found in this codebase/build and the recommended fixes (described, **never** applied, **never** printed in full).
4. `secrets-and-api-key-hygiene-code-findings.html` — the same code audit as a self-contained HTML
   page. The two tracks **cross-link** each other.

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, build config, env files, or secrets.** You are auditing: every fix is *described
in the report*, never applied. **Never print a full secret value** anywhere — mask to a short prefix
and cite `file:line`; hash/fingerprint before emitting any event.

**Scope discipline.** This model owns the **secret-material leakage surface and hygiene**: secrets in
source/config/IaC/containers/**git history**, secrets in the **built bundle / source maps / public
env**, secrets in **URLs / logs / telemetry / traces / error responses**, **API-key/token scope &
lifetime**, **rotation**, **vaulting / secret manager** adoption, **`.gitignore`/env hygiene**,
**per-tenant/per-env separation**, **secret-scanning gates**, **telemetry redaction**, and **abuse of
a leaked key**. It does **not** re-derive:

- **Cloud-IAM blast-radius** (role/policy escalation, `PassRole`, assume-role chains, IMDS-SSRF →
  credential theft, cross-account trust, KMS/STS) → defer to [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/)
  (this model flags the *secret/key leaking*; what an attacker does with a cloud credential, and IAM
  posture, live there).
- **CI/CD pipeline secret handling & artifact/registry integrity** → defer to [../22-supply-chain-cicd/](../22-supply-chain-cicd/).
- **At-rest storage encryption & log retention mechanics** → defer to [../23-storage-and-logs/](../23-storage-and-logs/).
- **Client-bundle script/SRI/source-map specifics** → defer to [../05-client-side-supply-chain/](../05-client-side-supply-chain/)
  (this model picks up the *secret value* in the bundle; bundle integrity lives there).
- **PII (vs secret) inventory/redaction governance** → defer to [../24-data-privacy-pii/](../24-data-privacy-pii/).
- **OAuth/SSO/PAT issuance, token-format & session design** → defer to [../07-oauth-sso-integrations/](../07-oauth-sso-integrations/)
  + [../14-api-security/](../14-api-security/) (this model models a *leaked* token's exposure & abuse;
  issuance/format/rotation-of-sessions live there).

List each deferred item in a "Deferred to sibling models" subsection with the linked report, and
only model its **traffic-/event-observable** symptom (e.g. a leaked key abused in traffic, a secret
appearing in a captured body) where SecureNow adds value.

---

## 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 posture)
```

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. Note the **firewall state** (the network firewall
blocks an IP **abusing a leaked key** at the edge), any **system signature rules** present, and the
**redaction posture** from `securenow env` (whether the SDK is configured to strip secrets from
captured bodies/headers — `captureBody`/`sensitiveFields`).

> Reality check to record now: SecureNow sees **your server's traffic and the events you emit**. It
> does **not** scan your git history, your built bundle, your container images, or your CI logs. So a
> row is 🟢 only when a leaked key is **abused in traffic** SecureNow sees, or a secret value
> **flows through** a captured request/response/log; otherwise the primary fix is app/build/CI
> (scanning + vaulting + rotation) and the SecureNow row is 🟡 (event-fed) or 🔴.

---

## 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. **Note especially:** `securenow/events` `track()` was added
in **8.2**, `securenow/sessions` (revoke/`guard`/`isRevoked`) in **8.3**, and `redactSensitiveData`
ships in `securenow/nextjs-middleware` — if the installed version predates these, annotate the
affected snippets. Record the resolved version in the appendix of BOTH reports.

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
`redactSensitiveData` field list, and every mitigation subcommand are discoverable there.
Cross-check before emitting.

---

## Phase 1 — Inventory the secret surface (codebase + build analysis)

Secret hygiene starts with a **secret inventory**: you must *know* every credential the app holds,
where it lives, how it's stored, and how it's rotated. Document what **actually exists**, not what is
intended. **Never print a full value** — mask to a short prefix + `file:line`. Cover at minimum:

- **Secret-bearing files & config** — `.env`, `.env.*`, `*.env`, `config/*.{js,ts,json,yaml}`,
  `*.pem`/`*.key`/`*.p12`/`*.pfx`, `credentials.json`, service-account JSON, `docker-compose*.yml`,
  Kubernetes `Secret`/manifests, Helm values, Terraform `*.tfvars`/state, CI config
  (`.github/workflows/*`, `.gitlab-ci.yml`), and any `secrets.*` file. Which are **gitignored**? Which
  are **tracked**? (`git ls-files | grep -E '\.env|secret|credential|\.pem|\.key'` and
  `git check-ignore <file>`.)
- **Hardcoded secrets in source** — grep source for high-signal patterns (mask the matches): AWS
  (`AKIA[0-9A-Z]{16}`, `aws_secret_access_key`), Stripe (`sk_live_`, `sk_test_`, `rk_live_`,
  `whsec_`), Google (`AIza[0-9A-Za-z_\-]{35}`, service-account `"private_key": "-----BEGIN`),
  GitHub (`ghp_`, `gho_`, `ghs_`, `github_pat_`), Slack (`xox[baprs]-`), JWT signing secrets
  (`NEXTAUTH_SECRET`, `JWT_SECRET`, `SESSION_SECRET`), generic `api[_-]?key`, `secret`, `token`,
  `password`, `passwd`, `-----BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY-----`, DB/connection URIs
  (`mongodb(\+srv)?://`, `postgres://`, `mysql://`, `redis://` with credentials), Twilio (`SK`,
  `AC…:…`), SendGrid (`SG\.`), npm (`npm_`), and any vendor key shape. Record **file:line**, the
  **masked prefix**, and whether it's a real secret or a placeholder/example.
- **Git history** — secrets committed then deleted are still in history. Note whether the repo has
  ever had secret-bearing files tracked (`git log --all --full-history -- <pattern>`), and whether a
  history-scanning tool (gitleaks/trufflehog) has been run. Do **not** run a full destructive history
  rewrite — inventory only.
- **Frontend / client bundle / public env** — any `NEXT_PUBLIC_*`/`VITE_*`/`REACT_APP_*`/`PUBLIC_*`
  env var carrying a *secret* value (publishable `pk_` is fine; secret `sk_`/signing/DB is not), and
  any secret inlined into the built JS or a public runtime-config endpoint (`/config.js`, `/env.json`).
  Cross-reference [../05-client-side-supply-chain/](../05-client-side-supply-chain/) for the bundle mechanics.
- **Secrets in URLs / query strings / Referer** — tokens/keys passed as query params (`?api_key=`,
  `?token=`, password-reset/magic-link tokens in GET URLs) that land in server logs, proxy logs, the
  `Referer` header, and analytics.
- **Secrets in logs / telemetry / traces / error responses** — `console.log`/logger calls that print
  tokens/keys/Authorization headers; secrets embedded in error messages returned to clients; the
  SecureNow SDK's own capture (`captureBody`) — is redaction configured so the SDK does **not** ship
  secrets to ingest? (`securenow env --json` → `captureBody`/`sensitiveFields`; `redactSensitiveData`
  usage.) Cross-reference [../23-storage-and-logs/](../23-storage-and-logs/) and [../24-data-privacy-pii/](../24-data-privacy-pii/).
- **API-key & token scope + lifetime** — for each credential the app issues or consumes: is it
  **short-lived** (minted per-request / per-session, expiring) or a **long-lived static** key? Is it
  **narrowly scoped** (read-only, single-tenant, single-service) or broad? Are publishable vs secret
  keys conflated? (Issuance/format design defers to [../07-oauth-sso-integrations/](../07-oauth-sso-integrations/).)
- **Vaulting / secret manager** — is there a secret manager (AWS Secrets Manager / SSM Parameter
  Store / HashiCorp Vault / GCP Secret Manager / Doppler / 1Password / Infisical), or are secrets
  plaintext in env files / committed config? How are secrets injected at deploy (env injection vs
  on-disk file)? Is KMS used for at-rest encryption? (Cloud-KMS/IAM mechanics defer to ../20.)
- **Rotation posture** — is there any rotation cadence/automation? Do secrets carry an age/created-at?
  Are any **shared across environments** (dev key == prod key) or **across tenants** (one secret for
  all customers)? Is there a documented "key compromised → rotate" runbook?
- **Per-tenant / per-integration secrets** — webhook signing secrets, third-party integration keys —
  are they per-tenant/per-integration or one global secret whose leak compromises everyone?
- **Third-party leak paths** — secrets sent to analytics pixels, error trackers (Sentry/Datadog),
  **LLM prompts** (a key pasted into a prompt/context window), support tickets, Slack, or pasted into
  logs shipped to a third party.
- **Secret-scanning gates already present** — is there a pre-commit hook (gitleaks/trufflehog/
  detect-secrets/husky), a CI secret-scan step, GitHub push protection / secret scanning, or a build
  gate that fails on a secret in the output? If yes, that is a feed SecureNow can consume via a
  `track('secret.scan.finding')` shim. If no, that is the highest-leverage gap.
- **SecureNow instrumentation already present** — `securenow/register` / `securenow run` /
  `securenow init` (server traffic spans), any `securenow/events` `track()` calls, `redactSensitiveData`
  usage, whether the **firewall** is engaged. Determines what works *today* vs *after instrumentation*.

Output of this phase = the report's **Secret surface & inventory** section: the **secret inventory
table** (kind / location `file:line` / masked prefix / storage / gitignored? / scope / lifetime /
rotation), the **git-history exposure note**, the **public-env/bundle secret list**, the
**logs/telemetry/URL leak-path list**, the **vaulting & rotation posture**, the **per-tenant/per-env
separation note**, the **secret-scanning-gate status**, and a short paragraph naming the real
secret-leakage attack surface for this app (especially the highest-impact credential and whether it
is rotatable). **No full secret values anywhere.**

---

## 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. "git-history items: N/A, repo never tracked a secret-bearing file"; "client-bundle
items: N/A, no secret in any `*_PUBLIC_*` var or built output"). Never silently drop an item. Add
stack-specific threats you discover — this catalog is the floor, not the ceiling. Tag each modeled
row with its **OWASP 2021** code (A02/A05/A07, or "—") and a **CWE**.

**A. Hardcoded secrets in source & config — A05/A02; CWE-798**
1. Live API key / token hardcoded in application source
2. Private key / certificate / keystore committed to the repo
3. DB / connection URI with embedded credentials in source or config
4. Signing / webhook / session secret hardcoded (`JWT_SECRET`, `NEXTAUTH_SECRET`, `whsec_`)
5. Secret in IaC (Terraform `*.tfvars`/state, CloudFormation, Pulumi) committed
6. Secret in a container image layer / Dockerfile `ENV`/`ARG`
7. Placeholder/example that is actually a live value (an "example" `.env` with real keys)

**B. Git-history & VCS leakage — A05; CWE-312/200**
8. Secret committed then deleted — still recoverable from git history
9. Secret-bearing file tracked in git (not gitignored) — `.env`, `credentials.json`, `*.pem`
10. Secret in a commit message / PR description / branch name
11. Repo went public (or a fork/mirror) with secrets in history
12. No pre-commit / push-protection gate — new leaks ship undetected

**C. Frontend / client-bundle / public-env exposure — A02/A05; CWE-200**
13. Secret (`sk_`/signing/DB) in a `NEXT_PUBLIC_`/`VITE_`/`REACT_APP_`/`PUBLIC_` env var → shipped to browser
14. Secret inlined into the built client JS (world-readable)
15. Secret leaked via a public runtime-config endpoint (`/config.js`, `/env.json`) the SPA fetches
16. Secret recoverable from a served source map (defer bundle mechanics to ../05)

**D. Secrets in URLs / query strings / Referer — A02; CWE-598**
17. API key / token passed as a URL query parameter (`?api_key=`, `?token=`)
18. Password-reset / magic-link / session token in a GET URL → logged, cached, in `Referer`
19. Secret in a redirect target / callback URL leaked cross-origin via `Referer`

**E. Secrets in logs / telemetry / traces / errors — A09/A05; CWE-532**
20. `Authorization` header / token / key written to application logs
21. Secret embedded in an error message returned to the client (stack trace, exception)
22. Secret captured into traces/telemetry (SecureNow `captureBody` or another APM) without redaction
23. Secret shipped to a third-party error tracker / analytics / LLM prompt / support ticket
24. Secret in a crash dump / debug endpoint / `/debug`/`/__inspect` route

**F. API-key & token scope / lifetime — A07/A05; CWE-272/798**
25. Long-lived static API key where a short-lived / per-request credential belongs
26. Over-scoped key (read+write+admin where read-only suffices; all-tenant where single-tenant suffices)
27. Publishable vs secret key conflated (a "public" key that actually grants write/admin)
28. No expiry / no maximum lifetime on issued tokens
29. Same secret shared across environments (dev key == staging == prod)
30. Same secret shared across tenants (one webhook/signing secret for all customers)

**G. Rotation & lifecycle — A02/A05; CWE-320**
31. No rotation cadence — secrets never change
32. Rotation overdue (a key past its documented/SLA rotation age)
33. No rotation automation — manual, error-prone, skipped
34. No revocation path — a leaked key can't be killed quickly
35. Orphaned / unused credentials still valid (former employee key, deprecated integration)

**H. Storage / vaulting — A02; CWE-312/315**
36. No secret manager / vault — secrets plaintext in env files on disk
37. Secrets unencrypted at rest (defer at-rest mechanics to ../23)
38. Secrets in environment variables visible to all processes / in `/proc` / in a crash report
39. Secret manager present but the master/bootstrap credential is itself mishandled

**I. Telemetry redaction gaps (SecureNow's own pipeline) — A09; CWE-532**
40. SDK `captureBody` on with no/weak `sensitiveFields` → secrets shipped to ingest
41. Custom event `attributes` carrying a raw secret value (an attribute becomes a leak path)
42. A proxied report (CSP, webhook, error) forwarded to SecureNow with the secret un-redacted

**J. Leaked-key abuse (observable in traffic) — A07; CWE-287**
43. Exposed API key/token used from a **new ASN / geography** (post-leak takeover)
44. Exposed key used after its owner rotated/expired it (use of a should-be-dead credential)
45. Volume spike on an authenticated route keyed to one token (a scraped key being drained)
46. A known-leaked key value (from a scanner/IoC feed) seen in your traffic
47. Impossible-travel / concurrent use of one token from distant IPs

**K. Third-party & integration secrets — A05; CWE-798**
48. Webhook signing secret leaked → forged inbound webhooks (defer verification design to ../16)
49. A vendor/integration key with weaker storage than yours (its breach = your access)
50. Secret embedded in a mobile app / desktop binary / browser extension (extractable)

**L. Process / human factors — A05**
51. Secret pasted into a chat / ticket / wiki / screenshot
52. Secret in a `.env.example` or README "quickstart" that is a real value
53. Shared team credential (no per-engineer keys, no audit trail of who used what)

**M. Negative-space & evasion (what makes secret leaks hard to catch)**
54. Secret base64/hex-encoded or split across variables to evade a naive regex scanner
55. Secret in a binary asset / image EXIF / minified vendor blob (scanner-blind)
56. Time-bomb / conditional use of a hidden credential (only fires off-hours / in one region)
57. Secret leaked only at runtime (assembled from parts) — invisible to static scanning

**N. Observable abuse (what SecureNow telemetry can actually catch — the workhorse rows)**
58. A **secret-scanner finding** (`secret.scan.finding`) you emit from a pre-commit/CI/history gate → alert
59. A **runtime exposure** (`secret.exposure.detected`) — a secret-shaped value seen in a captured body/header/URL/log
60. **Leaked-key abuse** (`secret.key.abuse`) — anomalous use of an exposed token → block / revoke
61. A **rotation-overdue** signal (`secret.rotation.overdue`) you emit from a rotation-tracking job → notify
62. A known-leaked key value hitting your infra from a known-bad **ASN/IP** → firewall-blockable

**O. Deferred — modeled in sibling models (reference, do not re-derive)**
63. Cloud-IAM blast-radius, role/policy escalation, IMDS-SSRF → credential theft, KMS/STS → [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/) (**A05/API7**)
64. CI/CD pipeline secret handling, artifact/registry integrity → [../22-supply-chain-cicd/](../22-supply-chain-cicd/) (**A08/A06**)
65. At-rest storage encryption & log retention mechanics → [../23-storage-and-logs/](../23-storage-and-logs/) (**A02/A09**)
66. OAuth/SSO/PAT issuance, token format, session design → [../07-oauth-sso-integrations/](../07-oauth-sso-integrations/) + [../14-api-security/](../14-api-security/)
67. Client-bundle integrity / SRI / source-map mechanics → [../05-client-side-supply-chain/](../05-client-side-supply-chain/) (**A08/A05**)

> For 63–67, add **one** matrix row each marked *"deferred — see linked model"*, and only note the
> SecureNow-observable symptom here (e.g. a leaked cloud key abused in traffic, a secret in a captured
> body). The full detection/mitigation lives in the linked report.

> **Coverage expectation for this catalog.** Honestly, **A–M are mostly 🟡/🔴**: the real fix is
> secret scanning + vaulting + rotation + scoped/short-lived keys + `.gitignore` discipline, which
> are app/build/CI/cloud-config changes SecureNow does not make. SecureNow turns 🟡 where you **emit a
> scanner-finding or runtime-exposure event** (catalog N), and 🟢 only where a leaked key is **abused
> in traffic** SecureNow sees (block/revoke), a secret value **flows through** a captured body, or a
> known-bad ASN is **firewall-blockable**. Mark each row with the badge it truly earns.

---

## Phase 3 — Audit the code & build (findings only — do not fix, never print secrets in full)

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

- **Location** — `file:line` (clickable): the source file, `.env`, config, IaC, Dockerfile, CI
  workflow, or built artifact path. For history findings, cite the file pattern + that it appears in
  history (not the commit's full content).
- **Pattern** — the credential **kind** and a **masked prefix only** (e.g. "`sk_live_4eC…` Stripe
  secret key in `lib/stripe.js:12`"; "`AKIA…7QX` AWS access key in `.env:4` (tracked, not
  gitignored)"; "`NEXT_PUBLIC_API_SECRET` in `.env` → shipped to browser"). **Never paste the full
  value.** State the missing control precisely (committed/tracked, public-env, in-URL, in-log, no
  rotation, shared across env, etc.).
- **Why exploitable** — the concrete blast radius: which system the credential unlocks, who can read
  it (world-readable bundle / repo-readable / log-readable), and what an attacker achieves (data
  access, fund movement, account takeover, lateral movement → ../20).
- **Severity** — critical / high / medium / low (impact × reachability; a **live, world-readable,
  non-rotatable, broadly-scoped** production secret is **critical**; a gitignored dev-only placeholder
  is low).
- **Recommended fix (described, not applied)** — the specific change: e.g. "move the secret to a
  secret manager and inject at deploy; **rotate the exposed key immediately** (it must be considered
  compromised); add `.env*` to `.gitignore`; scrub git history (BFG/filter-repo) after rotation";
  "convert the long-lived key to a short-lived scoped credential"; "split the publishable and secret
  keys; the browser gets only the publishable one"; "redact the field in the logger / SDK
  `sensitiveFields`"; "stop putting the token in the URL — move it to an `Authorization` header /
  POST body". Reference the secure pattern, not a code diff. **You must not edit the codebase, build
  config, env files, or secrets, and must not print any secret in full.**

If a control exists and is correct (no secret in source or history; all secret files gitignored;
vault/secret-manager in use; short-lived scoped keys; rotation automation; SDK redaction configured;
a pre-commit/CI secret-scan gate), note it as a **strength** — the posture must be honest. Absence of
a control where secrets exist is itself a finding ("12 secret-bearing env vars, 0 in a secret
manager, no rotation").

Look specifically for, and record findings the same way (`file:line`, **masked** value, exploitability,
severity, described fix — **no edits, no full values**):

**Hardcoded-secret flaws** — live keys/tokens/private-keys/DB-URIs in source/config/IaC/Docker.
*Fixes must mention* moving to a secret manager + deploy-time injection, **rotating the exposed key**,
and a pre-commit/CI secret-scan gate.

**Git-history flaws** — tracked secret files; secrets in history; no push-protection. *Fixes must
mention* gitignoring + rotating + history scrub (BFG/git-filter-repo) **after** rotation, and enabling
push protection / a pre-commit hook.

**Public-env / bundle flaws** — secret in a `*_PUBLIC_*` var or built output or config endpoint.
*Fixes must mention* moving the secret server-side + proxying, using only publishable scoped keys in
the browser, rotating, and a CI secret-scan on the built output (bundle mechanics → ../05).

**In-URL / in-log / in-error flaws** — tokens in query strings; secrets in logs/errors/telemetry.
*Fixes must mention* moving tokens to headers/POST body, redaction in the logger and the SecureNow SDK
`sensitiveFields`, and never returning secrets in error responses (log/telemetry mechanics → ../23/../24).

**Scope / lifetime / rotation flaws** — long-lived/over-scoped/shared/non-rotated keys. *Fixes must
mention* short-lived scoped credentials, per-env & per-tenant separation, a rotation cadence +
automation, and a revocation path.

**Vaulting flaws** — no secret manager; plaintext env on disk. *Fixes must mention* adopting a secret
manager / vault, deploy-time injection, and KMS at-rest (cloud-KMS/IAM → ../20).

**Telemetry-redaction flaws** — SDK `captureBody` without `sensitiveFields`; raw secrets in event
attributes. *Fixes must mention* configuring SDK redaction (`redactSensitiveData` /
`config.capture.sensitiveFields`) and hashing/masking before any `track()` attribute.

---

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

Classify each threat with exactly one coverage badge:

- 🟢 **COVERED** — detectable + mitigable with SecureNow today on telemetry already flowing, OR
  blockable at the network firewall. For this domain that is the **minority**: a leaked key **abused
  in traffic** (block the IP / revoke the token), a secret value **flowing through** a captured body
  SecureNow already ingests, a known-bad ASN at the firewall.
- 🟡 **PARTIAL** — works after the customer adds instrumentation (a **secret-scanner shim**, a
  **runtime-exposure/redaction shim**, or a **rotation-tracking job** emitting `track('secret.*')`
  events), and SecureNow then alerts + contains at the edge — **but the real fix is the app/build/CI
  control** (scanning / vaulting / rotation / scoping). Most catalog A–M rows are 🟡 *with the
  app/build fix as primary*.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this today (a secret sitting in git history or a
  built bundle that never flows through traffic and that you don't instrument). **Still include it**:
  give the app/build/CI fix, then add *"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 brutally honest about the edge here.** SecureNow sees **server traffic** and **events you
> emit**; it contains actors via firewall / rate-limit / challenge / block / revoke. It **cannot**
> read your git history, your built bundle, your container layers, or your CI logs — those emit no
> server traffic and no event until you instrument a scanner that emits one. Such a row is 🔴 **until**
> a `secret.scan.finding` / `secret.exposure.detected` event exists, at which point it becomes 🟡
> (SecureNow alerts + can block/revoke abuse) with **scanning + vaulting + rotation as the primary
> fix**. Pair the edge control with the app/build/CI fix on **every** A–M row. Never badge a row 🟢
> just because the secret is high-impact.

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

### 4a. Instrumentation (what secret-leak detections feed on)

Server traffic auto-captures once the app runs under `securenow run` / `securenow/register` /
`securenow init`. The high-leverage feeds you add are a **secret-scanner shim**, a **runtime-exposure/
redaction shim**, and a **rotation-tracking job**, all shipped to SecureNow via `securenow/events`
`track()` (never throws). **Always hash/mask the secret before it becomes an attribute — the event
must not carry the secret.** Emit from a **server route / CI step / cron** so the event enters the
same pipeline as traffic:

```js
const { track } = require('securenow/events');
const crypto = require('crypto');
const fp = (v) => crypto.createHash('sha256').update(String(v)).digest('hex').slice(0, 12); // fingerprint, never the value

// (1) A pre-commit / CI / git-history secret scanner (gitleaks/trufflehog/detect-secrets) flagged a secret:
track('secret.scan.finding', { attributes: { kind: 'hardcoded|git_history|bundle|ci_log|container_image', detector: 'gitleaks', rule: 'aws-access-key', location: 'lib/aws.js:14', fingerprint: fp(found), severity: 'high' } });

// (2) A runtime redaction/scan shim saw a secret-shaped value in a captured request/response/header/URL/log:
track('secret.exposure.detected', { ip, attributes: { kind: 'api_key|jwt|aws_key|stripe_sk|private_key|db_uri|webhook_secret', location: 'req_body|resp_body|header|query|log', route: '/api/x', fingerprint: fp(found) } });

// (3) A leaked/exposed key or token is being used anomalously (your auth layer detected it):
track('secret.key.abuse', { ip, attributes: { token_kind: 'api_key|session|pat', reason: 'new_asn|post_rotation_use|impossible_travel|volume_spike', token_fp: fp(token) } });

// (4) A rotation-tracking job found a key past its rotation SLA:
track('secret.rotation.overdue', { attributes: { kind: 'stripe_sk|aws_key|jwt_secret|webhook_secret', age_days: 410, sla_days: 90, key_fp: fp(keyId) } });
```

> **Redact / fingerprint before you emit — the event must never carry the secret.** Use a one-way
> hash for `fingerprint`/`token_fp`/`key_fp` (so you can correlate without storing the value), and a
> masked prefix at most for human-readable fields. SecureNow already ships `redactSensitiveData` in
> `securenow/nextjs-middleware` (auto-redacts `password`, `token`, `api_key`, `access_token`, `auth`,
> `secret`, `card`, `cvv`, `ssn`, …) — run it on any captured body before it enters telemetry, and on
> any value before it becomes an attribute. Attributes feed detection; they must not become the leak.

Recommended secret-leak event taxonomy — rules match these **exact strings**:

| Event | Emit when |
|---|---|
| `secret.scan.finding` | a pre-commit/CI/history secret scanner flags a secret (hardcoded / git history / bundle / CI log / container) |
| `secret.exposure.detected` | a runtime redaction/scan shim sees a secret-shaped value in a captured body/header/URL/log |
| `secret.key.abuse` | a leaked/exposed key or token is used anomalously (new ASN, post-rotation, impossible-travel, volume spike) |
| `secret.rotation.overdue` | a rotation-tracking job finds a key/secret past its rotation SLA |

Custom `attributes` become queryable as `attributes_string['<key>']` (e.g. `attributes_string['kind']`,
`attributes_string['fingerprint']`, `attributes_string['token_fp']`). Ingest enriches every IP with
**ASN/org** (`client.asn`, `client.as_org`) — central to leaked-key-abuse detection.

### 4b. Detection rules — SQL conventions

Two query shapes. Both **must** keep the tenant scope and (where they aggregate by client) **must**
select an `ip` column. **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']`, `attributes_string['http.client_ip']`, and the
`client_ip` coalesce below. Confirm any other column with a `--mode dry_run` before relying on it.

> Note for this domain: the **workhorse rules are events-based** (scanner findings + runtime exposure
> + rotation on the **logs** table), because a secret sitting in code/history/bundle is invisible to
> traces. The traffic-based rules that add value detect **abuse of a leaked key** (auth'd traffic from
> a new ASN) and **a secret in a URL/query** that lands in your trace data.

**Events-based — secret-scanner finding (any hit is high-signal):**

```sql
-- rules/secret-scan-finding.sql
SELECT
  attributes_string['kind']     AS finding_kind,
  attributes_string['detector'] AS detector,
  attributes_string['rule']     AS detector_rule,
  attributes_string['location'] AS location,
  attributes_string['severity'] AS severity,
  count() AS findings
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'secret.scan.finding'
  AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY finding_kind, detector, detector_rule, location, severity
HAVING findings >= 1
```

**Events-based — runtime secret exposure in a captured body/header/URL/log (any hit, escalate):**

```sql
-- rules/secret-exposure-detected.sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['kind']           AS secret_kind,
  attributes_string['location']       AS where_seen,
  attributes_string['route']          AS route,
  count() AS hits
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'secret.exposure.detected'
  AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, secret_kind, where_seen, route
HAVING hits >= 1
```

**Events-based — leaked-key abuse (anomalous use of an exposed token → block/revoke):**

```sql
-- rules/secret-key-abuse.sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['token_kind']     AS token_kind,
  attributes_string['reason']         AS reason,
  attributes_string['token_fp']       AS token_fp,
  count() AS hits
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'secret.key.abuse'
  AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, token_kind, reason, token_fp
HAVING ip != '' AND hits >= 1
```

**Events-based — rotation overdue (notify the runbook, never auto-block):**

```sql
-- rules/secret-rotation-overdue.sql
SELECT
  attributes_string['kind']     AS secret_kind,
  attributes_string['age_days'] AS age_days,
  attributes_string['sla_days'] AS sla_days,
  attributes_string['key_fp']   AS key_fp,
  count() AS occurrences
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'secret.rotation.overdue'
  AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY secret_kind, age_days, sla_days, key_fp
HAVING occurrences >= 1
```

**Traffic-based — secret/token in a URL query string (lands in your trace data):**

```sql
-- rules/secret-in-url.sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], '')) AS client_ip
SELECT client_ip AS ip,
       attributes_string['http.target'] AS target,
       count() AS hits
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 '%api_key=%'
    OR attributes_string['http.target'] LIKE '%apikey=%'
    OR attributes_string['http.target'] LIKE '%access_token=%'
    OR attributes_string['http.target'] LIKE '%token=%'
    OR attributes_string['http.target'] LIKE '%secret=%')
GROUP BY ip, target
HAVING ip != '' AND hits >= 1
```

> The in-URL rule is **notify-first** — `token=` legitimately appears on some flows (verify, magic
> link). Tune the `LIKE` list and add `securenow fp` exclusions for known-benign params. It surfaces
> the *anti-pattern of secrets in URLs* so you can move them to headers/POST.

The other events follow the **same shape** — swap the `event.type` filter and threshold:
`secret.scan.finding` (≥1/24h → notify; ≥1 `severity=critical` → escalate), `secret.exposure.detected`
(≥1 → page response), `secret.key.abuse` (≥1 → block/revoke), `secret.rotation.overdue` (≥1 → notify,
route to the rotation runbook, never auto-block).

> **No injection signature rules apply here.** Unlike API injection (SQLi/XSS/RCE system signatures),
> a leaked secret has **no request-payload signature** — it is a value at rest in code/history/bundle,
> or a legitimately-formatted credential being misused. Do **not** claim signature/`instant.block`
> coverage for this domain; rely on scanner findings + exposure/abuse events + the firewall blocklist
> for known-bad ASNs.

Useful attributes/columns: `event.type`, `http.client_ip`, `kind`, `location`, `route`, `detector`,
`rule`, `severity`, `token_kind`, `reason`, `token_fp`, `fingerprint`, `age_days`, `client.asn`,
`client.as_org`, `http.target`.

**Emit every detection as a ready-to-copy command unit.** A detection is never a fragment: for each
rule emit, in this order and each as its own fenced block — (1) the SQL with a `-- rules/<name>.sql`
first line; (2) a line saving it to `rules/<name>.sql`; (3) the full `securenow alerts rules create …`
command; (4) the dry-run test. Flags must match `securenow alerts rules --help` from Phase 0.5
(`--name`/`--sql`/`--apps`/`--severity`/`--schedule`/`--nlp`). Save each rule's SQL to
`rules/<name>.sql` so `--sql @rules/<name>.sql` resolves. Note pre-existing/system rules from Phase 0
instead of duplicating them.

````markdown
**Rule: Secret — scanner finding (hardcoded / git-history / bundle)**  · 🟡 PARTIAL · A05 · CWE-798 · high

```sql
-- rules/secret-scan-finding.sql
SELECT
  attributes_string['kind']     AS finding_kind,
  attributes_string['detector'] AS detector,
  attributes_string['rule']     AS detector_rule,
  attributes_string['location'] AS location,
  attributes_string['severity'] AS severity,
  count() AS findings
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'secret.scan.finding'
  AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY finding_kind, detector, detector_rule, location, severity
HAVING findings >= 1
```

```bash
# save the SQL, then create the rule (test-first — route to the rotation/scrub runbook):
cat > rules/secret-scan-finding.sql <<'SQL'
-- (paste the SQL block above)
SQL

securenow alerts rules create \
  --name "Secret: scanner finding (hardcoded / git-history / bundle)" \
  --sql @rules/secret-scan-finding.sql \
  --apps <APP_KEY> \
  --severity high \
  --schedule "*/15 * * * *" \
  --mode test \
  --nlp "any secret-scanner finding in the last 24 hours"
```

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

Reuse this exact unit shape for each other events-based rule (swap the `event.type` filter, threshold,
`--name`, `--nlp`, `rules/<name>.sql`) and for the traffic-based rules. Coverage badge on the unit
header is the one the row truly earns (mostly 🟡 here; 🟢 for leaked-key abuse you block/revoke).

#### 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; tune thresholds; add securenow fp exclusions for benign in-URL tokens / known scanners…
securenow alerts rules update <RULE_ID> --mode prod      # promote: arm 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** — the in-URL rule (`token=` on legit
flows), the scanner rule (a known placeholder), rotation counts — must ship in **`--mode test`
first**, observed for **3–7 days**, tuned + `securenow fp`-excluded, then promoted. Only
**high-precision** rules — `secret.key.abuse` keyed on a confirmed-leaked `token_fp`, a known-bad ASN
hit — may go straight to `prod`. **Tag each rule `test-first` or `prod-ready`** and say why.

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

For secret-leakage, the **app/build/CI/cloud-config fix is the primary control on almost every row**
(scan → vault → rotate → scope → gate); SecureNow **contains at the edge** (block an IP abusing a
leaked key, revoke the stolen token/session, drop a known-bad ASN at the firewall, alert on a scanner
finding). Always pair them. Re-check every flag against the installed SDK (`securenow <cmd> --help`);
annotate `# requires securenow >= <ver>` if absent. Scope by **app / env / route / method / IP /
duration**.

| # | 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 a known-bad ASN abusing a leaked key before the app. No app change. |
| 2 | **Exploit-signature instant block** | *(N/A here)* — system SQLi/XSS/RCE signatures + `--execution-mode instant` | **N/A for secret leaks** — a leaked credential carries no request-payload signature; do not claim this coverage. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "leaked-key abuse"` | confirmed source abusing a leaked key, all routes. |
| 4 | **IP block — scoped to route (+ method)** | `securenow blocklist add <ip> --route /api/v1 --mode prefix --method ALL --app <APP_KEY> --env production --reason "..."` (`--mode exact\|prefix\|regex`) | block an IP only on the API routes the leaked key reaches; least collateral. |
| 5 | **IP block — temporary** | `securenow blocklist add <ip> --duration 24h --reason "..."` (`30m`,`24h`,`7d`) · reverse `securenow blocklist unblock <id> --reason "..."` | auto-expiring containment while you rotate the key. |
| 6 | **Rate limit — per IP** | `securenow ratelimit add <ip> --limit 100 --window 1m --duration 24h --reason "..."` | throttle a leaked key being drained from one client. |
| 7 | **Rate limit — per route (per-IP budget)** | `securenow ratelimit add --route /api/v1 --mode prefix --method GET --limit 60 --window 1m --key-by ip` | cap a key-protected API for everyone, budgeted per IP, while a leak is contained. |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /api/v1 --mode exact --method GET --limit 20 --window 1m --duration 24h` · test `securenow ratelimit test <ip> --path /api/v1 --method GET` | precise throttle of one abusing client on one route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /api/v1 --difficulty 16 --clearance 30m` · test `securenow challenge test <ip> --path /api/v1 --method GET` | a bot draining a leaked key from **shared / NAT / CGNAT** egress — 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 of sources abusing a leaked key. |
| 11 | **Session / token revocation (THE leaked-credential control)** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | **kill the leaked session/token itself**, not just the IP — the right response to leaked-key abuse / a stolen session. |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "CI scanner / monitoring / office"` | stop false positives from known-good infra emitting scanner findings or hitting key-protected routes. 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/API surface a leaked key shouldn't reach from the internet. 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` | quiet a noisy in-URL/scanner/rotation rule after a known-benign case without weakening it. |
| 15 | **App / config / build / CI fix (PRIMARY for this whole domain)** | *described in the Code-Findings report, never auto-applied* | the actual fix: **rotate the exposed key**, **move secrets to a vault + deploy-time injection**, **add a pre-commit/CI secret-scan gate**, **scrub git history (BFG/filter-repo) after rotation**, **shorten lifetime + tighten scope**, **per-env/per-tenant separation**, **configure SDK redaction**, **move tokens out of URLs**. SecureNow contains; the fix removes. |

**Choosing per threat** — by **confidence**: confirmed leaked token abused → **#11 revoke + #3 block**;
known-bad ASN → **#1 firewall**; bot draining a key on shared egress → **#9 challenge**; scanner/in-URL
noise → **#7/#8 rate-limit + notify-only (test-mode first)**; known-good scanner/monitor → **#12
trusted / #14 fp**. By **blast radius**: scope to the narrowest `route`/`method`/`IP`/`duration`; on
NAT/CGNAT prefer challenge/rate-limit over a hard block. **Crucially:** SecureNow's edge stops a leaked
key's *abuse* — it does **not** remove the secret from your code/history/bundle, so on every A–M row
write the **app/build/CI/cloud fix as primary** (rotate + vault + scan + scope) and the SecureNow
control as containment of abuse + early warning. The single most important response to any confirmed
exposure is **rotate the key** — it must be considered compromised the moment it's found.

### 4d. Testing every detection and mitigation

Only test against apps/environments you own; prefer `--env local`/staging. Use TEST-NET source IPs
(`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`). **Use fake/fingerprinted values only — never a
real secret.**

```bash
# Synthetic scanner finding — exercise the scanner rule:
securenow event send secret.scan.finding \
  --attrs kind=hardcoded,detector=gitleaks,rule=aws-access-key,location=lib/aws.js:14,severity=high,fingerprint=deadbeef1234,test=true

# Synthetic runtime exposure (any hit fires):
securenow event send secret.exposure.detected --ip 203.0.113.50 \
  --attrs kind=stripe_sk,location=resp_body,route=/api/orders,fingerprint=cafef00d5678,test=true

# Synthetic leaked-key abuse (block/revoke path):
securenow event send secret.key.abuse --ip 203.0.113.51 \
  --attrs token_kind=api_key,reason=new_asn,token_fp=abc123def456,test=true

# Synthetic rotation-overdue (notify-only runbook rule):
securenow event send secret.rotation.overdue \
  --attrs kind=jwt_secret,age_days=410,sla_days=90,key_fp=99aa88bb77cc,test=true

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

# Telemetry pipeline / in-URL inspection:
securenow test-span "threat-model.secret-hygiene.smoke"
securenow forensics "requests with api_key or token in the URL in the last hour" --env production

# Edge mitigation verification (block / revoke / firewall):
securenow blocklist add 203.0.113.51 --duration 30m --reason "threat-model test"
securenow firewall test-ip 203.0.113.51 --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"
```

Every 🟢/🟡 threat row in the report must have a concrete test recipe (commands + expected outcome:
which rule fires, which notification appears, what the edge mitigation does). For 🔴 rows, state
explicitly that there is no SecureNow test — the validation is the **app/build/CI fix** (a clean
secret-scan run, the key rotated, the vault in use, the gate enabled).

---

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

Write **two tracks**, each as Markdown + a self-contained HTML page, into
`threat/26-secrets-and-api-key-hygiene/`:

- **Track A — Detection & Mitigation** → `secrets-and-api-key-hygiene-detection-mitigation.md` + `.html`.
- **Track B — Code Findings & Recommendations** → `secrets-and-api-key-hygiene-code-findings.md` + `.html`.

The two tracks **cross-link**: each gap/instrumentation row in Track A links to the relevant Track B
finding, and each Track B fix links back to the Track A row it backs. Record the **resolved installed
`securenow` version** (Phase 0.5) in the appendix of both. **No full secret values in either track.**

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

1. **Executive summary** — stats line (threats modeled · covered · partial · gaps · rules to create),
   the top 3 **detectable** secret risks for this app (name the highest-impact credential and whether
   it's rotatable, **masked**), the installed `securenow` version + app key + firewall state, and a
   one-paragraph posture verdict honest about **app/build-fix-primacy** (scanning + vaulting + rotation
   are the real fixes; SecureNow adds scanner-finding/exposure alerting + leaked-key-abuse containment)
   with a one-line OWASP A02/A05/A07 + CWE coverage note.
2. **SDK & environment** — installed SDK version, app key(s), environment, firewall state, redaction
   posture, and the existing rules / automations / challenge rules from Phase 0 (note **no system
   signature rules apply** to this domain).
3. **Threat → Detection → Mitigation matrix** — one row per modeled threat:
   `# | Threat | OWASP/CWE | Coverage 🟢/🟡/🔴 | Detection rule | Signal (threshold+window) | Schedule | Sev | Mitigation (app/CI fix + edge) | Tag`.
   The **Mitigation cell must pick specific, scoped mitigation(s)** from the §4c toolbox (by
   number/name) — never a generic "block the IP"; always pair **rotate/vault/scan (PRIMARY)** with the
   edge control. The **Tag column marks each rule `test-first` or `prod-ready`**. Then the **"Out of
   scope" N/A list** and the **"Deferred to sibling models"** rows (63–67). Each gap/instrumentation
   row links to its **Track B** finding.
4. **Detection rules to create** — each as the **ready-to-copy command unit** from Phase 4 (SQL → save
   → create → dry-run). **Mark each `test-first` or `prod-ready`**; for every `test-first` rule include
   the `--mode test` → observe (3–7 days) → `--mode prod` step inline. State explicitly that **no
   signature/instant-block rules apply**.
5. **Instrumentation the detections need** — only the `track('secret.*')` events the rules consume (the
   **scanner shim**, **runtime-exposure shim**, **rotation job**), each as a copyable snippet; **hash/
   mask before emit**; point to **Track B** for *where* in the code/CI to add them.
6. **Mitigation mechanisms** — render the **full §4c toolbox table verbatim** (all 15 rows, with
   instant-block marked N/A) + the "Choosing per threat" paragraph + per-threat ready-to-copy mitigation
   command + reversibility. Make explicit that the **app/build/CI fix is primary** (rotate + vault +
   scan + scope) and SecureNow is containment of abuse + early warning, and that the single most
   important response to any exposure is **rotate the key**.
7. **Action plan (copy-paste, ordered)** — ① engage the firewall, ② **add a pre-commit + CI secret-scan
   gate and proxy `secret.scan.finding` into SecureNow** (the single biggest win), ③ add the
   runtime-exposure + rotation-tracking events, ④ create the event rules — **create FP-prone
   (`test-first`) rules in `--mode test`** with an explicit **"promote after N days" step**; only
   `prod-ready` rules (confirmed-leaked token-fp, known-bad ASN) armed, ⑤ enable block/revoke for
   leaked-key abuse, ⑥ test, ⑦ verify, ⑧ schedule the **rotate-vault-scan backlog from Track B** (the
   actual remediation). Real commands only, `<APP_KEY>` substituted.
8. **Testing & validation** — per-rule recipe (4d): `securenow event send …` / `test-span` / dry-run +
   expected outcome + cleanup (TEST-NET IPs; fake/fingerprinted values only). For 🔴 rows, the
   build/CI verification that replaces a SecureNow test.
9. **Response runbooks** — per notification type (scanner finding, runtime exposure, leaked-key abuse,
   rotation overdue): what fired, how to confirm a true positive, the exact respond command (copy) —
   **with "rotate the key" as step one on any confirmed exposure** — the Track B fix to ship, and the
   exact reverse command (copy).
10. **Known gaps & SecureNow feature requests** — every 🔴 (secret at rest in history/bundle never
    flowing through traffic, encoded/runtime-assembled secrets): why it's not coverable, the interim
    app/CI fix (link to Track B), and the "contact the SecureNow team" line.
11. **Appendix** — resolved SDK/CLI version, app key, environment, firewall state, rule IDs created,
    the secret-inventory snapshot date (re-inventory cadence), date, link to the **Track B** report.

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

State at the top: *"Findings only — no application code, config, env, or secrets were modified, and
no secret value is printed in full (masked prefixes only)."*

1. **Executive summary** — findings by severity, the top 3 code risks (masked), and a one-paragraph
   posture verdict.
2. **Surface & inventory** — the Phase 1 secret inventory: secret-inventory table (kind / location
   `file:line` / **masked prefix** / storage / gitignored? / scope / lifetime / rotation) + git-history
   note + public-env/bundle list + logs/URL leak-path list + vaulting & rotation posture +
   per-tenant/per-env note + secret-scanning-gate status. This is the secret-authorization deliverable.
3. **Threat catalog** — the exhaustive Phase 2 catalog (A–O, grouped, each tagged OWASP/CWE, modeled or
   explicit N/A — drop nothing).
4. **Code-level findings (audit)** — the Phase 3 findings: a table
   `# | Location (file:line) | Kind (masked) | Threat | OWASP/CWE | Sev | Issue | Recommended fix`, each
   with the **masked** value and the described fix (**never applied, never printed in full**).
5. **Strengths** — controls present and correct (no secret in source/history; all secret files
   gitignored; vault in use; short-lived scoped keys; rotation automation; SDK redaction; a scan gate).
6. **App / build / CI fixes (primary remediation)** — the changes that remove the root cause (described,
   not applied): **rotate the exposed key**, move to a vault + deploy-time injection, add a pre-commit +
   CI secret-scan gate, scrub git history after rotation, shorten lifetime + tighten scope, per-env/
   per-tenant separation, configure SDK redaction, move tokens out of URLs. Each linked to the **Track
   A** row it backs.
7. **Instrumentation recommendations** — the `track('secret.*')` calls to add (scanner shim in
   pre-commit/CI, runtime-exposure/redaction shim, rotation-tracking cron) and the exact file/step to
   add them, so the **Track A** rules light up. Flag the **CI secret-scan gate** as the single
   highest-leverage instrumentation.
8. **Appendix** — files reviewed, resolved SDK version, date, link to the **Track A** report.

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

Write **both** HTML files (Track A and Track B) from the **shared skeleton in
[../_shared/report-output-contract.md](../_shared/report-output-contract.md) §4**: it carries the
SecureNow brand tokens, the copy-button CSS, and the copy `<script>`. Change only the `<title>`, the
sidebar subtitle, the header title, and the section content per track — **do not restyle**. Wrap
**every** command/SQL/code block as a `.cmd` (so it gets a Copy button); in Track A every command must
have a working Copy button. No external fonts/scripts/network. Badge usage: severity →
`<span class="b crit|high|med|low">`; coverage → `<span class="c cov|part|gap">`; OWASP →
`<span class="owasp">A02</span>`; CWE → `<span class="cwe">CWE-798</span>`; mitigation type →
`<span class="m firewall|rate|challenge|block|notify|appfix">` (use `appfix` heavily — it is primary
in this domain; there is **no** `signature` use here); rule IDs → `<span class="rid">`. Stats numbers
must equal the matrix/findings row counts.

---

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

- **No secret value is ever printed in full** in any of the four files, any event attribute, any log,
  or any commit — masked prefixes + `file:line` only; fingerprints are one-way hashes.
- **Phase 0.5 ran:** the resolved installed `securenow` version appears in both reports' appendix, and
  no command/flag/event/SQL column is emitted that the installed SDK/CLI does not expose (else it is
  annotated `# requires securenow >= <version>`).
- Every detection rule is a **complete copyable unit** (SQL → `rules/<name>.sql` → full `securenow
  alerts rules create …` → dry-run test); flags match `securenow alerts rules --help`.
- **Four** files are written to `threat/26-secrets-and-api-key-hygiene/` (detection-mitigation .md+.html,
  code-findings .md+.html); the two tracks **cross-link**; both HTML files are self-contained (inline
  CSS/JS, no network) and **every command block in the Track A HTML has a working Copy button**.
- The split is **honest**: SecureNow-runnable detections/mitigations live in the Detection report;
  code/build/CI/config changes live in the Code-Findings report; nothing security-relevant is dropped.
- Every catalog item A1–O67 is either a matrix row or an explicit N/A line; each modeled row carries its
  OWASP 2021 tag (A02/A05/A07 or "—") **and** a CWE.
- The defining emphasis items are each modeled or explicit N/A: hardcoded secrets, git-history leakage,
  public-env/bundle exposure, secrets-in-URLs, secrets-in-logs/telemetry, key scope/lifetime, rotation,
  vaulting, telemetry-redaction gaps, leaked-key abuse, per-tenant/per-env separation, and the
  secret-scanning gate.
- Cloud-IAM blast-radius, CI/CD pipeline, at-rest storage/logs, OAuth/PAT issuance, and client-bundle
  mechanics are **deferred** to the **numbered** sibling models (../20, ../22, ../23, ../07+../14, ../05)
  — rows present, linked, not re-derived.
- Coverage badges are **honest**: this is an app/build-fix-primary domain; most A–M rows are 🟡 with the
  app/CI fix as **primary**, 🟢 only where a leaked key is **abused in traffic** / a secret **flows
  through** a captured body / a known-bad ASN is firewall-blockable, and every 🔴 (secret at rest in
  history/bundle, encoded/runtime-assembled) is kept with an interim app/CI fix + the "contact the
  SecureNow team" line.
- Every matrix row pairs the **edge containment WITH the app/build/CI fix** (rotate + vault + scan +
  scope) and has a concrete signal (threshold + window), severity, and mitigation — no "monitor for
  suspicious activity" filler. The single most important response to any confirmed exposure is
  **rotate the key** — say so on every exposure row.
- Every code finding in Track B has a `file:line`, the **masked** value, and a described fix — and **no
  application code, config, env, or secrets were modified** (audit).
- Every detection SQL keeps `__USER_APP_KEYS__` scoping (correct table column: `service.name` for logs,
  `` `resource_string_service$$name` `` for traces), the `client_ip` coalesce where it aggregates by IP,
  and `HAVING ip != ''`; traffic queries keep the `ts_bucket_start` + `kind = 2` guards.
- The report does **not** claim signature/`instant.block` coverage for this domain and relies on
  scanner findings + exposure/abuse events + the firewall blocklist instead.
- Only commands, flags, events, and SQL columns from this prompt's building blocks appear (`securenow
  firewall`, `blocklist`, `ratelimit`, `challenge`, `automation`, `revoke`, `fp`, `trusted`, and the
  `secret.*` events) — never invented.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted, and lists the **CI secret-scan gate**
  as the single highest-leverage instrumentation plus the rotate-vault-scan backlog as the real remediation.
- **Every false-positive-prone rule is tagged `test-first`** with the `--mode test` → observe (3–7 days)
  → `--mode prod` workflow; only high-precision rules (confirmed-leaked token-fp, known-bad ASN) are
  `prod-ready`. The action plan creates the test-first rules in `--mode test` with an explicit "promote
  after N days" step.
- All **four** files are written and a one-line summary (per-track paths, threat counts, rules-to-create
  count, code findings by severity, gaps, resolved SDK version, OWASP A02/A05/A07 + CWE coverage) is
  printed back to the user.

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