# Client-Side Supply Chain 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 web project** that has the
`securenow` CLI installed and logged in. The agent will inventory the **browser-side
third-party surface** (every `<script>`, tag manager, pixel, CDN library, and client bundle),
build an exhaustive **client-side supply chain** threat model mapped to **OWASP A08:2021
Software & Data Integrity Failures** (with **A06:2021 Vulnerable & Outdated Components** and
**A05:2021 Security Misconfiguration**) and **PCI DSS 6.4.3 / 11.6.1** (payment-page script
controls), audit the code/build for integrity flaws, 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
which threats still need the SecureNow team.

This domain covers the code that runs in the **user's browser but isn't fully under your
control**: compromised third-party scripts (Magecart/formjacking), tag managers (GTM),
analytics/advertising pixels, CDN-hosted libraries, and the integrity of your own client
bundles. The defining property of this surface is that **most of the controls are app- and
build-time fixes** — Content-Security-Policy (CSP), Subresource Integrity (SRI), script
pinning/inventory, and source-map suppression — none of which SecureNow applies for you.

> **Honesty up front: this is a LOW-native-coverage domain.** SecureNow is an **API / traffic**
> security layer. A skimmer running *inside the browser* exfiltrates a victim's card number to
> an attacker host **directly from the page** — that beacon usually never touches your server,
> so SecureNow's traffic pipeline cannot see it. What SecureNow *can* do: (1) detect
> **anomalous outbound destinations** where the exfil round-trips through your own
> backend/proxy/CSP-report endpoint and therefore *is* observable in your telemetry; (2)
> **block known-bad exfil/CDN hosts at the free network firewall** so a compromised first-party
> server can't reach them; (3) ingest **CSP violation reports** and **client integrity events**
> you emit and turn them into alerts and edge containment. The **primary fix on almost every
> row is CSP + SRI + script pinning + source-map suppression** — pair the edge control with the
> app/build fix on every row, and mark a row 🔴 honestly when neither helps.

This model **does not re-derive its siblings.** It references and defers to:
[../04-xss-csrf-cors/](../04-xss-csrf-cors/) for CSP authoring, response headers, and
first-party XSS/CSRF/CORS; [../22-supply-chain-cicd/](../22-supply-chain-cicd/) for
server/build-time dependency integrity, lockfile/registry trust, and CI pipeline compromise;
[../09-payment-business-logic/](../09-payment-business-logic/) for checkout/skimming **impact**
and payment-flow abuse; [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/) for the
secret-rotation/IAM handling of any key found leaked into a bundle.

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 Client-Side Supply Chain Threat Model Report (SecureNow)

You are a senior application-security engineer specializing in browser-side supply chain
security (Magecart, tag managers, pixels, CDN scripts, client bundles). Produce an
**exhaustive client-side supply chain threat model for THIS codebase**, organized along
**OWASP A08:2021 Software & Data Integrity Failures** (primary), with **A06:2021 Vulnerable &
Outdated Components** and **A05:2021 Security Misconfiguration**, and mapped to **PCI DSS 6.4.3**
(manage and authorize all scripts on payment pages) and **PCI DSS 11.6.1** (detect tampering /
unauthorized change to payment-page scripts and HTTP headers). Map every threat to **SecureNow**
detections and mitigations, with a ready-to-run action plan **and** a code-level audit of every
client-side integrity flaw 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/05-client-side-supply-chain/`
(create the folder if needed):

1. `client-side-supply-chain-detection-mitigation.md` — the **operational runbook**: what to run
   in SecureNow (detection rules, mitigation commands, instrumentation, action plan, tests).
2. `client-side-supply-chain-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. `client-side-supply-chain-code-findings.md` — the **code audit**: the integrity flaws found in
   this codebase/build and the recommended fixes (described, **never** applied).
4. `client-side-supply-chain-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, CSP, or HTML.** You are auditing: every fix is *described in the
report*, never applied to the repo.

**Scope discipline.** This model owns the **browser-side third-party surface** and the
**integrity of your client bundle as shipped**: third-party `<script>`, tag managers,
analytics/advertising pixels, CDN-hosted libraries, SRI/pinning, client-bundle secrets,
source-map exposure, front-end npm package compromise reaching the bundle, and CSP *as it
governs script origins*. It does **not** re-derive:

- **CSP authoring, security-response headers, first-party XSS/CSRF/CORS** → defer to
  [../04-xss-csrf-cors/](../04-xss-csrf-cors/) (that model owns header authoring; this model
  only checks whether CSP/SRI *exist and are strong enough to constrain third-party script* and
  whether a CSP **report endpoint** feeds SecureNow).
- **Server-side / build-time dependency & registry trust, CI/CD pipeline compromise** → defer to
  [../22-supply-chain-cicd/](../22-supply-chain-cicd/) (this model picks up only at the point a
  compromised dependency **reaches the browser bundle**).
- **Checkout / skimming business impact, payment-flow abuse** → defer to
  [../09-payment-business-logic/](../09-payment-business-logic/) (this model models the
  *skimming mechanism*; the fraud/chargeback impact and order-flow controls live there).
- **Rotation / IAM blast-radius of a leaked key** → defer to
  [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/) (this model flags the *leak into
  the bundle*; rotation and least-privilege 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 CSP report spike, an exfil beacon
that round-trips your proxy) 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)
```

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 is the one SecureNow control that can block a compromised *first-party server* from
reaching a known-bad exfil/CDN host) and any **system signature rules** already present.

> Reality check to record now: SecureNow sees **your server's traffic and the events you emit**.
> It does **not** sit in the victim's browser. So a row is 🟢 only when the malicious behavior
> round-trips your backend/proxy/CSP-report endpoint or hits the network firewall; otherwise the
> primary fix is app/build (CSP/SRI/pinning) 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. 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, and every
mitigation subcommand are discoverable there. Cross-check before emitting.

---

## Phase 1 — Inventory the browser-side surface (codebase + build analysis)

Client-side supply chain starts with a **script inventory** (PCI DSS 6.4.3: you must *know and
authorize* every script on sensitive pages). Document what **actually ships to the browser**, not
what is intended. Cover at minimum:

- **Framework & build** — React/Next/Vue/Nuxt/Angular/Svelte/SvelteKit/Astro/Remix, bundler
  (webpack/Vite/Rollup/esbuild/Turbopack/Parcel), SSR vs SSG vs SPA, hydration model. Where does
  the built JS live and which pages load which bundles?
- **First-party bundle inventory** — every emitted JS/CSS chunk, the entry HTML, and which
  third-party code is *bundled in* (compiled into your chunk) vs *loaded externally* (a separate
  `<script src>`). Bundled-in third-party code cannot carry SRI and is governed only by your
  dependency/build trust → cross-reference [../22-supply-chain-cicd/](../22-supply-chain-cicd/).
- **External `<script>` catalog** — enumerate **every** `<script src>` across HTML templates,
  framework `<Head>`/`next/script`/`<svelte:head>`, layout components, and any
  runtime-injected script (createElement('script'), `document.write`, GTM/loader snippets). For
  each: the **origin/host**, whether the URL is **version-pinned** vs mutable (`@latest`, no
  version, a rolling CDN path), whether it has an **`integrity=` (SRI) hash** + `crossorigin`,
  `async`/`defer`, and whether it loads on a **sensitive page** (checkout, login, account,
  payment, anything handling card/PII). This catalog is a report deliverable and is your PCI
  6.4.3 script authorization list.
- **Tag manager** — Google Tag Manager / Tealium / Adobe Launch / Segment present? Identify the
  container ID, who can publish to it (marketing account → arbitrary script injection blast
  radius), whether GTM is allowed to inject scripts on payment pages, and whether **Custom HTML
  tags / custom JS variables** are enabled (the GTM feature that lets a compromised marketing
  account run arbitrary JS on every page).
- **Analytics / advertising pixels** — GA4/gtag, Meta/Facebook Pixel, TikTok, LinkedIn, Twitter/X,
  Hotjar/FullStory/LogRocket/Clarity (session replay), Google Ads, programmatic ad tags. For each:
  what does it have access to (full DOM? form fields? `dataLayer`?), does it auto-capture inputs
  or run on pages with card/PII fields, and could it exfiltrate tokens/PII (intentionally or via
  compromise)?
- **CDN-hosted libraries** — jQuery, Bootstrap, font/icon loaders, map SDKs, chat widgets,
  payment widgets, A/B-test tools, consent-management platforms — loaded from jsDelivr/unpkg/
  cdnjs/Cloudflare/a vendor CDN. Pinned? SRI? Is the CDN **path account-takeoverable** (a
  username-scoped unpkg/jsDelivr path, an abandoned package, a wildcard `@latest`)?
- **Subresource Integrity (SRI) posture** — for every external script/style, is there an
  `integrity` hash + `crossorigin="anonymous"`? Is SRI applied to **mutable** URLs (where it's
  defeated, because the host can serve a new version that just fails to load) vs **pinned**
  URLs (where it actually protects)? Note where SRI is missing on a sensitive page.
- **Content-Security-Policy (as it constrains third-party script)** — is there a CSP at all
  (header or `<meta>`)? Does `script-src` use `unsafe-inline` / `unsafe-eval` / `*` / broad
  scheme sources (`https:`)? Are third-party hosts explicitly allowlisted or wide open? Is there
  a **`report-uri`/`report-to`** endpoint (the hook SecureNow can ingest)? Is `connect-src`
  constrained (this is what limits where a skimmer can beacon to)? **Do not author the CSP** —
  defer header design to [../04-xss-csrf-cors/](../04-xss-csrf-cors/); here you only assess
  whether it constrains third-party script and whether it reports.
- **Secrets / keys in client code** — grep the **built output and public config** (not just
  source) for API keys, tokens, private keys, signing secrets, service-account JSON, DB URLs,
  Stripe **secret** keys (`sk_live`/`sk_test` — publishable `pk_` is fine), AWS keys
  (`AKIA…`), Google API keys, Firebase service-account blobs, JWT signing secrets, webhook
  secrets, and `NEXT_PUBLIC_`/`VITE_`/`REACT_APP_`/`PUBLIC_` env vars that shouldn't be public.
  Anything in the bundle is **world-readable**.
- **Source-map exposure** — are `.map` files deployed to production and **publicly fetchable**
  (`/_next/static/**/*.js.map`, `/assets/*.js.map`, `sourceMappingURL` comments pointing to a
  reachable path)? Source maps leak original source, comments, internal endpoints, and sometimes
  secrets. Note hidden-source-map / upload-to-error-tracker-only configs as a strength.
- **Front-end dependency integrity (reaching the bundle)** — read `package.json` /
  lockfile for front-end deps with **postinstall/preinstall/install scripts**, **typosquat-prone
  or recently-published** packages, packages pulling **runtime code from a CDN at build time**,
  and any **dependency-confusion** risk (a public package name that could shadow an internal one
  and land in the client bundle). Inventory only — **do not run installs/updates**; deep registry
  trust is [../22-supply-chain-cicd/](../22-supply-chain-cicd/).
- **Transitive third-party trust** — for each third-party script, what does *it* load
  (chains: a tag manager loads a pixel that loads a vendor SDK that loads…)? Each hop is a
  domain you transitively trust with full page access. List the known first hop and note that
  deeper hops are unknowable without runtime observation.
- **Browser-extension / injected-content exposure** — sensitive pages (login, payment, account)
  that don't defend against extension/userscript DOM injection: no integrity check on critical
  form handlers, secrets rendered into the DOM, no SubresourceIntegrity on the page's own
  scripts. (Largely an app-design concern; note observable symptoms only.)
- **CSP report / client-error pipeline already present** — is there a `report-uri`/`report-to`
  endpoint, a Reporting-API collector, an error tracker (Sentry/Datadog RUM) that already
  captures CSP violations or unexpected outbound requests? If yes, that is a feed SecureNow can
  consume via a `track()` shim. If no, that is the highest-leverage instrumentation gap.
- **Telemetry privacy & redaction** — if you proxy CSP reports or client errors into SecureNow,
  confirm the pipeline redacts any captured form values, tokens, query strings, and PII before
  ingestion. Attributes must not become a new leak path. If not handled, raise a high-severity
  finding.
- **SecureNow instrumentation already present** — `securenow/register` / `securenow run` /
  `securenow init` (gives server traffic spans automatically), any `securenow/events` `track()`
  calls, whether the network **firewall** is engaged. This determines what works *today* vs
  *after instrumentation*.

Output of this phase = the report's **Browser-side surface & script inventory** section: the
**external `<script>` catalog** (host / pinned? / SRI? / sensitive-page?), the tag-manager
posture, the pixel/analytics list (with DOM/form access), the CDN-library list, the **SRI &
pinning posture table**, the **CSP-as-script-constraint posture**, the **bundle-secret scan
results**, the **source-map exposure status**, the **front-end dependency-integrity notes**, the
**transitive-trust map**, the **CSP-report/error pipeline status**, and a short paragraph naming
the real client-side supply chain attack surface for this app (especially which third parties run
on payment/login pages).

---

## 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. "GTM items: N/A, no tag manager present"; "payment-page items: N/A, no card/PII
collected client-side"). 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
(A08/A06/A05, or "—") and note **PCI DSS 6.4.3 / 11.6.1** where the script runs on a payment page.

**A. Magecart / formjacking (compromised third-party script skimming) — OWASP A08; PCI 6.4.3/11.6.1**
1. Compromised third-party script skims **payment fields** (card number/CVV/expiry) from the DOM
2. Formjacking of **login/PII fields** (email, password, address, SSN) via a tampered shared script
3. Overlay / fake-form injection (skimmer renders its own card form over the real one)
4. Keylogger / input-listener attached to sensitive inputs by a compromised dependency
5. Beacon exfil to an attacker host (image-pixel / `fetch` / `navigator.sendBeacon` / WebSocket)
6. Conditional/targeted skimmer (only fires on `/checkout`, only for some geos, time-bombed) — evades casual review
7. Double-entry / clone-and-forward skim (data sent to both the real processor and the attacker)

**B. Tag-manager (GTM/Tealium/Segment) abuse — OWASP A08/A05; PCI 6.4.3**
8. Compromised marketing/GTM account injects arbitrary **Custom HTML / custom JS** site-wide
9. GTM Custom HTML tag runs on **payment/login pages** (full DOM access, no review gate)
10. Container hijack / unauthorized publish (no 2-person review, weak account auth on the tag platform)
11. `dataLayer` poisoning / reading sensitive values pushed into `dataLayer` (tokens, PII, order totals)
12. A pixel/tag added through GTM that itself loads further untrusted script (tag-of-tags chain)

**C. Analytics / advertising pixel exfiltration — OWASP A08; PCI 6.4.3**
13. Pixel/session-replay auto-captures **form field values** (incl. card/PII) and ships them off-domain
14. Pixel exfiltrates **auth tokens / session identifiers / CSRF tokens** readable in the DOM or storage
15. Advertising tag with **broad DOM access** that a compromise turns into a skimmer
16. Consent bypass — pixel fires before/around consent, capturing PII it shouldn't
17. Pixel sending **full URLs with secrets in query strings** (password-reset links, tokens) to the ad network

**D. CDN-hosted library compromise / hijack — OWASP A08/A06; PCI 11.6.1**
18. CDN-hosted library tampered at the CDN (compromised CDN, MITM, cache poisoning) — **no SRI** to catch it
19. SRI present but applied to a **mutable** URL (`@latest`) → host serves a new version, SRI just fails-open if not enforced or breaks the page
20. CDN account/path **takeover** (abandoned package on unpkg/jsDelivr/cdnjs, username-scoped path republished)
21. Vendor-CDN compromise (a SaaS widget's own CDN is breached → all customers skimmed at once)

**E. Unpinned / mutable third-party `<script src>` — OWASP A08/A06**
22. Script referenced with **no version** or `@latest` (silent content change at any time)
23. Rolling "always-newest" loader URL (vendor pushes a new build → unreviewed code ships to your users)
24. Script loaded over a **takeoverable / wildcard** path (subdomain takeover of a vendor host, dangling DNS)
25. Mixed-content / non-HTTPS third-party script (MITM injection on the wire)

**F. Dependency confusion / typosquat reaching the client bundle — OWASP A08/A06**
26. Typosquatted front-end package compiled **into** the bundle (lookalike name)
27. Dependency-confusion: a public package shadows an internal name and lands in the client bundle
28. Malicious **transitive** front-end dependency pulled into the bundle (deep in the tree)
29. A front-end dep that **fetches code from a CDN at build time** (build-time injection into the bundle)

**G. Compromised npm front-end package (postinstall → bundle) — OWASP A08/A06**
30. `postinstall`/`preinstall` script in a front-end dep tampering with build output or injecting a payload
31. A legitimate package **hijacked** (maintainer-account takeover) shipping a browser-side payload in an update
32. Protestware / sabotage payload triggered by date/geo in a client dependency

**H. Source-map / source exposure — OWASP A05/A06**
33. `.map` files publicly served in production (full original source disclosure)
34. Source maps / comments leaking **internal API endpoints, hostnames, feature flags, logic**
35. Source maps / bundle leaking **secrets** that were inlined at build time
36. `sourceMappingURL` comments pointing at a reachable map even when not directly linked

**I. Secrets / keys shipped into the client bundle or public config — OWASP A05; defer rotation to ../20**
37. Server/secret API key, signing secret, or private key inlined into a client bundle (world-readable)
38. Over-scoped "public" key (a `pk_`-style key that actually grants write/admin, or a Google API key with no referrer restriction)
39. `NEXT_PUBLIC_`/`VITE_`/`REACT_APP_` (or equivalent) env var exposing a sensitive value to the browser
40. Secret leaked via public runtime config endpoint (`/config.js`, `/env.json`) the SPA fetches

**J. Missing / weak CSP constraining third-party script — OWASP A05; PCI 6.4.3 (defer header design to ../04)**
41. **No CSP at all** — any compromised/injected script runs and beacons anywhere
42. `script-src` with `unsafe-inline` and/or `unsafe-eval` (defeats most script-injection protection)
43. `script-src` too broad (`*`, `https:`, whole-CDN allowlist) → a takeover anywhere in the allowed origin runs
44. `connect-src` unconstrained → a skimmer can beacon to any host (no exfil-destination limit)
45. **No CSP `report-uri`/`report-to`** → violations (the early signal of injection) go unseen by SecureNow

**K. Transitive partner-domain trust — OWASP A08**
46. A partner/SaaS subresource on a domain with **weaker security than yours** (its breach = your skim)
47. Script-of-scripts chains where you trust N hops deep without inventory or SRI on any hop
48. A trusted third party that **changes what it loads** without notice (silent new subresource)

**L. Browser-extension / injected-content interaction — OWASP A05**
49. Malicious extension/userscript reads card/PII or session tokens off a sensitive page (no in-page integrity defense)
50. Extension/MITM rewrites a form action or injects a field — page has no tamper detection on critical handlers

**M. Negative-space & evasion (what makes client-side compromise hard to catch)**
51. Skimmer that only activates off-server (pure browser→attacker beacon) — **invisible to server telemetry** by design
52. Obfuscated / dynamically-assembled payload (string-concat of `atob`, `eval`, `Function`) evading static review
53. Exfil disguised as a legitimate analytics/CDN request (typosquatted host, e.g. `google-anaiytics.com`)
54. WebSocket / DNS-prefetch / CSS-`url()` / WebRTC exfil channels that bypass naive `connect-src` thinking
55. Geo/time/UA-gated activation so review machines and scanners never see the payload

**N. Observable abuse (what SecureNow telemetry can actually catch — the workhorse rows)**
56. **CSP violation report** spike (blocked-script / blocked-connect) ingested via the report proxy → injection/exfil onset
57. **Anomalous outbound destination** in *server* traffic to a new/known-bad host (compromised first-party server reaching an exfil/CDN host)
58. A client **integrity event** (`client.script.unexpected` / `client.exfil.detected`) you emit from a runtime monitor
59. Known-bad **exfil/skimmer host** hit from your infrastructure → blockable at the **network firewall**
60. Spike in client-error / RUM events referencing an unknown script origin (instrumentation-fed)

**O. Deferred — modeled in sibling models (reference, do not re-derive)**
61. CSP authoring, response-header hardening, first-party XSS/CSRF/CORS → [../04-xss-csrf-cors/](../04-xss-csrf-cors/) (**A05/A03**)
62. Server/build-time dependency & registry trust, CI/CD pipeline compromise, lockfile integrity → [../22-supply-chain-cicd/](../22-supply-chain-cicd/) (**A08/A06**)
63. Checkout/skimming **business impact**, fraud, chargebacks, payment-flow abuse → [../09-payment-business-logic/](../09-payment-business-logic/)
64. Rotation, least-privilege, and IAM blast-radius of any leaked key → [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/) (**A07/A05**)

> For 61–64, add **one** matrix row each marked *"deferred — see linked model"*, and only note
> the SecureNow-observable symptom here (e.g. a CSP-report spike, an outbound exfil beacon, a key
> appearing in traffic). The full detection/mitigation lives in the linked report.

> **Coverage expectation for this catalog.** Honestly, **A–L are mostly 🟡/🔴**: the real fix is
> CSP + SRI + script pinning + secret removal + source-map suppression, which are app/build
> changes SecureNow does not make. SecureNow turns 🟡 where you **emit a CSP-report or client
> integrity event** (catalog N), and 🟢 only where the malicious behavior **round-trips your
> server / firewall** (a compromised backend reaching an exfil host, a known-bad host blockable
> at the network firewall). Mark each row with the badge it truly earns.

---

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

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 HTML template, layout/`<Head>` component, GTM
  snippet, bundler/CSP config, env file, or the built artifact path. For built-output findings
  (secret in bundle, served `.map`), cite the source that produces them *and* the deployed path.
- **Pattern** — quote the 1–8 relevant lines. State the missing control precisely (e.g.
  "`<script src="https://cdn.example.com/lib.js">` — no `integrity=`, no version pin → CDN
  tamper undetectable"; "`script-src 'self' 'unsafe-inline' https:` → any injected inline script
  runs"; "GTM Custom HTML enabled and container loads on `/checkout`"; "`NEXT_PUBLIC_API_SECRET`
  in `.env` → shipped to browser"; "`*.js.map` emitted and served at `/_next/static/`").
- **Why exploitable** — the concrete compromise (which third party, what an attacker who controls
  it skims/exfiltrates, and from which page) and what they achieve (card theft, token theft, PII).
- **Severity** — critical / high / medium / low (impact × reachability; a skimmable third party on
  a live payment page with no CSP/SRI is **critical**).
- **Recommended fix (described, not applied)** — the specific change: e.g. "pin the script to an
  exact version and add an SRI `integrity` hash + `crossorigin=anonymous`"; "move the script into
  the first-party bundle so it's covered by build integrity"; "remove the secret from the client
  and proxy the call server-side; rotate the key (see ../20-secrets-and-cloud-iam/)"; "disable
  GTM Custom HTML on payment pages / route GTM behind review"; "add a strict `script-src` +
  `connect-src` allowlist and a `report-uri` (header design → ../04-xss-csrf-cors/)"; "stop
  deploying `.map` files publicly; upload them to the error tracker only". Reference the secure
  pattern, not a code diff. **You must not edit the codebase, build config, CSP, or HTML.**

If a control exists and is correct (every external script pinned + SRI; strict CSP with a
report endpoint; no secrets in the bundle; source maps not served), note it as a **strength** —
the posture must be honest. Absence of a control where the surface exists is itself a finding
("payment page loads 4 external scripts, 0 with SRI").

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

**SRI / pinning flaws** — external scripts/styles with no `integrity`/`crossorigin`; SRI on
mutable URLs; `@latest`/unversioned/rolling CDN paths; third-party scripts on payment/login pages
without SRI. *Fixes must mention* exact version pinning, SRI hashes, `crossorigin=anonymous`,
self-hosting where feasible, and a process to update the hash when the vendor publishes.

**Tag-manager flaws** — GTM/Tealium present with Custom HTML/custom-JS enabled; container loading
on sensitive pages; no publish review / weak account auth; sensitive values pushed to `dataLayer`.
*Fixes must mention* disabling Custom HTML on payment pages, server-side tagging where possible,
2-person publish review, scoping containers off card/PII pages, and not putting secrets in
`dataLayer`.

**Pixel / analytics flaws** — session-replay or pixels capturing inputs; pixels on card/PII pages;
URLs-with-secrets sent to ad networks; consent bypass. *Fixes must mention* input masking/blocklists
in the replay tool, excluding sensitive pages, stripping secrets from URLs before pixels fire, and
gating non-essential pixels behind consent.

**Bundle-secret flaws** — secret/over-scoped keys inlined; sensitive `*_PUBLIC_*` env vars; runtime
config endpoints leaking secrets. *Fixes must mention* moving the secret server-side and proxying,
using truly-public scoped keys with referrer/IP restrictions, **rotating the exposed key** (defer
to ../20-secrets-and-cloud-iam/), and a CI secret-scan on the built output.

**Source-map flaws** — `.map` served in prod; `sourceMappingURL` reachable; secrets in maps.
*Fixes must mention* disabling public source-map serving, hidden source maps uploaded to the error
tracker only, and a deploy check that 404s `*.map`.

**CSP-as-script-constraint flaws** — no CSP; `unsafe-inline`/`unsafe-eval`; broad `script-src`/
`connect-src`; **no `report-uri`/`report-to`**. *Fixes must mention* a strict nonce/hash-based
`script-src`, a tight `connect-src` exfil allowlist, removing `unsafe-*`, and **adding a report
endpoint** (full header authoring → ../04-xss-csrf-cors/). The missing report endpoint is also a
SecureNow instrumentation gap (Phase 4a).

**Front-end dependency flaws** — front-end deps with install scripts; typosquat-prone/recent
packages; build-time CDN fetches; dependency-confusion exposure reaching the bundle. *Fixes must
mention* lockfile pinning + integrity, disabling install scripts where possible, an allowlisted
internal registry/scope, and SCA on front-end deps (deep registry trust → ../22-supply-chain-cicd/).

---

## 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 compromised
  first-party server reaching a known-bad exfil host, a known-bad CDN/skimmer host droppable at
  the firewall.
- 🟡 **PARTIAL** — works after the customer adds instrumentation (a **CSP-report proxy** or a
  **client integrity monitor** emitting `track('client.*')` events), and SecureNow then alerts +
  contains at the edge — **but the real fix is the app/build control** (CSP/SRI/pinning/secret
  removal). Most catalog A–L rows are 🟡 *with the app/build fix as primary*.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this today (a pure browser→attacker skim that
  never touches your server and that you don't instrument). **Still include it**: give the
  app/build 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. It **cannot** see a
> skimmer running in a victim's browser that beacons straight to the attacker — that emits no
> server traffic and no event. Such a row is 🔴 **until** the app emits the signal (a CSP report
> or a client integrity event), at which point it becomes 🟡 (SecureNow alerts + can firewall-block
> the exfil host) with **CSP/SRI/pinning as the primary fix**. Pair the edge control with the
> app/build fix on **every** A–L row. Never badge a row 🟢 just because the threat is serious.

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

### 4a. Instrumentation (what client-side detections feed on)

Server traffic auto-captures once the app runs under `securenow run` / `securenow/register` /
`securenow init` — but a browser skimmer is *not* in that traffic. The two high-leverage feeds
you must add are a **CSP-report proxy** and a **client integrity monitor**, both shipped to
SecureNow via `securenow/events` `track()` (never throws). Emit from a **server route** (your CSP
`report-uri` handler, or a tiny beacon-collector the page POSTs to) so the event enters the same
pipeline as traffic:

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

// CSP violation report proxied into SecureNow (your report-uri handler receives the JSON,
// REDACTS any sensitive sample, then forwards the shape — this is the earliest injection signal):
track('client.csp.violation', { ip, attributes: { directive: 'script-src|connect-src|default-src', blocked_uri_host: 'evil.example', document_uri: '/checkout', disposition: 'enforce|report' } });

// A runtime client integrity monitor (your own MutationObserver / script-allowlist check) saw an
// unexpected <script> or outbound destination and POSTed it to a collector route:
track('client.script.unexpected', { ip, attributes: { src_host: 'unpkg.com', page: '/checkout', reason: 'unlisted_origin|no_sri|mutated_dom|new_subresource' } });
track('client.exfil.detected',    { ip, attributes: { dest_host: 'collector.evil', page: '/checkout', channel: 'fetch|beacon|img|websocket', field_kind: 'card|pii|token' } });

// An SRI / integrity failure fired in a real browser (script blocked by integrity mismatch):
track('client.sri.failure', { ip, attributes: { src_host: 'cdn.example', page: '/checkout' } });

// Your build/CI integrity gate flagged a client-side risk (secret in bundle, served map, new
// unpinned script) — emit so it shows up alongside runtime signals:
track('client.bundle.risk', { ip, attributes: { kind: 'secret_in_bundle|sourcemap_served|unpinned_script|missing_sri|risky_dependency', detail: '<hash_or_path>' } });
```

> **Redact before you emit.** A CSP report or exfil sample can contain the very card/PII a skimmer
> was stealing. Strip/hash form values, tokens, query strings, and full URLs (keep only host +
> path-template) before they become attribute values — see the Phase 1 telemetry-redaction check.
> Attributes feed detection; they must not become a new leak path.

Recommended client-side event taxonomy — rules match these **exact strings**:

| Event | Emit when |
|---|---|
| `client.csp.violation` | your CSP `report-uri`/`report-to` handler receives a violation (script/connect blocked) |
| `client.script.unexpected` | a runtime allowlist/MutationObserver monitor sees an unlisted/no-SRI/mutated script |
| `client.exfil.detected` | a runtime monitor sees an outbound request to an unexpected destination from a sensitive page |
| `client.sri.failure` | a browser blocks a subresource on SRI integrity mismatch |
| `client.bundle.risk` | a build/CI integrity gate flags a secret-in-bundle / served-map / unpinned-script / missing-SRI / risky-dep |

Custom `attributes` become queryable as `attributes_string['<key>']` (e.g.
`attributes_string['blocked_uri_host']`, `attributes_string['dest_host']`). Ingest enriches every
IP with **ASN/org** (`client.asn`, `client.as_org`).

### 4b. 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']`, the `client_ip` coalesce below.
Confirm any other column with a `--mode dry_run` before relying on it; OTEL SDK versions vary.

> Note for this domain: the **workhorse rules are events-based** (CSP reports + client integrity
> events on the **logs** table), because a browser skimmer is invisible to traces. The one
> traffic-based rule that adds value detects **your own server reaching an exfil/known-bad host**
> *if* that egress shows up as a span; otherwise lean on the network firewall blocklist.

**Events-based — CSP violation spike (script/connect blocked = injection/exfil onset):**

```sql
SELECT
  attributes_string['http.client_ip']    AS ip,
  attributes_string['blocked_uri_host']  AS blocked_host,
  attributes_string['document_uri']      AS page,
  count() AS violations
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'client.csp.violation'
  AND attributes_string['directive'] IN ('script-src','connect-src','default-src')
  AND timestamp >= now() - INTERVAL 15 MINUTE
GROUP BY ip, blocked_host, page
HAVING ip != '' AND violations >= 10
```

**Events-based — client exfil / unexpected-script on a sensitive page (any hit is high-signal):**

```sql
SELECT
  attributes_string['http.client_ip'] AS ip,
  attributes_string['event.type']     AS signal,
  attributes_string['dest_host']      AS dest_host,
  attributes_string['page']           AS page,
  count() AS hits
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] IN ('client.exfil.detected','client.script.unexpected','client.sri.failure')
  AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, signal, dest_host, page
HAVING ip != '' AND hits >= 1
```

**Events-based — build/CI integrity gate flagged a client-side risk (notify the runbook, ≥1):**

```sql
SELECT
  attributes_string['kind']   AS risk_kind,
  attributes_string['detail'] AS detail,
  count() AS occurrences
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'client.bundle.risk'
  AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY risk_kind, detail
HAVING occurrences >= 1
```

**Traffic-based — server egress to a suspected exfil host (only if outbound calls produce spans):**

```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,
       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 '%collector%' OR attributes_string['http.target'] LIKE '%.ru/%' OR attributes_string['http.target'] LIKE '%pixel%')
GROUP BY ip, target
HAVING ip != '' AND hits >= 1
```

> The egress rule is **notify-only** and **example-pattern** — replace the `LIKE` list with the
> actual known-bad/exfil hosts you maintain (or that your CSP reports surface). It catches a
> *compromised first-party server* phoning out; it does **not** catch a browser-only skim.

The other events follow the **same shape** — swap the `event.type` filter and threshold:
`client.csp.violation` (≥10/15m → injection onset; ≥1 on a payment-page `script-src` block →
escalate), `client.exfil.detected` (≥1 → page response), `client.sri.failure` (≥1 → a vendor
changed a pinned script or a CDN was tampered), `client.bundle.risk` (≥1 → notify, route to the
deploy runbook, never auto-block).

> **No injection signature rules apply here.** Unlike API injection (SQLi/XSS/RCE system
> signatures), client-side supply chain compromise has **no request-payload signature** to match —
> the malicious code runs in the browser. Do **not** claim signature/`instant.block` coverage for
> this domain; rely on CSP-report + integrity events + the firewall blocklist.

Useful attributes/columns: `event.type`, `http.client_ip`, `blocked_uri_host`, `dest_host`,
`src_host`, `page`, `directive`, `kind` (build-risk kind), `client.asn`, `client.as_org`.

**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 (so it copies cleanly) —
(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. The exact 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: Client-side — CSP violation spike (script/connect blocked)**  · 🟡 PARTIAL · A05 · high

```sql
-- rules/client-csp-violation.sql
SELECT
  attributes_string['http.client_ip']    AS ip,
  attributes_string['blocked_uri_host']  AS blocked_host,
  attributes_string['document_uri']      AS page,
  count() AS violations
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
  AND attributes_string['event.type'] = 'client.csp.violation'
  AND attributes_string['directive'] IN ('script-src','connect-src','default-src')
  AND timestamp >= now() - INTERVAL 15 MINUTE
GROUP BY ip, blocked_host, page
HAVING ip != '' AND violations >= 10
```

```bash
# save the SQL, then create the rule:
cat > rules/client-csp-violation.sql <<'SQL'
-- (paste the SQL block above)
SQL

securenow alerts rules create \
  --name "Client-side: CSP violation spike (script/connect blocked)" \
  --sql @rules/client-csp-violation.sql \
  --apps <APP_KEY> \
  --severity high \
  --schedule "*/5 * * * *" \
  --nlp "10+ CSP script/connect violations from one client in 15 minutes"
```

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

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

#### 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 (CSP-violation
spikes that a legitimate vendor version-bump trips, build-risk counts), 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, raise/lower the threshold and
add `securenow fp` exclusions for legitimate hits, then `--mode prod` to arm mitigation. Only
**high-precision** rules (an exact-match confirmed exfil/skimmer host hit, a known-bad ASN hit) 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 CSP-report spike, exfil/unexpected-script, SRI-failure, and build-risk rules
> are all heuristic/event-fed and trip on legitimate vendor changes — tag them **`test-first`**,
> ship in `--mode test`, and promote to `--mode prod` only after 3–7 days of observation. A rule
> keyed on a confirmed-bad exfil host (exact IoC) may be **`prod-ready`**.

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

For client-side supply chain, the **app/build fix is the primary control on almost every row**;
SecureNow **contains at the edge** (block a compromised first-party server's egress, block a
known-bad host, challenge/block an abusive source hitting your report collector). Always pair them.

Once a threat is confirmed, **choose the narrowest effective mitigation(s) from ALL of these** and
combine them (e.g. rate-limit your `/csp-report` collector + block the worst exfil-host IPs +
challenge a NAT egress flooding the collector). 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.

| # | 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 before the app. No app change. Add known exfil/skimmer host IPs to stop a compromised first-party server reaching them. |
| 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 the matching request (payload injection). **N/A for browser-side skims** — they carry no request-payload signature; do not claim this coverage here. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "..."` | confirmed-malicious source, all routes (e.g. a confirmed exfil/skimmer host reachable from your infra). |
| 4 | **IP block — scoped to route (+ method)** | `securenow blocklist add <ip> --route /csp-report --mode prefix --method ALL --app <APP_KEY> --env production --reason "..."` (`--mode exact\|prefix\|regex`, `--method GET\|POST\|…\|ALL`) | block an IP only on sensitive paths (e.g. your report/beacon collector); 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; audit-preserving unblock. |
| 6 | **Rate limit — per IP** | `securenow ratelimit add <ip> --limit 100 --window 1m --duration 24h --reason "..."` | throttle one abusive client across the app. |
| 7 | **Rate limit — per route (all clients, per-IP budget)** | `securenow ratelimit add --route /csp-report --mode prefix --method POST --limit 100 --window 1m --key-by ip` | cap an expensive/abusable endpoint for everyone, budgeted per IP (protect the CSP-report/beacon collector from being flooded into uselessness). |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /csp-report --mode exact --method POST --limit 20 --window 1m --duration 24h` · NL `securenow ratelimit from-text "rate limit /csp-report to 20/min for 24h" --yes` · test `securenow ratelimit test <ip> --path /csp-report --method POST` | precise throttle of one client on one route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /csp-report --difficulty 16 --clearance 30m` (route-wide) **or** `securenow challenge add <ip> --route /csp-report --difficulty 18 --clearance 30m` · test `securenow challenge test <ip> --path /csp-report --method POST` | bot spamming your report collector 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 of sources confirmed abusing the collector; actions include block / rate_limit / requireCaptcha. |
| 11 | **Session revocation** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | session theft / account takeover (e.g. a pixel exfiltrated a session token) — kill the stolen session, not the IP. |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "Vendor / monitoring source"` | stop false positives from known-good infra (your own RUM/monitoring, a vetted partner source) — 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 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 CSP-report/build-risk rule quiet after a known vendor change without weakening it. |
| 15 | **App / config / code fix (PRIMARY for this whole domain)** | *described in the Code-Findings report, never auto-applied* | the actual fix: **add SRI + pin versions**, **author/strengthen CSP + a report endpoint** (→ ../04-xss-csrf-cors/), **remove secrets from the bundle + rotate** (→ ../20-secrets-and-cloud-iam/), **stop serving source maps**, **gate/disable GTM Custom HTML on payment pages**, **mask inputs in pixels/session-replay**, **self-host or move third-party code into the build-integrity-covered bundle**. SecureNow contains; the fix removes. |

All commands above must be **verified against the installed SDK in Phase 0.5** (`securenow <cmd>
--help`) before you emit them; annotate `# requires securenow >= <ver>` for anything the installed
version lacks rather than emitting a broken command.

**Choosing per threat** — by **confidence**: exact IoC (confirmed exfil/skimmer host) → block;
probable bot flooding the collector on shared egress → **challenge**; noisy/legit-mixed CSP-report
or build-risk traffic → **rate-limit + notify-only (test-mode first)**; session compromise via a
pixel → **revoke**; known-good vendor/monitoring 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. **Crucially for this domain:**
SecureNow's edge controls protect the **collector route** and stop a **compromised first-party
server's egress** — they do **not** stop a browser-only skim, so on every A–L row write the
**app/config/build fix as primary** and the SecureNow control as containment/early warning. Mark
CSP-report and build-risk rules **notify-only** (a vendor pushing a legit new version trips them)
with the runbook command the human runs after confirming.

### 4d. Testing every detection and mitigation

Only test against apps/environments you own; 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`).

```bash
# Synthetic CSP violation spike — exercise the CSP-report rule end to end:
for i in $(seq 1 12); do
  securenow event send client.csp.violation --ip 203.0.113.50 \
    --attrs directive=script-src,blocked_uri_host=evil.example,document_uri=/checkout,disposition=enforce,test=true
done

# Synthetic exfil / unexpected-script detection (any hit fires):
securenow event send client.exfil.detected --ip 203.0.113.51 \
  --attrs dest_host=collector.evil,page=/checkout,channel=beacon,field_kind=card,test=true
securenow event send client.script.unexpected --ip 203.0.113.51 \
  --attrs src_host=unpkg.com,page=/checkout,reason=no_sri,test=true

# Synthetic build/CI integrity-gate finding (notify-only runbook rule):
securenow event send client.bundle.risk --ip 203.0.113.52 \
  --attrs kind=secret_in_bundle,detail=stripe_sk_live_in_main_chunk,test=true

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

# Confirm telemetry pipeline / inspect outbound-by-host in traffic:
securenow test-span "threat-model.client-supply-chain.smoke"
securenow forensics "outbound requests and unusual destination hosts in the last hour" --env production

# Edge mitigation verification (collector-route protection + firewall):
securenow ratelimit test 203.0.113.50 --path /csp-report --method POST
securenow challenge test 203.0.113.50 --path /csp-report --method POST
securenow firewall test-ip 203.0.113.50 --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 edge mitigation does). For 🔴 rows,
state explicitly that there is no SecureNow test — the validation is the **app/build fix**
(SRI/CSP/pinning verified in the browser / build output).

---

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

Write **two tracks**, each as Markdown + a self-contained HTML page, into
`threat/05-client-side-supply-chain/`:

- **Track A — Detection & Mitigation** (the operational runbook): what to run in SecureNow.
  → `client-side-supply-chain-detection-mitigation.md` + `.html`.
- **Track B — Code Findings & Recommendations** (the code audit): what is wrong in the codebase
  and the described fixes. → `client-side-supply-chain-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 app/build-fix links back to the Track A row it backs. Record the
**resolved installed `securenow` version** (Phase 0.5) in the appendix of both.

### 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 · mitigations), the top 3 **detectable** client-side risks for this specific app (name
   the actual third parties on payment/login pages), the installed `securenow` version + app key +
   firewall state, and a one-paragraph posture verdict honest about **low native coverage**
   (CSP/SRI/pinning are the real fixes; SecureNow adds early-warning via CSP-report/integrity
   events and firewall containment) with a one-line OWASP A08/A06/A05 + **PCI DSS 6.4.3/11.6.1**
   coverage note.
2. **SDK & environment** — installed SDK version (from `node_modules/securenow`), app key(s),
   environment, firewall state, 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 (+PCI) | Coverage 🟢/🟡/🔴 | Detection rule | Signal (threshold+window) | Schedule | Sev | Mitigation (app/build fix + edge) | Tag`.
   Severity ∈ {critical, high, medium, low}. The **Mitigation cell must pick specific, scoped
   mitigation(s) from the §4c toolbox** (by number/name, scoped to the right
   `route`/`method`/`IP`/`duration` — e.g. "app/build fix: SRI+pin (PRIMARY) + #9 challenge
   `/csp-report` + #4 route-scoped block of the exfil host") — never a generic "block the IP."
   The **Tag column marks each rule `test-first` or `prod-ready`** (per §4b). Then the
   **"Out of scope" N/A list** and the **"Deferred to sibling models"** rows (61–64) linking the
   numbered sibling models. Each gap/instrumentation row links to its **Track B code finding**.
4. **Detection rules to create** — each as the **ready-to-copy command 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); for every `test-first` rule include
   the `--mode test` create → observe (3–7 days) → `--mode prod` promotion step inline. Note rules
   that already exist (Phase 0). State explicitly that **no signature/instant-block rules apply** to
   this domain.
5. **Instrumentation the detections need** — only the `track('client.*')` events the rules above
   consume (the **CSP-report proxy** + **client integrity monitor**), each as a copyable snippet;
   redact before emitting; point to the **Track B** report for *where* in the code to add them.
6. **Mitigation mechanisms** — render the **full §4c toolbox table verbatim** (all 15 rows:
   firewall · instant-block [N/A here] · block [global / route+method / temporary] · rate-limit
   [IP / route / IP+route] · challenge · auto-block · revoke · trusted · allowlist · fp ·
   **app/config/build fix**) + the "Choosing per threat" paragraph + per-threat ready-to-copy
   mitigation command (scoped to the right route/method/IP/duration) + reversibility. Make explicit
   that the **app/build fix is primary for the entire domain** and the SecureNow control is
   containment + early warning, and that the highest-leverage one-time work is **CSP + SRI +
   pinning + secret removal + source-map suppression**.
7. **Action plan (copy-paste, ordered)** — ① engage the network firewall + add known exfil hosts,
   ② **add a CSP `report-uri` endpoint and proxy `client.csp.violation` into SecureNow** (the
   single biggest win), ③ add the client integrity monitor events where feasible, ④ create the
   event rules — **create every FP-prone (`test-first`) rule in `--mode test` (detect-only)** and
   include an explicit **"promote after N days" step** (`--mode prod` after 3–7 days of observation
   + threshold tuning + `securenow fp` exclusions); only `prod-ready` rules (exact IoC hits) are
   created armed, ⑤ enable challenge/rate-limit on the collector route, ⑥ test, ⑦ verify in
   dashboard, ⑧ schedule the **app/build-fix backlog from Track B** (SRI/pinning/CSP/secret-
   removal/source-map suppression — 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 192.0.2/198.51.100/203.0.113). For 🔴 rows,
   the browser/build verification that replaces a SecureNow test.
9. **Response runbooks** — per notification type (CSP-violation spike, exfil/unexpected-script,
   SRI failure, build risk): what fired, how to confirm a true positive (vendor change vs real
   compromise), the exact respond command (copy) **and** the Track B app/build fix to ship, and
   the exact reverse command (copy).
10. **Known gaps & SecureNow feature requests** — every 🔴 threat (browser-only skims, obfuscated
    payloads, geo/time-gated activation, extension injection): why it's not coverable today, the
    interim app/build fix (link to Track B), and the "contact the SecureNow team" line.
11. **Appendix** — resolved SDK/CLI version (Phase 0.5), app key, environment, firewall state,
    rule IDs created, the script-inventory snapshot date (PCI 6.4.3 requires periodic
    re-inventory — note the 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, build config, CSP, or HTML was modified."*

1. **Executive summary** — findings by severity (critical/high/med/low), the top 3 code risks, and
   a one-paragraph posture verdict.
2. **Surface & inventory** — the Phase 1 browser-side inventory: external-`<script>` catalog (host
   / pinned? / SRI? / sensitive-page?) + tag-manager posture + pixel/analytics list + CDN-library
   list + SRI/pinning table + CSP-as-script-constraint posture + bundle-secret scan + source-map
   status + dependency-integrity notes + transitive-trust map. This is the PCI 6.4.3 script
   authorization deliverable.
3. **Threat catalog** — the exhaustive Phase 2 catalog (A–O, grouped, each tagged OWASP/CWE + PCI
   where on a payment page, modeled or explicit N/A — drop nothing).
4. **Code-level findings (audit)** — the Phase 3 findings: a table
   `# | Location (file:line) | Threat | OWASP/CWE | Sev | Issue | Recommended fix`, each with the
   quoted 1–8 line snippet and the described fix (**never applied**).
5. **Strengths** — controls already present and correct (every external script pinned + SRI; strict
   CSP with a report endpoint; no secrets in the bundle; source maps not served) — honest posture.
6. **App / build fixes (primary remediation)** — the config/code/build changes that remove the
   root cause (described, not applied): add SRI + pin versions, author/strengthen CSP + a report
   endpoint (→ ../04-xss-csrf-cors/), remove secrets from the bundle + rotate
   (→ ../20-secrets-and-cloud-iam/), stop serving source maps, gate/disable GTM Custom HTML on
   payment pages, mask inputs in pixels/session-replay, self-host or move third-party code into
   the build-integrity-covered bundle. Each linked to the **Track A** detection/mitigation row it
   backs.
7. **Instrumentation recommendations** — the `track('client.*')` calls to add (CSP-report handler,
   integrity-monitor collector route, build/CI integrity gate) and the exact file:line to add
   them, so the **Track A** rules light up. Flag the CSP report endpoint as the single
   highest-leverage instrumentation to add.
8. **Appendix** — files reviewed, resolved SDK version (Phase 0.5), date, link to the **Track A**
   detection-mitigation 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 below**: it carries the
SecureNow brand tokens, the copy-button CSS, and the copy `<script>` at the end of `<body>`. 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.

```html
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title><!-- Track A: "Detection & Mitigation — Client-Side Supply Chain — SecureNow"
            Track B: "Code Findings — Client-Side Supply Chain — 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.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"><!-- Track A: "Detection & Mitigation · Client-Side Supply Chain"
                          Track B: "Code Findings · Client-Side Supply Chain" --></div>
    <!-- one <a href="#…"> per section of THIS track (5a = 11 sections, 5b = 8 sections) -->
  </nav>
  <main>
    <header class="top">
      <h1><!-- Track A: "Detection & Mitigation — Client-Side Supply Chain"
               Track B: "Code Findings — Client-Side Supply Chain" --></h1>
      <p><code><!-- app name / domain --></code> · <span class="pill">securenow <!-- installed version --></span>
         · <span class="pill"><!-- stack, e.g. Next.js SPA · GTM + 6 pixels · 3 CDN scripts --></span></p>
    </header>
    <div class="stats">
      <!-- Track A: threats modeled · covered · partial · gaps — SecureNow team · rules to create
           Track B: critical · high · medium · low · total findings
           5 .stat cards; numbers MUST equal the matrix/findings row counts -->
      <div class="stat"><div class="n"><!-- N --></div><div class="l"><!-- label --></div></div>
      <div class="stat"><div class="n" style="color:var(--ok)"><!-- N --></div><div class="l"><!-- label --></div></div>
      <div class="stat"><div class="n" style="color:var(--med)"><!-- N --></div><div class="l"><!-- label --></div></div>
      <div class="stat"><div class="n" style="color:var(--crit)"><!-- N --></div><div class="l"><!-- label --></div></div>
      <div class="stat"><div class="n" style="color:var(--high)"><!-- N --></div><div class="l"><!-- label --></div></div>
    </div>
    <!-- <section id="…"> blocks mirroring the Markdown sections of THIS track (5a or 5b) -->
    <footer>Generated by the SecureNow client-side supply chain 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>
```

Every SQL/command block in the **Track A (Detection & Mitigation)** HTML uses the copyable wrapper
(the `.cmd` div + the Copy button + the `<pre>`):

```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>`; OWASP tag → `<span class="owasp">A08</span>`
(add a second pill for `PCI 6.4.3`/`PCI 11.6.1` on payment-page rows); CWE → `<span class="cwe">CWE-…</span>`
(Track B findings); 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. The Track B HTML may
omit copy buttons on prose, but still wraps any example/fix command in `.cmd`.

---

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

- **Phase 0.5 ran:** the resolved installed `securenow` version appears in the appendix of **both**
  reports, 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/05-client-side-supply-chain/`
  (`client-side-supply-chain-detection-mitigation.md`+`.html`,
  `client-side-supply-chain-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
  (detection) HTML has a working Copy button**.
- The split is **honest**: SecureNow-runnable detections/mitigations live in the Detection report;
  code/build/config changes live in the Code-Findings report; nothing security-relevant is dropped.
- Every catalog item A1–O64 is either a matrix row or an explicit N/A line; each modeled row
  carries its OWASP 2021 tag (A08/A06/A05 or "—") **and** the PCI DSS 6.4.3/11.6.1 tag where the
  script runs on a payment page.
- The defining emphasis items are each modeled or explicit N/A: Magecart/formjacking,
  tag-manager/GTM abuse, pixel exfiltration, CDN-library compromise (missing SRI), unpinned
  `<script src>`, dependency-confusion/typosquat reaching the bundle, source-map exposure,
  secrets in the client bundle, compromised npm front-end package, weak/missing CSP, transitive
  partner trust, and browser-extension interaction.
- CSP authoring/headers/first-party-XSS, server/build-time deps & CI/CD, checkout/skimming
  impact, and secret rotation/IAM are **deferred** to the **numbered** sibling models
  (../04-xss-csrf-cors/, ../22-supply-chain-cicd/, ../09-payment-business-logic/,
  ../20-secrets-and-cloud-iam/) — rows present, linked, not re-derived.
- Coverage badges are **honest**: this is a low-native-coverage domain; most A–L rows are 🟡 with
  the app/build fix as **primary**, 🟢 only where the behavior round-trips the server/firewall, and
  every 🔴 (browser-only skim, obfuscated/gated payload, extension injection) is kept with an
  interim app/build fix + the "contact the SecureNow team" line.
- Every matrix row pairs the **edge containment WITH the app/build fix** (SRI/pinning/CSP/secret
  removal/source-map suppression) and has a concrete signal (threshold + window), severity, and
  mitigation — no "monitor for suspicious activity" filler.
- Every code finding in the Track B Code-Findings report has a `file:line`, the quoted snippet, and
  a described fix — and **no application code, build config, CSP, or HTML was 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, and selects
  an `ip` column with `HAVING ip != ''`; traffic queries keep the `ts_bucket_start` + `kind = 2`
  guards and use `--mode dry_run` to confirm columns.
- The report does **not** claim signature/`instant.block` coverage for this domain (no
  request-payload signature exists for a browser-side skim) and relies on CSP-report + client
  integrity events + the network firewall instead.
- Only commands, flags, events, and SQL columns from this prompt's building blocks appear
  (`securenow firewall`, `blocklist`, `ratelimit`, `challenge`, `automation`, `fp`, `trusted`, and
  the `client.*` events) — never invented.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted in, and lists the **CSP report
  endpoint** as the single highest-leverage instrumentation plus the app/build-fix backlog as the
  real remediation.
- Both HTML files are self-contained (no CDN, no fonts, no external network) with inline CSS + the
  copy `<script>`, and the stats cards match the table/findings counts.
- All **four** files are written to `threat/05-client-side-supply-chain/` and a one-line summary
  (per-track file paths, threat counts, rules-to-create count, code findings by severity, gaps,
  resolved SDK version, OWASP A08/A06/A05 + PCI 6.4.3/11.6.1 coverage) is printed back to the user.
- The Detection report's mitigation section presents the **full toolbox** (§4c: firewall ·
  instant-block [N/A here] · block [global / route / method / temporary] · rate-limit [IP / route /
  IP+route] · challenge · auto-block · revoke · trusted · allowlist · fp · app/build 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 (exact IoC hits)
  are `prod-ready`. The action plan creates the test-first rules in `--mode test` and has an
  explicit "promote after N days" step.

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