#14API1–API10:2023
API Security — OWASP API Top 10
The full OWASP API Security Top 10 (2023) for your HTTP/REST surface.
How to use this prompt
- 1Install SecureNow in your project (then optionally
npx securenow login):$ npm install securenow - 2Copy the prompt below and paste it into your AI coding agent (Claude Code, Cursor, Codex…) opened at the root of your project.
- 3It generates four files into
threat/14-api-security/— openapi-security-code-findings.html(the audit) andapi-security-detection-mitigation.html(the defenses) in your browser.
🔒Runs entirely in your environment — your codebase is never uploaded or shared. The generated HTML reports are self-contained and work offline.
The prompt
# API Security 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 the API surface, build an
exhaustive **API security** threat model mapped to the **OWASP API Security Top 10:2023**,
audit the code for API-layer flaws, and emit a SecureNow-branded **two-track** deliverable —
a **Detection & Mitigation** runbook and a **Code Findings & Recommendations** audit, each in
**Markdown + self-contained HTML** (offline, with one-click copy buttons on every command) —
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. Every rule and command is grounded in the `securenow` SDK actually installed in this repo
(`node_modules/securenow`) and emitted as ready-to-copy rule + command blocks.
This is the third of three complementary models. **Authentication** covers *who you are*
(credentials, sessions, tokens — OWASP **API2**). **Authorization** covers *what you may do*
(BOLA/BFLA/BOPLA, tenancy — OWASP **API1/API3/API5**). **API security** covers everything else
about the API *as an exposed surface*: resource consumption (**API4**), sensitive business
flows (**API6**), SSRF (**API7**), misconfiguration (**API8**), inventory drift (**API9**),
unsafe upstream consumption (**API10**), plus payload-borne injection and protocol abuse. It
**does not re-model** API1/2/3/5 — it references the other two and detects their *observable*
abuse only. Run [authentication](../01-authentication/authentication-threat-model-prompt.md) and
[authorization](../02-authorization/authorization-threat-model-prompt.md) too; the three together
cover the whole OWASP API Top 10.
> SecureNow is fundamentally an **API / traffic** security layer (firewall, rate-limit,
> challenge, exploit-signature instant-block, ASN enrichment, forensics). Of the three models,
> this one has the **highest native coverage** — most detections are traffic-based and need
> **no instrumentation at all**.
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 an API Security Threat Model Report (SecureNow)
You are a senior application-security engineer specializing in API security. Produce an
**exhaustive API security threat model for THIS codebase**, organized along the **OWASP API
Security Top 10:2023**, mapped to **SecureNow** detections and mitigations, with a ready-to-run
action plan **and** a code-level audit of every API-layer flaw you find. Every rule and command
you emit is grounded in the **installed** SecureNow SDK (`node_modules/securenow`) — never guess
flags, subcommands, event names, or SQL columns — and each detection is emitted as a
**ready-to-copy** rule + command block. You write **four** deliverables (two tracks) into
`threat/14-api-security/` (create the folder if needed):
1. `api-security-detection-mitigation.md` — the **operational runbook**: what to run in
SecureNow (rules to create, mitigations, tests, response runbooks).
2. `api-security-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 block**.
3. `api-security-code-findings.md` — the **code audit**: API-layer issues found in the codebase
+ recommended fixes (described, never applied).
4. `api-security-code-findings.html` — the same audit as a **self-contained** HTML page.
The two tracks **cross-link** each other: detection-report gap/instrumentation rows link to the
relevant code finding, and code findings link back to the detection row they back. Both HTML
files use the SecureNow branding skeleton in Phase 5.
Work in the seven phases below (0, 0.5, 1–5), in order. **Never invent facts**: if something is
not in the codebase or not returned by a CLI command, say "not found" — do not guess. **Do not
modify application code.** You are auditing: every code-level fix is *described in the report*,
never applied to the repo.
**Scope discipline.** This model owns OWASP **API4, API6, API7, API8, API9, API10**, payload
injection, and protocol/parsing abuse. For **API1 (BOLA)**, **API2 (Broken Authentication)**,
**API3 (BOPLA)**, **API5 (BFLA)**, do **not** re-derive the deep model — list them in a
"Deferred to the authn/authz models" subsection, link those reports, and only model their
**traffic-observable** symptoms (401/403 patterns, enumeration) 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** and any
**system signature rules** (SQLi/XSS/RCE) already present — those are the backbone of injection
coverage and must not be duplicated.
---
## 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 API surface (codebase analysis)
API security starts with an **inventory** (OWASP API9). Document what is **actually exposed**,
not what is intended. Cover at minimum:
- **Style & protocols** — REST, GraphQL, gRPC, WebSocket, SOAP/XML-RPC, server actions,
16-webhooks/callbacks. Public vs internal/service-to-service.
- **Endpoint catalog** — enumerate every route (method + path), grouped by router/controller.
Flag which are public, which are internal, which are admin. This catalog is a report
deliverable.
- **Versions & environments** — `/v1`, `/v2`, beta/legacy prefixes; are old versions still
routed? Are staging/dev/preview hosts internet-reachable? (API9 shadow/zombie APIs.)
- **Resource controls** — request body size caps, pagination ceilings (`limit`/`page_size`
maxima), per-route/per-client rate limits or quotas, timeouts, query-complexity limits
(GraphQL depth/cost). Note where **none** exist. (API4.)
- **Sensitive business flows** — purchase/checkout, payout/transfer, signup, coupon/referral,
invite, bulk export, gift-card redemption, anything a bot would farm or scalp. (API6.)
- **Outbound / upstream calls** — every place the server fetches a **user-influenced URL**
(webhooks, link previews, image/PDF proxies, import-from-URL, SSO metadata) → SSRF sinks
(API7). Every third-party/upstream API the server consumes and trusts (API10).
- **Parsers & content types** — JSON, XML (XXE risk), multipart, form-encoded, protobuf,
YAML, deserialization (Java/PHP/Python/.NET), file upload, archive/zip handling.
- **Configuration surface** — CORS policy, security headers (HSTS/CSP/X-CTO/X-Frame),
error verbosity/stack traces, debug & diagnostic endpoints (`/actuator`, `/metrics`,
`/__debug`, swagger/OpenAPI UI, GraphQL playground/introspection), enabled HTTP methods,
TLS posture, exposed cloud storage. (API8.)
- **Injection sinks** — raw SQL/NoSQL, shell/`exec`, template engines (SSTI), file-path
construction, eval, header/redirect construction from request input.
- **API contract & schema posture** — identify OpenAPI/Swagger specs, JSON schemas,
Zod/Joi/Yup/class-validator/Pydantic validators, protobuf schemas, GraphQL schemas,
request/response DTOs, and whether each route rejects unknown fields, duplicate keys, wrong
types, invalid enums, prototype-pollution keys, and oversized/nested objects.
- **Route normalization** — determine whether telemetry captures raw paths only
(`/api/orders/123`) or normalized route templates (`/api/orders/:id`). Prefer normalized
route templates for detection rules and keep raw path only for forensics.
- **Webhook endpoints** — enumerate all inbound webhook/callback endpoints, their providers,
signature verification method, timestamp tolerance, replay protection, event-id
deduplication, idempotency handling, and whether the raw body is verified before
parsing/trusting data.
- **Replay, race & idempotency controls** — identify all state-changing routes and sensitive
flows that need nonce/timestamp/idempotency/concurrency protection: checkout, payment, order
creation, upload jobs, coupon use, gift-card use, balance changes, payout, invite, referral,
export, LLM/image-generation calls, email/SMS sending, and webhook processing.
- **Proxy, host & forwarding trust** — inspect reverse-proxy assumptions, Express/Fastify/
Nest/Koa `trust proxy`, use of `Host`, `X-Forwarded-Host`, `X-Forwarded-Proto`,
`X-Forwarded-For`, `Forwarded`, `Origin`, and `Referer` headers, and whether untrusted public
clients can spoof them.
- **WebSocket/SSE/streaming APIs** — inventory WebSocket, Socket.IO, SSE, long-polling, AI
streaming, report/export streaming, upload/download streaming, and any long-lived connection
path. Check origin validation, per-message authorization, channel/tenant isolation,
message/byte/token/duration limits, queue limits, and connection rate limits.
- **Cache/CDN behavior** — identify CDN/proxy caching on API routes, `Cache-Control`, `Vary`,
02-authorization/session/tenant cache keys, static-looking suffixes, cacheable error responses,
and any API route reachable through a CDN edge.
- **File upload & media processing** — identify upload endpoints, direct-to-storage uploads,
signed URLs, object key generation, ACLs, tenant isolation, MIME/content validation,
SVG/PDF/image/archive processing, EXIF/metadata handling, virus/malware scanning, and async
processing cost limits.
- **API dependency posture** — identify framework, router, body parser, multipart parser,
GraphQL/gRPC libraries, XML/YAML parsers, OpenAPI/Swagger UI packages, webhook libraries,
media/PDF/image processors, reverse-proxy/ingress assumptions, and known vulnerable versions
from lockfiles/package manifests. Do not run destructive updates.
- **Telemetry privacy & redaction** — confirm whether SecureNow SDK/log pipeline redacts
`Authorization`, `Cookie`, `Set-Cookie`, `X-API-Key`, bearer tokens, API keys, webhook
secrets, payment tokens, emails, phone numbers, PII, sensitive body fields, and
customer-uploaded file/image URLs before ingestion. If not found, create a high-severity
finding.
- **SecureNow instrumentation already present** — `securenow/register` / `securenow run` /
`securenow init` (gives traffic spans automatically), any `securenow/events` `track()` calls,
and whether the firewall is engaged. This determines what works *today* vs *after
instrumentation*.
Output of this phase = the report's **API surface & inventory** section: the endpoint catalog
(method/path/visibility/version), a controls table (Size caps / Pagination / Rate limits /
Timeouts / Query complexity), the SSRF-sink + upstream list, the config posture, the **contract
& schema posture** (validators, unknown-field handling), the **webhook inventory** (provider /
signature / replay / idempotency), the **replay/race/idempotency control map** for sensitive
flows, the **proxy/host-trust posture**, the **streaming (WS/SSE) inventory**, the **cache/CDN
behavior**, the **upload/media posture**, the **API dependency posture**, the **telemetry
redaction status**, and a short paragraph naming the real API attack surface for this stack.
---
## Phase 2 — Enumerate threats (exhaustive catalog)
Evaluate **every** threat below against the discovered surface. Each item is either **modeled**
(a row in the threat matrix) or **explicitly N/A** (one line in an "Out of scope" subsection
with the reason — e.g. "GraphQL items: N/A, REST-only API"). Never silently drop an item. Add
stack-specific threats you discover that are not listed — this catalog is the floor, not the
ceiling. Tag each modeled row with its **OWASP API Top 10:2023** code (API1–API10, or "—").
**A. Unrestricted resource consumption (OWASP API4)**
1. Volumetric L7 flood on an endpoint (high request rate from one IP)
2. Distributed flood (many IPs within one ASN / botnet)
3. Expensive-endpoint amplification (one request → heavy CPU/DB: search, report, export, regex)
4. Oversized request payload / no body-size cap (memory exhaustion)
5. Unbounded pagination / `limit`/`page_size` with no ceiling (mass pull per request)
6. Missing per-client rate limits or quotas (no 429s anywhere)
7. Third-party cost amplification (each call triggers a paid downstream: SMS/email/LLM/maps)
8. Slow-body / connection-holding (Slowloris-class) resource starvation
**B. Unrestricted access to sensitive business flows (OWASP API6)**
9. Automated scraping / data harvesting at scale on read endpoints
10. Inventory scalping / hoarding (tickets, stock, reservations) via the buy flow
11. Signup / coupon / referral / free-tier farming through the API
12. Comment / review / message spam via public write endpoints
13. Gift-card / balance / promo-code enumeration & draining
14. Workflow automation a human wouldn't perform at that rate (replay of a multi-step flow)
**C. Injection through the API (payload-borne exploits)**
15. SQL injection in API parameters/body
16. NoSQL / operator injection (`$gt`, `$ne`, `$where`, JSON operator smuggling)
17. OS / command injection (RCE) via API input
18. Cross-site scripting reflected or stored through an API field
19. Server-side template injection (SSTI)
20. LDAP / XPath / header / log injection
21. Path traversal / LFI via an API parameter (`../`, encoded, absolute path)
**D. Server-side request forgery (OWASP API7)**
22. SSRF to cloud metadata (IMDS `169.254.169.254`, GCP/Azure metadata endpoints)
23. SSRF to internal/private/link-local services via a user-supplied URL
24. Blind SSRF / out-of-band callback (webhook, url-fetch, image proxy, PDF/HTML render sink)
25. SSRF filter bypass (DNS rebinding, redirect-to-internal, decimal/hex/octal IP, `[::]`, `0`)
**E. Security misconfiguration (OWASP API8)**
26. CORS misconfiguration (reflected origin + credentials, or `*` with credentials)
27. Missing/weak security headers (HSTS, CSP, X-Content-Type-Options, X-Frame-Options)
28. Verbose errors / stack traces / framework & version banners leaking internals
29. Debug/diagnostic endpoints exposed (`/actuator`, `/metrics`, `/__debug`, swagger, GraphQL playground)
30. Default credentials / sample routes / unhardened defaults reachable in prod
31. Unnecessary HTTP methods enabled (TRACE, reflective OPTIONS, unintended PUT/DELETE)
32. Missing transport hardening (HTTP allowed, no HSTS, TLS downgrade, mixed content)
33. Public cloud storage / bucket / object reachable via or referenced by the API
**F. Improper inventory management (OWASP API9)**
34. Shadow APIs — undocumented endpoints live in prod, absent from the spec
35. Zombie / deprecated versions still routed (`/v1` after `/v2` ships)
36. Non-prod environments internet-exposed (staging/dev/preview hosts)
37. Spec drift — documented routes ≠ deployed routes (params, methods, fields)
38. Sensitive data still flowing through old / unmonitored endpoints
**G. Unsafe consumption of upstream APIs (OWASP API10)**
39. Blindly trusting upstream/third-party responses (no validation → downstream injection)
40. Following upstream redirects to untrusted hosts
41. Upstream timeout/error unhandled → request pileup, partial data, or data leak
42. Secrets/tokens sent to the wrong or over-broad upstream (egress over-trust)
**H. Protocol, parsing & payload attacks**
43. XML external entity (XXE) / entity-expansion (billion-laughs) DoS
44. Insecure deserialization (gadget chains; polymorphic/typed JSON)
45. Decompression bombs (zip/gzip) / multipart-part flooding
46. Content-type confusion / parser differential (JSON-as-form, charset tricks)
47. HTTP request smuggling / desync (CL.TE, TE.CL through a fronting proxy)
48. HTTP response splitting / CRLF header injection from a reflected parameter
**I. GraphQL & batch-specific** (model only if GraphQL/batch present, else N/A)
49. Introspection enabled in production (schema disclosure)
50. Deep / circular query (query-depth & complexity amplification DoS)
51. Alias / field-duplication amplification; query-batching abuse
52. Batch / bulk endpoint bypassing per-item limits or per-item authz
**J. Data exposure at the API edge**
53. Sensitive data over-returned in responses (PII/secrets/tokens) — traffic-observable as anomalous responses; app fix primary
54. Secrets/keys/tokens leaked in URLs, query strings, error bodies, or logs
55. Per-user API responses cached on a shared CDN/proxy → cross-user leak / cache poisoning
56. Existence/enumeration oracle via response or timing differences
**K. Negative-space & evasion**
57. Path/encoding normalization bypass of WAF/rate-limit (`%2e`, double-encoding, case, trailing slash, matrix params)
58. HTTP method override (`X-HTTP-Method-Override`, verb tunneling) reaching protected verbs
59. Direct-origin access bypassing the CDN/WAF (hitting the origin IP/host)
60. Client-IP header spoofing (`X-Forwarded-For`) to evade per-IP limits
**L. Deferred — modeled in the authn/authz reports (reference, do not re-derive)**
61. Broken object-level authorization (BOLA, **API1**) → [authorization model](../02-authorization/authorization-threat-model-prompt.md)
62. Broken authentication (**API2**) → [authentication model](../01-authentication/authentication-threat-model-prompt.md)
63. Broken object-property authz (BOPLA, **API3**) & broken function-level authz (BFLA, **API5**) → authorization model
> For 61–63, add **one** matrix row each marked *"deferred — see linked model"*, and only note
> the SecureNow traffic-observable symptom (e.g. 401/403 spikes, ID enumeration) here. The full
> detection/mitigation lives in the other reports.
**M. Observable abuse (what telemetry actually catches — the workhorse rules)**
64. 4xx/5xx spike from one IP or ASN (fuzzing, probing, broken integration)
65. 429 ceiling repeatedly hit by one client (ignoring back-off — abuse or runaway)
66. One IP touching an anomalous count of **distinct** endpoints (API mapping / fuzzing)
67. Exploit-signature match in a request (SQLi/XSS/RCE patterns) → instant block
68. Sudden traffic spike / new high-volume source (volumetric onset)
69. Requests to deprecated / shadow / non-prod paths surfacing in live traffic (inventory drift)
70. Large / anomalous response sizes from a single client (bulk extraction / scraping)
**N. Contract, webhook, replay, streaming, cache, upload & dependency threats**
71. Missing strict request schema validation — unknown fields, duplicate keys, type confusion, lenient coercion, invalid enum values, excessive nesting, or unbounded object structure
72. HTTP parameter pollution / duplicate parameter ambiguity between CDN, proxy, framework, and application parser
73. Prototype pollution through JSON/body/query keys such as `__proto__`, `constructor`, or `prototype`
74. Response contract drift — sensitive or undeclared fields returned by API responses
75. Webhook signature missing, weak, bypassable, or verified after trusting/parsing the body
76. Webhook replay attack — no timestamp tolerance, nonce, event-id deduplication, or idempotency
77. Duplicate webhook processing causing double shipment, double credit, double order, double email/SMS, duplicate job execution, or state corruption
78. Replay of state-changing API requests without nonce/timestamp/idempotency protection
79. Race condition / parallel request abuse in checkout, coupon, gift-card, balance, inventory, upload, export, invite, referral, or payout flows
80. Missing idempotency on paid downstream calls, causing duplicate charge/order/email/SMS/LLM/API/image-generation cost
81. Host header / `X-Forwarded-Host` poisoning causing poisoned links, redirects, cache keys, password-reset links, magic links, OAuth redirects, webhook URLs, or tenant routing
82. Misconfigured proxy trust — spoofed `X-Forwarded-*`, `Forwarded`, or client-IP headers accepted from the public internet
83. Tenant/domain confusion via Host header, custom-domain routing, workspace slug routing, or organization subdomain routing
84. WebSocket/SSE connection flood or long-lived connection exhaustion
85. WebSocket message-level authorization missing after initial connection authentication
86. WebSocket/SSE cross-tenant subscription, room, topic, or channel data leak
87. Streaming API cost amplification — long-running AI/export/report/download streams not capped by duration, tokens, messages, bytes, or server-side work
88. Server/provider API keys exposed to frontend bundles, mobile apps, public source maps, public config, or client-readable env
89. Credentials/tokens transmitted in query strings, Referer headers, analytics pixels, logs, telemetry, or third-party calls
90. Sensitive request/response bodies captured by telemetry without redaction
91. API cache key confusion — authenticated responses cached without varying on auth/session/tenant
92. Web cache deception against API/user endpoints through static-looking suffixes such as `/api/me/avatar.css`
93. Cache poisoning through Host, `X-Forwarded-Host`, query params, method override, unkeyed headers, or cacheable error responses
94. Unsafe file upload validation — extension/MIME/content mismatch, SVG script, polyglot files, malicious archives, or parser exploit surface
95. Uploaded object storage exposure — predictable keys, public-read ACL, missing tenant isolation, direct object overwrite, or over-broad signed URLs
96. Media/PDF/image/archive processing cost or parser exploitation through crafted files
97. Sensitive metadata leakage from uploaded files, including EXIF GPS, author, device, embedded thumbnails, hidden layers, or document metadata
98. Vulnerable API framework, parser, middleware, OpenAPI UI, GraphQL, gRPC, XML/YAML, webhook, media, PDF, image, or multipart dependency
99. Deprecated runtime/framework/proxy version exposing known API-layer CVEs
> Items N71–N99 are mostly OWASP **API4/API6/API8/API9/API10** (tag each row accordingly;
> webhook/replay/race → API6/API8, contract/schema → API8, dependency → API9/API8, telemetry
> leakage → API8/API3-deferred-symptom). Each must be either a matrix row or an explicit N/A
> line with a reason — do not silently drop any item.
---
## Phase 3 — Audit the code (findings only — do not fix)
For **each** modeled threat that maps to real code, locate the responsible code and record a
**finding** for the report's "Code-level findings" section. A finding is:
- **Location** — `file:line` (clickable), the route/handler/middleware/resolver name.
- **Pattern** — quote the 1–8 relevant lines. State the missing control precisely (e.g. "no
`express.json({ limit })` → unbounded body"; "`fetch(req.body.url)` with no host allowlist →
SSRF"; "`cors({ origin: true, credentials: true })` reflects any origin"; "`/v1` router still
mounted after `/v2`").
- **Why exploitable** — the concrete request an attacker sends and what they achieve.
- **Severity** — critical / high / medium / low (impact × reachability).
- **Recommended fix (described, not applied)** — the specific change: e.g. "cap body size and
reject >256 KB"; "enforce a max `page_size` and a hard `LIMIT`"; "validate the outbound URL
against an allowlist and block link-local/private ranges, disable redirects"; "lock CORS to
an explicit origin list, drop `credentials` for public routes"; "remove the swagger/actuator
route in production"; "retire the `/v1` router or gate it behind the gateway"; "set
`Content-Security-Policy` and `Strict-Transport-Security`". Reference the secure pattern, not
a code diff. **You must not edit the codebase.**
If a control exists and is correct (size caps present, SSRF allowlist enforced, introspection
disabled in prod), note it as a **strength** — the posture must be honest. Absence of a control
where the surface exists is itself a finding ("no rate limit on any route").
For each modeled threat **N71–N99** that maps to real code, record a finding the same way
(`file:line`, 1–8 quoted lines, exploitability, severity, described fix — **no code edits**).
Look specifically for:
**Strict API contract / schema flaws** — handlers reading `req.body`/`req.query`/`req.params`
directly without schema validation; validators that allow unknown fields by default; missing
`additionalProperties: false` equivalent; missing DTO validation/transformation safety; type
coercion of booleans/numbers/enums; undocumented/unguarded duplicate-JSON-key behavior; `qs` /
`body-parser` / `express.urlencoded` / nested-parser settings that allow deep objects, arrays,
prototype keys, or excessive nesting; no maximum object depth, array length, string length, or
field count. *Recommended fixes must mention* strict schema validation, unknown-field
rejection, explicit type parsing, enum allowlists, prototype-key blocking, depth/array/string
limits, and response-DTO allowlisting.
**Webhook flaws** — signature verification missing; performed after `JSON.parse` or after
trusting fields; not using the raw body; no timestamp tolerance; no replay/event-id dedupe; no
idempotent processing; non-constant-time signature comparison; source-IP allowlist used as the
only trust mechanism. *Recommended fixes must mention* raw-body HMAC verification, constant-time
comparison, timestamp tolerance, replay cache / event-id dedupe, idempotency keys, and verifying
before processing.
**Replay, race & idempotency flaws** — state-changing POST/PATCH/DELETE without idempotency
keys; payment/order/upload/generation/email/SMS/LLM calls that can be triggered repeatedly;
coupon/gift-card/balance/inventory updates without transaction/lock/unique constraint; async
job creation without dedupe; retry logic without idempotent provider keys. *Recommended fixes
must mention* idempotency keys, nonce/timestamp windows, database unique constraints,
transactions, optimistic locking, per-resource locks, and provider idempotency.
**Proxy/header trust flaws** — use of `req.headers.host`, `x-forwarded-host`,
`x-forwarded-proto`, `x-forwarded-for`, `origin`, or `referer` for redirects, absolute URLs,
tenant routing, password-reset links, OAuth callbacks, webhook URLs, or cache keys without
allowlisting; `app.set('trust proxy', true)` (or equivalent) without a trusted-proxy CIDR list;
client IP derived directly from `X-Forwarded-For`. *Recommended fixes must mention* explicit
trusted-proxy CIDRs, a canonical public base-URL config, host allowlists, tenant-domain
allowlists, ignoring public spoofed forwarding headers, and deriving client IP only from
trusted ingress.
**WebSocket/SSE/streaming flaws** — endpoints with no origin check; auth checked only at
connection time, not per message/action/subscription; rooms/channels/topics joined from client
input without tenant/user authorization; no connection/message/byte/token/duration limits;
unbounded queues or broadcast fanout; long-lived streams that bypass normal HTTP rate limits.
*Recommended fixes must mention* origin allowlists, per-message authz, channel-ownership checks,
message/connection quotas, duration/byte/token caps, backpressure, and route-specific
challenge/rate-limit.
**Cache/CDN flaws** — authenticated API responses with cacheable headers; missing
`Cache-Control: private, no-store` on sensitive routes; missing `Vary: Authorization, Cookie`
where caching is used; API routes behind a CDN without explicit cache bypass; static-looking
suffixes accepted on dynamic user endpoints; cache keys influenced by untrusted
Host/forwarded headers. *Recommended fixes must mention* no-store/private caching for user
data, correct `Vary` headers, explicit CDN bypass for APIs, cache-key hardening, and rejecting
deceptive suffixes.
**File upload / media flaws** — file type checked only by extension or client-provided MIME;
SVG/PDF/image/archive/office files processed without sandboxing; uploaded objects stored with
predictable names; public-read ACLs for private files; signed URLs too broad or too long-lived;
no tenant prefix isolation; EXIF/metadata not stripped; no malware scan or async processing
limits; no max file size, part count, archive depth, decompressed size, pixel count, page
count, or processing time. *Recommended fixes must mention* content sniffing, allowlisted
MIME/signatures, AV scan where appropriate, sandboxed processors, metadata stripping, random
object keys, tenant-scoped prefixes, private buckets, short-lived signed URLs, and
file/processing limits.
**Dependency / framework exposure** — outdated API frameworks, body/multipart parsers, OpenAPI
UI, GraphQL/gRPC libraries, XML/YAML parsers, webhook libraries, media/PDF/image processors, or
runtime versions; deprecated Node/Python/Java/PHP/.NET versions; Swagger/OpenAPI UI exposed in
production with vulnerable package versions. *Recommended fixes must mention* upgrading
vulnerable packages, removing or gating prod debug/spec UIs, replacing unsupported runtimes, and
adding dependency scanning to CI. (Inventory from lockfiles/manifests — **do not run
destructive updates**.)
---
## Phase 4 — Map every modeled threat to SecureNow detection + mitigation
Classify each threat with exactly one coverage badge:
- 🟢 **COVERED** — detectable + mitigable with SecureNow today (existing rule, system signature
rule, or a rule you provide the SQL for, on telemetry that is already flowing). Most volumetric,
scraping, fuzzing, and injection threats land here — this is SecureNow's home turf.
- 🟡 **PARTIAL** — works after the customer adds instrumentation (`track('api.*')` / `ssrf.blocked`
events), or the detection is inherently pattern-based / false-positive-prone (notify-only), or
SecureNow can only *contain the abuser at the edge* while the real fix is app/config-level.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this today. **Still include it**: give the
app/config-level fix, then add the line *"Requires SecureNow team — contact your SecureNow
account contact (or in-dashboard support) to request support for this threat."* Collect all
gaps in the report's "Known gaps & SecureNow feature requests" section.
> **Be honest about what is edge-detectable vs an app fix.** SecureNow sees **traffic** and
> **events**; it contains actors via firewall / rate-limit / challenge / block / signature
> instant-block. It **cannot** see what it isn't told: a misconfigured CORS header, an unbounded
> `LIMIT` in a query, a deprecated route nobody hits yet, or an SSRF sink with no guard are
> **config/app fixes** — SecureNow detects the *abuse* (the scrape, the flood, the metadata
> fetch attempt that errors) and contains the source, but the missing control is the primary
> fix. Pair the control with the app fix on every such row. A flaw that emits no traffic and no
> event is a 🔴 until the app emits the event in Phase 3's recommended fix.
Use **only** the SecureNow building blocks below. Never invent CLI flags, event names, or SQL
columns.
### 4a. Instrumentation (what API detections feed on)
The big advantage here: once the app runs under `securenow run` / `securenow/register` /
`securenow init`, **HTTP traffic is captured automatically** — status codes (incl. **429** and
**5xx**), methods, paths, client IPs, response sizes. **Most API-security rules need no events.**
Add `securenow/events` `track()` only for app-internal signals traffic can't see (never throws):
```js
const { track } = require('securenow/events');
// The app rejected a request itself (its own quota / size / schema gate):
track('api.ratelimit.exceeded', { userId, ip, attributes: { route: '/api/search', limit: '100', window: '1m' } });
track('api.payload.rejected', { ip, attributes: { route: '/api/upload', reason: 'too_large', bytes: '9100000' } });
// An outbound URL fetch was blocked by your SSRF guard (API7) — high-signal:
track('ssrf.blocked', { ip, attributes: { route: '/api/preview', target_host: '169.254.169.254', reason: 'link_local' } });
// A consumed upstream/third-party API failed or returned untrusted data (API10):
track('api.upstream.error', { ip, attributes: { upstream: 'payments', status: '502', route: '/api/checkout' } });
// A deprecated/legacy endpoint or version was hit (API9 inventory drift):
track('api.deprecated.called', { ip, attributes: { route: '/v1/orders', superseded_by: '/v2/orders' } });
// A sensitive business flow executed (API6) — feeds abuse-rate detection:
track('api.sensitive.flow', { userId, ip, attributes: { flow: 'checkout', items: '50' } });
```
**Advanced API events** — contract, webhook, replay/race, proxy-trust, streaming, cache,
upload/media, and dependency signals (all `api.*`; emit at the enforcement point, never throws):
```js
const { track } = require('securenow/events');
// Strict API contract / schema validation:
track('api.schema.rejected', { userId, ip, attributes: { route: '/api/example', reason: 'unknown_field|duplicate_key|type_mismatch|invalid_enum|prototype_key|too_deep|too_many_fields', field: 'role' } });
// Webhook authenticity / replay:
track('api.webhook.signature_failed', { ip, attributes: { route: '/16-webhooks/provider', provider: 'stripe|shopify|github|meta|custom', reason: 'missing|invalid|expired_timestamp|bad_format' } });
track('api.webhook.replay_blocked', { ip, attributes: { route: '/16-webhooks/provider', provider: 'stripe|shopify|github|meta|custom', event_id: '<hash_or_id>' } });
track('api.webhook.duplicate_ignored',{ ip, attributes: { route: '/16-webhooks/provider', provider: 'stripe|shopify|github|meta|custom', event_id: '<hash_or_id>' } });
// Replay, race and idempotency:
track('api.replay.blocked', { userId, ip, attributes: { route: '/api/checkout', reason: 'duplicate_nonce|expired_timestamp|duplicate_idempotency_key', flow: 'checkout|coupon|gift_card|payout|upload|export' } });
track('api.race.detected', { userId, ip, attributes: { route: '/api/coupon', resource_id: '<hash_or_id>', flow: 'checkout|coupon|gift_card|balance|inventory|payout' } });
track('api.idempotency.missing',{ userId, ip, attributes: { route: '/api/order', flow: 'checkout|payment|webhook|email|sms|llm|image_generation' } });
// Proxy / host header trust:
track('api.proxy.spoof_detected', { ip, attributes: { route: '/api/example', header: 'x-forwarded-for|x-forwarded-host|forwarded|host', reason: 'untrusted_forwarded_header|host_not_allowed' } });
// Streaming / WebSocket / SSE:
track('api.stream.started', { userId, ip, attributes: { route: '/api/stream', type: 'sse|websocket|ai_stream|export_stream' } });
track('api.stream.limit_exceeded', { userId, ip, attributes: { route: '/api/stream', type: 'sse|websocket|ai_stream|export_stream', reason: 'duration|bytes|messages|tokens|queue' } });
track('api.websocket.authz_failed',{ userId, ip, attributes: { route: '/ws', channel: '<hash_or_name>', action: 'subscribe|publish|join|message' } });
// Cache security:
track('api.cache.sensitive_blocked', { userId, ip, attributes: { route: '/api/me', reason: 'auth_response_cacheable|missing_vary|deceptive_suffix|host_cache_key' } });
// File upload / media processing:
track('api.upload.rejected', { userId, ip, attributes: { route: '/api/upload', reason: 'mime_mismatch|too_large|too_many_parts|archive_bomb|svg_blocked|malware|pixel_limit|page_limit|metadata', bytes: '<number_as_string>' } });
track('api.media.processing_limit',{ userId, ip, attributes: { route: '/api/process', reason: 'timeout|pages|pixels|decompressed_size|archive_depth|cpu', file_type: 'image|pdf|zip|svg|office' } });
// Dependency exposure:
track('api.dependency.vulnerable', { ip, attributes: { package: '<name>', version: '<version>', severity: 'critical|high|medium|low', surface: 'framework|parser|multipart|graphql|grpc|swagger|xml|yaml|media|runtime' } });
```
> Hash or omit any PII before it becomes an attribute value (emails, raw tokens, file URLs,
> webhook secrets) — see the Phase 1 **telemetry privacy & redaction** check. Attributes feed
> detection; they must not become a new leak path.
Recommended API event taxonomy — rules match these **exact strings**:
| Event | Emit when |
|---|---|
| `api.ratelimit.exceeded` | the app's own quota/limit rejects a request |
| `api.payload.rejected` | a body fails size / schema / content-type validation |
| `ssrf.blocked` | an outbound fetch is denied by an SSRF allowlist/guard |
| `api.upstream.error` | a consumed upstream/third-party call fails or times out |
| `api.deprecated.called` | a deprecated endpoint / API version is invoked |
| `api.sensitive.flow` | a sensitive business flow (checkout/payout/export) executes |
| `api.schema.rejected` | a request fails strict schema validation (unknown field, dup key, type, enum, prototype key, depth, field count) |
| `api.webhook.signature_failed` | an inbound webhook signature is missing/invalid/expired/malformed |
| `api.webhook.replay_blocked` | a webhook is rejected by timestamp/nonce/event-id replay protection |
| `api.webhook.duplicate_ignored` | an already-processed webhook event-id is ignored (idempotency) |
| `api.replay.blocked` | a state-changing request is rejected (dup nonce / expired ts / dup idempotency key) |
| `api.race.detected` | concurrent/parallel abuse of a single-use or limited flow is detected |
| `api.idempotency.missing` | a paid/state-changing call ran without an idempotency key |
| `api.proxy.spoof_detected` | an untrusted forwarding/Host header was rejected |
| `api.stream.started` | a WebSocket/SSE/AI/export stream opens (feeds duration/volume detection) |
| `api.stream.limit_exceeded` | a stream hits a duration/bytes/messages/tokens/queue cap |
| `api.websocket.authz_failed` | a per-message/subscribe/publish/join authz check fails |
| `api.cache.sensitive_blocked` | a cacheable-auth-response / deceptive-suffix / host-cache-key risk is blocked |
| `api.upload.rejected` | an upload fails MIME/size/parts/archive/SVG/malware/pixel/page/metadata checks |
| `api.media.processing_limit` | media/PDF/image/archive processing hits a cost/parser limit |
| `api.dependency.vulnerable` | a vulnerable API framework/parser/library/runtime is detected |
Custom `attributes` become queryable as `attributes_string['<key>']` (e.g.
`attributes_string['target_host']`). Ingest enriches every IP with **ASN/org** (`client.asn`,
`client.as_org`) — enabling botnet/datacenter-origin detection with no extra code.
### 4b. Detection rules — SQL conventions
**Rule lifecycle: 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 (flood / scrape
/ enumeration / distinct-path counts), broad patterns, anomaly / volume / response-size 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
(exploit-signature SQLi/XSS/RCE matches, exact-match IoCs, known-bad ASN hits, single-hit SSRF/proxy
-spoof event rules) 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.)
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 (`http.method`, response size) with a `--mode dry_run`
before relying on it; OTEL SDK versions vary (`http.method` vs `http.request.method`).
**Traffic-based — volumetric flood (single IP), no events needed:**
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
count() AS requests,
uniqExact(attributes_string['http.target']) AS distinct_paths
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 5 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 5 MINUTE)) - 1800
AND kind = 2
GROUP BY ip
HAVING ip != '' AND requests >= 600
```
**Traffic-based — endpoint fuzzing / API mapping (many distinct paths + client errors):**
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
uniqExact(attributes_string['http.target']) AS distinct_paths,
countIf(response_status_code IN ('400','401','403','404','405','422')) AS client_errors
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 15 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
AND kind = 2
GROUP BY ip
HAVING ip != '' AND distinct_paths >= 40 AND client_errors >= 30
```
**Traffic-based — 5xx amplification (expensive-endpoint abuse or attack onset):**
```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,
countIf(response_status_code LIKE '5%') AS server_errors,
count() AS total
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 10 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 10 MINUTE)) - 1800
AND kind = 2
GROUP BY ip
HAVING ip != '' AND server_errors >= 50
```
**Traffic-based — deprecated / shadow / non-prod path access (API9 inventory drift):**
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
count() AS hits,
uniqExact(attributes_string['http.target']) AS paths
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 30 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 30 MINUTE)) - 1800
AND kind = 2
AND (attributes_string['http.target'] LIKE '/v1/%' OR attributes_string['http.target'] LIKE '%/internal%' OR attributes_string['http.target'] LIKE '%/debug%')
GROUP BY ip
HAVING ip != '' AND hits >= 1
```
**Events-based — app rate-limit ceiling repeatedly hit (API4; query the logs table):**
```sql
SELECT
attributes_string['http.client_ip'] AS ip,
attributes_string['route'] AS route,
count() AS rejections
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] = 'api.ratelimit.exceeded'
AND timestamp >= now() - INTERVAL 15 MINUTE
GROUP BY ip, route
HAVING ip != '' AND rejections >= 50
```
**Events-based — SSRF attempt blocked (API7) — any hit is high-signal:**
```sql
SELECT
attributes_string['http.client_ip'] AS ip,
attributes_string['target_host'] AS target_host,
count() AS attempts
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] = 'ssrf.blocked'
AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, target_host
HAVING ip != '' AND attempts >= 1
```
**Events-based — webhook forgery / replay & request replay (catalog N; same logs-table shape):**
```sql
SELECT
attributes_string['http.client_ip'] AS ip,
attributes_string['event.type'] AS signal,
attributes_string['route'] AS route,
count() AS attempts
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] IN ('api.webhook.signature_failed','api.webhook.replay_blocked','api.replay.blocked')
AND timestamp >= now() - INTERVAL 15 MINUTE
GROUP BY ip, signal, route
HAVING ip != '' AND attempts >= 5
```
The other catalog-N events follow the **same shape** — swap the `event.type` filter and the
threshold: `api.schema.rejected` (≥20/5m → fuzzing a contract), `api.stream.limit_exceeded`
(≥1 → stream-cost abuse), `api.websocket.authz_failed` (≥3 → cross-channel probing),
`api.upload.rejected` (≥10/15m → malicious-upload probing), `api.proxy.spoof_detected` (≥1 →
header spoofing), `api.cache.sensitive_blocked` / `api.dependency.vulnerable` (≥1 → notify-only,
route to the runbook, not auto-block).
**Injection (catalog C) — use the SecureNow system signature rules, don't write SQL.** SQLi /
XSS / RCE detection ships as **system signature rules** with synchronous **`instant.block`**
(blocks a matching request in ~2.6s on ingest). Confirm they're present and enabled for this
app via `securenow alerts rules --json` (look for the signature/exploit rules); enable
`instant.block` rather than authoring duplicate pattern SQL.
Useful attributes/columns: `event.type`, `http.client_ip`, `http.target`,
`response_status_code`, `kind`, `client.asn`, `client.as_org`, and your API attributes
(`route`, `reason`, `target_host`, `upstream`, `flow`, `bytes`).
**Ready-to-copy command unit (required for every detection).** Every detection becomes a
**complete, copyable unit** — never a fragment. For each rule emit, in order: (1) the SQL, (2) a
line saving it to `rules/<name>.sql`, (3) the full `securenow alerts rules create …` command, (4)
the dry-run test. In Markdown each is its own fenced block so it copies cleanly; the exact flags
must match `securenow alerts rules --help` from Phase 0.5; save each rule's SQL to
`rules/<name>.sql` so `--sql @rules/<name>.sql` works; note pre-existing/system rules instead of
duplicating them. Example:
```sql
-- rules/api-flood-single-ip.sql
WITH coalesce(nullIf(attributes_string['http.client_ip'],''), nullIf(attributes_string['net.peer.ip'],'')) AS client_ip
SELECT client_ip AS ip, count() AS requests
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 5 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 5 MINUTE)) - 1800
AND kind = 2
GROUP BY ip HAVING ip != '' AND requests >= 600
```
```bash
securenow alerts rules create \
--name "API: volumetric flood (single IP)" \
--sql @rules/api-flood-single-ip.sql \
--apps <APP_KEY> \
--severity high \
--schedule "*/5 * * * *" \
--nlp "single IP making 600+ requests in 5 minutes"
securenow alerts rules test <RULE_ID> --mode dry_run --wait # validate before it runs live
```
### 4c. Mitigation commands — the full SecureNow toolbox (select per threat)
For API abuse, SecureNow **contains the actor at the edge**; the **app/config fix** removes the
underlying weakness. Always pair them on config/SSRF/inventory rows. Once a threat is confirmed,
**choose the narrowest effective mitigation(s) from ALL of these** and combine them (e.g. rate-limit
a route + block the worst IPs + challenge a NAT egress). 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. |
| 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, catalog C). Don't duplicate pattern SQL. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "..."` | confirmed-malicious source, all routes. |
| 4 | **IP block — scoped to route (+ method)** | `securenow blocklist add <ip> --route /v1* --mode prefix --method ALL --app <APP_KEY> --env production --reason "..."` (`--mode exact\|prefix\|regex`, `--method GET\|POST\|…\|ALL`) | block an IP only on sensitive/deprecated paths; 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 /api/search --mode prefix --method GET --limit 60 --window 1m --key-by ip` | cap an expensive/abusable endpoint (search/export/report) for everyone, budgeted per IP. |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /api/checkout --mode exact --method POST --limit 5 --window 1m --duration 24h` · NL `securenow ratelimit from-text "rate limit /api/checkout to 5/min for 24h" --yes` · test `securenow ratelimit test <ip> --path /api/checkout --method POST` | precise throttle of one client on one route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /api/search --difficulty 16 --clearance 30m` (route-wide) **or** `securenow challenge add <ip> --route /api/search --difficulty 18 --clearance 30m` · test `securenow challenge test <ip> --path /api/search --method GET` | bot scraping / business-flow abuse from **shared / NAT / CGNAT** egress — a human passes once, a script can't. Prefer over a hard block when real users share the IP. |
| 10 | **Auto-block (risk-scored)** | `securenow automation defaults --yes` (≥95→7d, 90–94→72h, 85–89→24h) · custom `securenow automation create --conditions '[...]' --actions '[...]'` · preview `securenow automation dry-run <id>` | hands-off blocking by risk score; actions include block / rate_limit / requireCaptcha. |
| 11 | **Session revocation** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | session theft / account takeover — kill the stolen session, not the IP. |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "Office VPN / partner / monitor"` | stop false positives from known-good infra (internal scanners, partner batch jobs) — 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 rule quiet without weakening it. |
| 15 | **App / config / code fix (primary for root cause)** | *described in the Code-Findings report, never auto-applied* | the actual fix: body-size cap, pagination ceiling, SSRF allowlist, CORS lockdown, security headers, disable debug endpoints, retire deprecated routes, validate upstream responses, idempotency, IAM. SecureNow contains; the fix removes. |
**Choosing per threat** — by **confidence**: exploit-signature/exact IoC → instant-block or block;
probable bot on shared egress → **challenge**; noisy/legit-mixed traffic (floods, scrape,
enumeration counts) → **rate-limit (test-mode first)**; session compromise → **revoke**; known-good
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. Always pair an edge mitigation with the **app/config fix** (Code-Findings report) when
SecureNow can only contain the actor — primary for API7/API8/API9/API10.
### 4d. Testing every detection and mitigation
Only test against apps/environments the user owns; prefer `--env local`/staging. For synthetic
source IPs use TEST-NET ranges (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`).
```bash
# Synthetic API events — exercise an events-based rule end to end (rate-limit ceiling):
for i in $(seq 1 60); do
securenow event send api.ratelimit.exceeded --ip 203.0.113.50 \
--attrs route=/api/search,limit=100,window=1m,test=true
done
# Synthetic SSRF block:
securenow event send ssrf.blocked --ip 203.0.113.50 \
--attrs route=/api/preview,target_host=169.254.169.254,reason=link_local,test=true
# Synthetic webhook forgery / replay (catalog N) — exercise the webhook-abuse rule:
for i in $(seq 1 6); do
securenow event send api.webhook.signature_failed --ip 203.0.113.51 \
--attrs route=/16-webhooks/stripe,provider=stripe,reason=invalid,test=true
done
securenow event send api.replay.blocked --ip 203.0.113.51 \
--attrs route=/api/checkout,reason=duplicate_idempotency_key,flow=checkout,test=true
# Validate a rule query without waiting for the schedule:
securenow alerts rules test <RULE_ID> --mode dry_run --wait
# Traffic-based rules (flood / fuzzing / deprecated paths) — generate spans, then check pipeline:
securenow test-span "threat-model.api.smoke"
securenow forensics "requests and 4xx/5xx by IP in the last hour" --env production
# Injection signatures — confirm a payload triggers the system rule + instant block (staging):
# send a request carrying a benign-but-matching marker (e.g. ?q=' OR '1'='1) to a staging URL,
# then verify the block fired:
securenow firewall test-ip 203.0.113.50 --app <APP_KEY> --env production
# Mitigation verification:
securenow ratelimit test 203.0.113.50 --path /api/search --method GET
securenow challenge test 203.0.113.50 --path /api/search --method GET
# Confirm + clean up:
securenow notifications list --limit 10
securenow blocklist list # then: securenow blocklist unblock <id> --reason "threat-model test"
securenow challenge list # then: securenow challenge remove <id>
```
Every 🟢/🟡 threat row in the report must have a concrete test recipe (commands + expected
outcome: which rule fires, which notification appears, what the mitigation does).
---
## Phase 5 — Write the two tracks (FOUR files)
Write **four** files into `threat/14-api-security/` — two tracks, each as Markdown **and** a
self-contained HTML page. The **Detection & Mitigation** track is the operational runbook (what
to run in SecureNow). The **Code Findings & Recommendations** track is the code audit (issues in
the codebase + recommended fixes, described, never applied). The two tracks **cross-link** each
other: the detection report's gap/instrumentation rows link to the relevant code finding, and
each code finding links back to the detection row it backs. `<slug>` = `api-security`.
1. `api-security-detection-mitigation.md` — Track A, Markdown (sections 3a below).
2. `api-security-detection-mitigation.html` — Track A, self-contained HTML (skeleton below).
3. `api-security-code-findings.md` — Track B, Markdown (sections 3b below).
4. `api-security-code-findings.html` — Track B, self-contained HTML (skeleton below).
---
### 5a. Detection & Mitigation report — sections (both .md and .html), in order
1. **Executive summary** — stats line (N threats modeled · N covered · N partial · N gaps ·
N rules to create · N mitigations), top 3 **detectable** API risks for this stack, installed
`securenow` version (from `node_modules/securenow`, Phase 0.5) + app key + firewall state,
and a one-line OWASP API Top 10:2023 coverage note (which API1–10 are owned here vs deferred).
2. **SDK & environment** — installed SDK version (from `node_modules/securenow`), app key(s),
environment, firewall state, existing rules / automations / challenge rules (from Phase 0),
and the **system signature rules** present (SQLi/XSS/RCE backbone).
3. **Threat → Detection → Mitigation matrix** — one row per modeled threat:
`# | Threat | OWASP/CWE | Coverage 🟢/🟡/🔴 | Detection rule | Signal (threshold+window) | Schedule | Sev | Mode | Mitigation`.
Severity ∈ {critical, high, medium, low}. Each row's **Mitigation** cell must name
**specific, scoped** mitigation(s) chosen from the Phase 4c toolbox (e.g. "rate-limit `/api/search`
60/1m per-IP + challenge NAT egress + app: pagination cap", not a generic "block the IP"). The
**Mode** cell tags the rule `test-first` or `prod-ready` (per the Phase 4b rule of thumb): any
FP-prone heuristic/threshold/anomaly rule is `test-first`; only high-precision exploit-signature /
exact-IoC rules are `prod-ready`. Then the "Out of scope" N/A list and the deferred
API1/2/3/5 rows pointing to the authn/authz models. Gap / instrumentation-needed rows
**cross-link** to the matching code finding in the Code Findings report.
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`** (Phase 4b rule of thumb), and for every
`test-first` rule include the promotion step `securenow alerts rules update <RULE_ID> --mode test`
→ observe 3–7 days → `securenow alerts rules update <RULE_ID> --mode prod`. Injection-class rows
reference the **system signature rules + `instant.block`**, not duplicate SQL. Note rules that
already exist (from Phase 0) instead of duplicating them.
5. **Instrumentation the detections need** — only the `track('…')` events the rules above consume
(the `api.*` / `ssrf.blocked` taxonomy), each as a copyable snippet; point to the Code Findings
report for *where* (file:line) to add each one.
6. **Mitigation mechanisms** — render the **full Phase 4c toolbox table** verbatim (all 15 rows:
free firewall · exploit-signature `instant.block` · IP block [global / route+method / temporary] ·
rate-limit [per-IP / per-route / per-route+IP] · challenge · auto-block · session revocation ·
trusted · allowlist · fp · **app/config fix**) plus the "Choosing per threat" guidance + per-threat
**ready-to-copy**, scoped mitigation command + reversibility. Make explicit that the app/config fix
is primary for API7/8/9/10 and the SecureNow control is containment; all commands are verified
against the installed SDK in Phase 0.5.
7. **Action plan (copy-paste, ordered)** — ① engage the firewall + enable signature
instant-block, ② add the API event instrumentation where traffic is blind, ③ create rules
— **create every FP-prone (`test-first`) rule with `--mode test`** and only `prod-ready`
high-precision rules armed, ④ enable automations / challenge rules, ⑤ test, ⑥ verify in
dashboard, ⑦ **promote: after 3–7 days of clean observation, run
`securenow alerts rules update <RULE_ID> --mode prod` on each `test-first` rule** (tune the
threshold / add `fp` exclusions first), ⑧ schedule the app/config-fix work (from the Code
Findings report). Real commands only, `<APP_KEY>` already substituted.
8. **Testing & validation** — per-rule recipe (Phase 4d): `securenow event send …` / `test-span`
/ dry-run + expected outcome + cleanup (TEST-NET IPs `192.0.2.0/24` / `198.51.100.0/24` /
`203.0.113.0/24`).
9. **Response runbooks** — for each notification type: what fired, how to confirm a true
positive, the exact command to respond (rate-limit / challenge / block / signature) and the
exact command to reverse — each as a copyable block.
10. **Known gaps & SecureNow feature requests** — every 🔴 threat: why it's not coverable today,
interim app/config fix (link to the Code Findings report), and the "contact the SecureNow
team" line.
11. **Appendix** — resolved SDK/CLI version (Phase 0.5), app key, environment, firewall state,
rule IDs created, date, link to the Code Findings report.
---
### 5b. Code Findings & Recommendations report — sections (both .md and .html), in order
State at the very top: *"Findings only — no application code was modified."*
1. **Executive summary** — findings by severity (critical / high / medium / low), top 3 **code**
risks for this stack, one-paragraph posture verdict.
2. **API surface & inventory** — the full Phase 1 inventory: endpoint catalog
(method/path/visibility/version), controls table, SSRF-sink + upstream list, config posture,
contract/schema posture, webhook inventory, replay/race/idempotency map, proxy/host-trust
posture, streaming (WS/SSE) inventory, cache/CDN behavior, upload/media posture, dependency
posture, telemetry redaction status.
3. **Threat catalog** — the exhaustive Phase 2 catalog (A1–N99, grouped), each tagged
OWASP API Top 10:2023 / CWE, marked modeled or explicit N/A — never silently dropped. Include
the deferred API1/2/3/5 references to the authn/authz models.
4. **Code-level findings (audit — not applied)** — 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). Each finding **cross-links** to
the detection-report row it backs.
5. **Strengths** — controls already present and correct (size caps, SSRF allowlist, introspection
disabled in prod, …) — the posture must be honest.
6. **App / config fixes (primary remediation)** — the config/code changes that remove the root
cause (described, not applied): body-size cap, pagination ceiling, SSRF allowlist, CORS
lockdown, security headers, disable debug endpoints, retire deprecated routes, validate
upstream responses — each linked to the detection-report row it backs.
7. **Instrumentation recommendations** — the `track('api.*')` / `ssrf.blocked` calls to add and
the exact `file:line` to add them, so the detection rules in Track A light up.
8. **Appendix** — files reviewed, resolved SDK version (Phase 0.5), date, link to the
Detection & Mitigation report.
---
### 5c. Self-contained HTML skeletons (offline; inline CSS + copy JS; no network)
Both HTML files share this `<head>` (SecureNow brand tokens + copy-button styles) and the copy
`<script>` at the end of `<body>`. Change only the `<title>`, the sidebar subtitle, the header,
the stats cards, and the section content per track. **Wrap EVERY command/SQL block as a `.cmd`**
(so it gets a Copy button). The Detection & Mitigation HTML uses the title/subtitle "Detection &
Mitigation — API Security"; the Code Findings HTML uses "Code Findings — API Security". Use this
skeleton and these exact brand tokens (extend content, do not restyle):
```html
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title><!-- "Detection & Mitigation — API Security — SecureNow" OR "Code Findings — API Security — 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"><!-- "Detection & Mitigation · API Security" OR "Code Findings · API Security" --></div>
<!-- one <a href="#…"> per section of THIS track (5a or 5b) -->
</nav>
<main>
<header class="top"><h1><!-- report title for this track --></h1>
<p><code><!-- app name / domain --></code> · <span class="pill">securenow <!-- installed version --></span></p></header>
<div class="stats"><!-- 5 .stat cards; numbers MUST equal the matrix/finding counts of THIS track --></div>
<!-- <section id="…"> blocks mirroring the Markdown sections of THIS track (5a or 5b) -->
<footer>Generated by the SecureNow API security 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 **Detection & Mitigation** HTML uses the copyable wrapper (the
Code Findings HTML may omit copy buttons on prose, but still wraps any example/fix command in
`.cmd`):
```html
<div class="cmd"><button class="copy" type="button">Copy</button><pre>securenow alerts rules create \
--name "..." --sql @rules/<name>.sql --apps <APP_KEY> --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">API4</span>`, CWE tag → `<span class="cwe">CWE-89</span>`, mitigation type →
`<span class="m firewall|signature|rate|challenge|block|notify|appfix">`, rule IDs →
`<span class="rid">`. Stats numbers must equal the matrix/findings row counts for that track.
---
## Quality bar (the report is rejected if any of these fail)
- **Phase 0.5 ran**: the resolved installed `securenow` version (from `node_modules/securenow`)
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 → save to `rules/<name>.sql` → full
`securenow alerts rules create …` → dry-run test); flags match `securenow alerts rules --help`.
- **Four** files are written to `threat/14-api-security/` (`api-security-detection-mitigation.md`
+ `.html`, `api-security-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 detection
HTML has a working Copy button**.
- The split is **honest**: SecureNow-runnable detections/mitigations live in the Detection &
Mitigation report; code/config changes live in the Code Findings report; nothing
security-relevant is dropped.
- Every catalog item A1–N99 is either a matrix row or an explicit N/A line; each modeled row
carries its OWASP API Top 10:2023 tag (or "—").
- The advanced threats (N71–N99 — contract/schema, webhook, replay/race/idempotency,
proxy/host trust, WebSocket/SSE/streaming, cache, upload/media, dependency, telemetry
redaction) are each modeled or explicit N/A, with the matching `api.*` event where the
detection needs instrumentation.
- API1/API2/API3/API5 are **deferred** to the authn/authz models (rows present, linked, not
re-derived) — this model does not duplicate them.
- Every matrix row has a concrete signal (threshold + window), severity, and mitigation — no
"monitor for suspicious activity" filler.
- The Detection report's mitigation section presents the **full toolbox** (Phase 4c / §6: firewall ·
instant-block · block [global / route / method / temporary] · rate-limit [IP / route / IP+route] ·
challenge · auto-block · revoke · trusted · allowlist · fp · app-fix), and **each modeled threat's
matrix row selects specific, scoped mitigation(s) from it** — never a generic "block the IP."
- **Every false-positive-prone rule is tagged `test-first`** and carries the `--mode test` → observe
(3–7 days) → `--mode prod` promotion workflow; only high-precision rules are `prod-ready`. The
action plan creates the test-first rules in `--mode test` and has an explicit "promote after N
days" step.
- Every code finding in section 4 has a `file:line`, the quoted snippet, and a described fix —
and **no application code was modified** (this is an audit).
- Every detection SQL keeps `__USER_APP_KEYS__` scoping (correct table column) and selects an
`ip` column; traffic queries keep the `ts_bucket_start` + `kind = 2` guards.
- Injection coverage references the **system signature rules + `instant.block`**, not duplicate
pattern SQL.
- Only commands, flags, events, and SQL columns from this prompt's building blocks appear
(including `securenow challenge …`, `firewall`, and the `api.*` / `ssrf.blocked` events).
- Detection vs. fix is honest: where SecureNow can only contain the actor at the edge
(API7/8/9/10), the row pairs the control with the **app/config fix**.
- Every 🔴 gap appears in the gaps section with an interim app/config fix **and** the "contact
the SecureNow team" line.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted in.
- Both HTML files are self-contained (no CDN, no fonts, no external scripts) and the stats cards
match the table/finding counts for their track.
- All **four** files (two tracks: detection-mitigation .md+.html, code-findings .md+.html) are
written to `threat/14-api-security/`, the two tracks cross-link, and a one-line summary is
printed back: per-track file paths, threat counts, rules-to-create count, code findings by
severity, gaps, OWASP coverage, and the resolved SDK version.
<!-- ════════════════ END OF PROMPT ════════════════ -->