#21A02 · A05 · A08
DNS / TLS / Certificates
Subdomain takeover, certificate posture, and transport trust.
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/21-dns-tls-certificates/— opendns-tls-certificates-code-findings.html(the audit) anddns-tls-certificates-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
# DNS / TLS / Certificates 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 **naming & transport-trust surface**
(domains, DNS records, registrar settings, TLS/certificate posture, email-auth DNS), build an
exhaustive **DNS / TLS / certificates** threat model mapped to the **OWASP Top 10:2021**
(**A02 Cryptographic Failures**, **A05 Security Misconfiguration**, **A08 Software & Data
Integrity Failures**), audit the DNS/registrar/TLS configuration for flaws, and emit a
**two-track set of SecureNow-branded reports** (each in **Markdown + self-contained HTML with
offline copy buttons**) — a **Detection & Mitigation** runbook covering the few Host-header
traffic rules SecureNow *can* run plus the containment commands, and a **Code Findings &
Recommendations** report that (for this audit-led domain) carries most of the content: the
config-level DNS / registrar / TLS findings (audited, **not** fixed) and the recommended fixes.
Every rule and command is **grounded in the SDK actually installed in the repo** and emitted as a
**ready-to-copy block** you can paste straight into a terminal.
> **Read this first — coverage is deliberately LOW and that is correct.** Unlike the API,
> authentication, or rate-limit models (where SecureNow lives in the request path and detection
> is its home turf), DNS / TLS / certificate posture is mostly **infrastructure config that emits
> no application telemetry**. A dangling CNAME, a missing CAA record, an expired cert, a weak
> cipher suite, an absent DMARC policy — **none of these generate a span or an event SecureNow
> can see**. This model is therefore primarily an **audit + findings** model: most rows are 🔴/🟡
> with a **DNS / registrar / TLS-config fix as the primary remediation**, not a SecureNow rule.
> SecureNow's genuine contribution is narrow but real: it can **detect HTTP traffic arriving at
> unexpected or takeover-prone Host headers** (a takeover that is being *used* leaves traffic),
> and it can **contain malicious source IPs** (firewall / rate-limit / challenge / block) once an
> attack rides over a name you control. Be honest about this throughout — never dress a config
> audit up as a detection rule.
This is a **platform / cross-cutting** model: the trust anchors *below* the app. It owns name
ownership and transport-trust integrity, and it **defers** to siblings rather than re-deriving
them:
- **Forgotten hosts / subdomains / retired services** → [legacy-endpoints model](../13-legacy-endpoints/legacy-endpoints-threat-model-prompt.md) (the *inventory* of what is stale; this model covers the *DNS record* that dangles).
- **Where DNS points (cloud resources, load balancers, buckets, CDNs, deprovisioned IPs)** → [cloud-infrastructure model](../19-cloud-infrastructure/cloud-infrastructure-threat-model-prompt.md).
- **SPF / DKIM / DMARC abuse impact (phishing, spoofed transactional mail)** → [messaging-notifications model](../11-messaging-notifications/messaging-notifications-threat-model-prompt.md); this model audits the *DNS records themselves*, that model owns the send-path abuse.
- **Private-key custody, secret storage, CA/ACME credentials, registrar account creds** → [secrets-and-cloud-iam model](../20-secrets-and-cloud-iam/secrets-and-cloud-iam-threat-model-prompt.md).
Requirements on the customer machine: `npm i -g securenow && securenow login` (admin auth + app
runtime connected). Everything else — DNS records, registrar settings, certificate chains, IaC —
is discovered by the agent from the codebase, public DNS, and the live endpoints.
---
<!-- ════════════════ COPY EVERYTHING BELOW THIS LINE ════════════════ -->
# Generate a DNS / TLS / Certificates Threat Model Report (SecureNow)
You are a senior application-security engineer specializing in **DNS, TLS, and PKI / certificate
management**. Produce an **exhaustive DNS / TLS / certificates threat model for THIS
project's domains and transport posture**, organized along **OWASP Top 10:2021** (**A02
Cryptographic Failures**, **A05 Security Misconfiguration**, **A08 Software & Data Integrity
Failures**), mapped to **SecureNow** detections and mitigations where they genuinely apply, with
a ready-to-run action plan **and** a config-level audit of every DNS / registrar / TLS flaw you
find. You write **four deliverables** (two tracks) into `threat/21-dns-tls-certificates/` (create
the folder if needed):
1. `dns-tls-certificates-detection-mitigation.md` — the **operational runbook**: what to run in
SecureNow (the Host-header traffic detection rules, mitigation/containment commands, tests).
2. `dns-tls-certificates-detection-mitigation.html` — the same runbook as a **self-contained**
HTML page (inline CSS + JS, no network) with a **Copy button** on every command/SQL block.
3. `dns-tls-certificates-code-findings.md` — the **code/config audit**: the DNS / registrar / TLS
config findings + recommended fixes. **For this audit-led domain this report carries most of
the content** — the detection report stays small (it can only act on Host-header traffic),
while almost every real fix is a DNS / registrar / TLS configuration change documented here.
4. `dns-tls-certificates-code-findings.html` — the same audit as a **self-contained** HTML page.
The two tracks **cross-link** each other: the gaps/instrumentation rows in the detection report
link to the relevant config finding, and each config finding links back to the detection row (if
any) that backs it. Every rule, flag, event name, and SQL column is **grounded in the installed
SecureNow SDK** (Phase 0.5) and emitted as a **ready-to-copy block** (Phase 4). Be honest about
the split: SecureNow-runnable detections/mitigations live in the Detection report; the
DNS/registrar/TLS-config fixes — the bulk of this domain — live in the Code Findings report.
Work in the phases below, in order. **Never invent facts**: if a DNS record, registrar
setting, certificate, or CLI result is not observable, say "not found" / "not resolvable" — do
not guess. **Do not modify any DNS records, registrar settings, TLS configuration, IaC, or
application code.** You are auditing: every fix is *described in the report*, never applied.
**Coverage honesty (non-negotiable).** This domain has **LOW native SecureNow coverage**. Most
threats here are **config-audit findings** whose fix is a DNS / registrar / TLS change, not a
SecureNow rule. SecureNow adds value in exactly two narrow places: (1) **detecting HTTP traffic
to an unexpected or takeover-prone Host header / hostname** in the traces it already captures,
and (2) **containing a malicious source IP** once an attack rides over a name you control. Tag
those — and only those — as 🟢/🟡. Everything else is 🔴 with a config fix and the "contact the
SecureNow team" line. **Do not manufacture detection rules for posture that emits no telemetry.**
**Scope discipline.** This model owns: subdomain takeover, dangling/stale DNS, domain &
registrar hijack, DNS-provider compromise / DNSSEC, CAA & rogue issuance, TLS protocol/cipher/
cert posture, cert expiry & renewal, key custody (audit-only — defer custody mechanics),
mixed-content / downgrade / stripping, email-auth DNS records (SPF/DKIM/DMARC — audit-only),
and CT-log recon. For the four sibling domains above, do **not** re-derive their model — add a
**"Deferred to sibling models"** subsection, link the report, and note only the DNS/TLS *slice*
this model owns.
---
## 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, configured hostnames)
```
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** (it is the only
network-layer containment available for traffic that rides over your names), and record the
**service name / configured hostnames** from `securenow env` — they are the canonical list of
names you *expect* traffic on, which Phase 4b compares live Host headers against.
> Most of this model needs **no telemetry to be flowing** — the audit (Phases 1 & 3) runs from
> DNS, the registrar, and the live TLS endpoints regardless. SecureNow tooling only powers the
> two narrow traffic detections in Phase 4 and the containment commands.
---
## 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 naming & transport surface
DNS/TLS security starts with knowing **every name you own, where each one points, and how each
one terminates TLS**. Document what is **actually resolvable and live**, not what is intended.
Discover from: IaC (Terraform/CloudFormation/Pulumi/CDK `route53`/`dns`/`acm`/`cert-manager`/
`ingress` resources), app config & env (`*_URL`, `*_HOST`, `NEXTAUTH_URL`, `ALLOWED_HOSTS`,
CORS origins, redirect URIs, webhook URLs, `securenow env`), CI/CD, and live resolution /
connection of every name you find. Cover at minimum:
- **Apex & subdomain inventory** — enumerate every hostname the project references or owns:
apex (`example.com`), `www`, `api`, `app`, `auth`, `cdn`, `assets`, `mail`, `staging`, `dev`,
`preview`, marketing/landing hosts, custom-domain / per-tenant / vanity domains, regional
hosts. For each: **resolution chain** (CNAME → CNAME → A/AAAA), final IP(s), and **what owns
the target** (your cloud account? a third-party SaaS like Heroku/Netlify/Vercel/GitHub Pages/
S3/CloudFront/Azure/Fastly/Zendesk/Shopify? a now-deprovisioned resource?). This map is a
report deliverable.
- **Dangling / takeover-prone records** — for every CNAME/A/AAAA, determine whether the target
**still exists and is claimed by you**. Flag any record pointing at: a deleted cloud resource,
an unclaimed SaaS hostname (the classic takeover fingerprints — "NoSuchBucket", "There isn't a
GitHub Pages site here", default Heroku/Netlify/Vercel 404, unclaimed Fastly/Azure CNAME), a
released/reclaimable elastic IP, or an NS delegation to a zone you no longer control. (A02/A05.)
- **Registrar & domain posture** — for each registrable domain: registrar, **registrar lock**
(clientTransferProhibited / clientUpdateProhibited / clientDeleteProhibited present?),
**registration expiry date**, auto-renew status, WHOIS/RDAP privacy, and whether registrant
contact / DNS-provider account is on a shared/personal email. (A05/A08.)
- **DNS provider & DNSSEC** — which DNS provider hosts each zone; is **DNSSEC** enabled (DS
record at parent + signed zone)? Who can change records (account MFA, scoped API tokens vs
god-mode keys)? Are zone-edit credentials in code/CI (defer custody to the secrets model, but
*flag the existence here*). (A05/A08.)
- **CAA records** — does each apex/relevant zone publish **CAA** (`issue` / `issuewild` /
`iodef`)? Which CAs are authorized? A **missing CAA record means any public CA may issue a cert
for the domain** → rogue-issuance exposure. (A05/A08.)
- **TLS protocol & cipher posture** — for every live HTTPS endpoint: enabled protocol versions
(TLS 1.0/1.1 still on? TLS 1.2/1.3 only?), cipher suites (RC4, 3DES, CBC, export, NULL,
anonymous, no-PFS suites present?), key exchange / forward secrecy, OCSP stapling, session
resumption. (A02.)
- **Certificate posture** — for every cert: subject CN + SANs, issuer (public CA? self-signed?
internal CA?), **expiry date & days remaining**, key type & **size** (RSA <2048, weak/legacy
EC?), signature algorithm (SHA-1 still?), chain completeness, **hostname match** (does the cert
cover the name it's served on?), and **wildcard scope** (`*.example.com` covering far more than
needed). (A02.)
- **Renewal & monitoring** — how certs are obtained & renewed (ACME/Let's Encrypt automation,
`cert-manager`, ACM-managed, manual upload), whether **auto-renew** is configured and proven
working, and whether **expiry is monitored/alerted** (a cert with no monitoring is an outage
waiting to happen). (A05/A08.)
- **Key custody (audit-only)** — where private keys live (in repo?, in a secret manager?, on
disk with what perms?), key **reuse** across services/hosts, and weak/legacy key sizes. *Flag
existence and reuse here; defer custody mechanics* to [secrets-and-cloud-iam](../20-secrets-and-cloud-iam/secrets-and-cloud-iam-threat-model-prompt.md). (A02.)
- **HTTP→HTTPS & HSTS** — is plain HTTP served at all? Does it 301→HTTPS or serve content? Is
**HSTS** present (`Strict-Transport-Security`), with `includeSubDomains`, `preload`, and a
sane `max-age`? Is the domain on the **HSTS preload list**? Any **mixed content** (HTTPS pages
loading `http://` sub-resources)? Any path that downgrades. (A02/A05.)
- **Host / virtual-host trust** — which Host headers the origin actually accepts (does it serve
for arbitrary/unknown Hosts, or only an allowlist?), default-vhost behaviour, and whether
unexpected Hosts reaching the origin would be served. This is the **one place SecureNow can
detect** — record the **expected-Host allowlist** for the Phase 4b rule. (A05.)
- **Email-auth DNS (audit-only)** — presence & correctness of **MX**, **SPF** (`v=spf1`, `-all`
vs `~all`, `+all`/overly-broad includes, >10 lookups), **DKIM** (selector keys published, key
size), and **DMARC** (`p=none` vs `quarantine`/`reject`, `rua`/`ruf`). Gaps enable transactional-
mail spoofing. *Audit the records; defer send-path abuse* to [messaging-notifications](../11-messaging-notifications/messaging-notifications-threat-model-prompt.md). (A05/A08.)
- **Certificate-transparency exposure** — what hostnames are discoverable in **CT logs**
(crt.sh-style) for your domains — do **internal / staging / admin / pre-release** hostnames
leak there, handing an attacker a recon map? (A05 — information disclosure.)
- **SecureNow instrumentation already present** — whether the app runs under `securenow run` /
`securenow/register` / `securenow init` (so HTTP traces incl. the **Host header** are captured),
any existing `securenow/events` `track()` calls, and the firewall state. This determines what
the two traffic detections can see today.
Output of this phase = the report's **Naming & transport surface** section: the **hostname →
resolution-chain → target-owner** map, the **dangling-record candidates** list, the **registrar
posture** table (lock/expiry/auto-renew per domain), the **DNS/DNSSEC** posture, the **CAA**
posture, the **TLS protocol/cipher** table, the **certificate inventory** (CN/SAN/issuer/expiry/
key/wildcard per endpoint), the **renewal/monitoring** status, the **HTTP/HSTS/mixed-content**
posture, the **expected-Host allowlist**, the **email-auth DNS** records, the **CT-log exposure**
notes, and a short paragraph naming the real naming/transport attack surface for this project.
---
## 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 N/A — no GraphQL", "Wildcard-cert items: N/A, only single-SAN
certs in use"). Never silently drop an item. Add project-specific threats you discover that are
not listed — this catalog is the floor, not the ceiling. Tag each modeled row with its **OWASP
2021** code (**A02**, **A05**, **A08**, or a combination).
**A. Subdomain takeover (dangling pointers to deprovisioned resources)**
1. Dangling **CNAME** to a deleted cloud resource / unclaimed SaaS hostname (S3, CloudFront, Heroku, Netlify, Vercel, GitHub Pages, Azure, Fastly, Zendesk, Shopify, Surge, etc.) — attacker claims the target and serves content on your subdomain (A05/A08)
2. Dangling **A/AAAA** to a released, reclaimable cloud IP (elastic IP / floating IP back in the pool) (A05/A08)
3. **NS-record takeover** — delegation to a nameserver/zone you no longer control (full-zone hijack) (A05/A08)
4. **MX-record takeover** — dangling MX to a deprovisioned mail target (mail interception) (A05/A08)
5. Takeover-in-use detected via **HTTP traffic to a subdomain you didn't deploy** (the takeover is being served) (A05)
**B. Stale / dangling DNS hygiene**
6. Stale records resolving to **third-party services you no longer use** (former CDN, former analytics, former status page) (A05)
7. Orphaned `TXT` / verification records left after a SaaS was decommissioned (residual trust / re-verification by an attacker) (A05/A08)
8. Wildcard `*.example.com` CNAME/A masking individual dangling names (broad surface, hard to audit) (A05)
9. Internal-only hostnames published in **public** DNS (split-horizon leakage) (A05)
**C. Domain & registrar hijack**
10. **Missing registrar lock** (no clientTransferProhibited) → unauthorized transfer-out (A05/A08)
11. **Domain registration expiring / not auto-renewing** → lapse, snipe, re-registration by attacker (A05/A08)
12. Registrant / DNS-provider account on a **personal/shared email** or without MFA → account takeover → full DNS control (A05/A08)
13. **No DNS provider account MFA / over-broad API tokens** in CI able to rewrite any record (A05/A08)
**D. DNS integrity & DNSSEC**
14. **No DNSSEC** → DNS spoofing / cache poisoning / on-path record forgery feasible (A08)
15. DNSSEC misconfigured (DS at parent absent/stale, expired RRSIG → resolution failures or unsigned-fallback) (A08/A05)
16. Unauthorized record change via provider-account or API-token compromise (no change auditing/alerting) (A08)
**E. CAA & rogue certificate issuance**
17. **Missing CAA record** → **any public CA may issue** a cert for your domain (rogue issuance) (A05/A08)
18. Over-broad CAA (`issuewild` allowing unintended wildcard issuance, stale authorized CAs) (A05)
19. No `iodef` in CAA → no notification channel on a rejected/attempted issuance (A05)
20. No monitoring of **CT logs for unexpected certs** issued for your domains (rogue-cert blind spot) (A08)
**F. TLS protocol & cipher posture**
21. **Weak protocols** enabled (TLS 1.0 / 1.1 / SSLv3) — downgrade & known attacks (A02)
22. **Weak / insecure cipher suites** (RC4, 3DES, export, NULL, anonymous, CBC-only, no-PFS) (A02)
23. **No forward secrecy** (static-RSA key exchange) — retrospective decryption on key compromise (A02)
24. Missing OCSP stapling / broken revocation checking (A02/A05)
**G. Certificate validity & trust**
25. **Expired certificate** served (hard outage / trust error) (A02/A05)
26. **Self-signed or untrusted-CA** certificate on a public endpoint (A02)
27. **Hostname mismatch** — cert CN/SAN doesn't cover the name it's served on (A02)
28. **Weak key size / signature** — RSA <2048, SHA-1 signature, deprecated curve (A02)
29. Incomplete / mis-ordered chain (intermediates missing) → client trust failures (A05)
**H. Certificate lifecycle & exposure**
30. **Cert expiry causing outage** — no monitoring, no alerting, manual renewal forgotten (A05/A08)
31. **No auto-renew** / ACME automation broken (silent drift toward expiry) (A05/A08)
32. **Wildcard-cert over-exposure** — one `*.example.com` cert + key shared across many hosts; single compromise = all hosts (A02)
33. **Private-key exposure** — key committed to repo, in a build artifact, world-readable on disk, or in logs (A02) *(custody mechanics deferred — see §L)*
34. **Key reuse** — same private key across multiple services/domains/environments (A02)
**I. Transport downgrade & mixed content**
35. **Plain HTTP served** (no redirect, or content before redirect) → interception / stripping (A02/A05)
36. **No HSTS** (or weak `max-age`, no `includeSubDomains`, not on preload list) → SSL-strip / first-visit downgrade (A02/A05)
37. **Mixed content** — HTTPS pages loading `http://` scripts/styles/images (A02/A05)
38. **TLS stripping / downgrade** on a redirect or sub-resource path (A02)
**J. Email-authentication DNS (audit-only — abuse impact deferred to messaging model)**
39. **Missing/permissive SPF** (`+all`, `~all` where `-all` expected, >10 lookups, overly-broad include) → spoofing (A05/A08)
40. **Missing/weak DKIM** (no selector keys published, short key, unrotated) (A05/A08)
41. **Missing/weak DMARC** (`p=none`, no `rua`/`ruf`, no subdomain policy) → no enforcement against spoofed mail (A05/A08)
42. **Missing/wrong MX** or open mail-routing trust enabling delivery to attacker (A05)
**K. Reconnaissance & negative-space / evasion**
43. **Internal/staging/admin hostnames leaking via CT logs** (free recon map for an attacker) (A05)
44. Verbose DNS (AXFR zone transfer allowed, overly-detailed `TXT`/`HINFO`) → enumeration (A05)
45. **Direct-origin / IP access bypassing the CDN/WAF** by connecting to the origin IP or an unexpected Host (evades edge controls) (A05)
46. **Host-header spoofing to an unexpected Host** the origin still serves (cache poisoning / routing confusion / takeover-in-use) (A05)
47. **DNS-rebinding-style** access to internal services via a name you control resolving to private IPs (A05) *(SSRF sink modeling deferred — see API/infra models)*
**L. Deferred — modeled in sibling models (reference, do not re-derive)**
48. **Forgotten hosts / retired services inventory** → [legacy-endpoints model](../13-legacy-endpoints/legacy-endpoints-threat-model-prompt.md). This model owns the *dangling DNS record*; the legacy model owns the *forgotten service* behind it.
49. **Where DNS points (cloud resources, LBs, buckets, CDNs, deprovisioned IPs)** → [cloud-infrastructure model](../19-cloud-infrastructure/cloud-infrastructure-threat-model-prompt.md). This model flags the dangling pointer; the infra model owns the resource lifecycle.
50. **SPF/DKIM/DMARC abuse impact (phishing / spoofed transactional mail send-path)** → [messaging-notifications model](../11-messaging-notifications/messaging-notifications-threat-model-prompt.md). This model audits the *DNS records*; the messaging model owns the *abuse*.
51. **Private-key & ACME/registrar/CA credential custody** → [secrets-and-cloud-iam model](../20-secrets-and-cloud-iam/secrets-and-cloud-iam-threat-model-prompt.md). This model flags *exposure & reuse*; the secrets model owns *storage, rotation, IAM*.
> For 48–51, add **one** matrix row each marked *"deferred — see linked model"*, note only the
> DNS/TLS *slice* this model owns, and do not duplicate the sibling's detection/mitigation.
**M. Observable abuse (the narrow slice SecureNow telemetry can actually catch)**
52. **HTTP traffic to an unexpected / non-allowlisted Host header** — a name you don't deploy is being served (takeover-in-use, direct-origin probing, host-spoof recon) → traffic-detectable (A05)
53. **Requests to takeover-prone subdomains** (the dangling-candidate names from Phase 1) appearing in live traces → a takeover may be live (A05)
54. **4xx/5xx or malicious-payload traffic riding over a name you control** — once an attack rides your name, the **source IP** is containable (firewall / rate-limit / challenge / block) even though the DNS/TLS root cause is a config fix (A05)
---
## Phase 3 — Audit the config (findings only — do not fix)
For **each** modeled threat that maps to a real DNS record, registrar setting, TLS config, IaC
resource, or live endpoint, locate the responsible config and record a **finding** for the
report's "Config-level findings" section. Because this domain is mostly infrastructure, most
findings cite **DNS records, registrar settings, certificate fields, and IaC** rather than
application code — that is expected. A finding is:
- **Location** — the concrete artifact: the **DNS record** (`name TYPE → value`), the **registrar
setting**, the **certificate** (CN/SAN + issuer + expiry), the **IaC resource** (`file:line` —
e.g. `infra/dns.tf:42` `aws_route53_record.api`), the **TLS config** (`nginx.conf:18`
`ssl_protocols`), or the **live endpoint** (host:port). Make it clickable where it is a repo
file; quote the exact record/value otherwise.
- **Pattern** — quote the offending record/config/field. State the missing control precisely
(e.g. "`api.example.com CNAME d111.cloudfront.net` → distribution deleted, target unclaimed →
takeover"; "apex has **no CAA record** → any CA may issue"; "`ssl_protocols TLSv1 TLSv1.1
TLSv1.2;` → TLS 1.0/1.1 still enabled"; "cert for `app.example.com` expires in **6 days**, no
auto-renew configured"; "no `clientTransferProhibited` at registrar"; "`v=spf1 include:… +all`";
"DMARC `p=none`").
- **Why exploitable** — the concrete attack and what it achieves (e.g. "attacker registers the
unclaimed CloudFront alt-domain, serves a phishing/cookie-stealing page on your subdomain";
"any CA can be social-engineered into issuing a valid cert for your domain"; "an on-path
attacker downgrades TLS 1.0 and decrypts"; "the cert lapses → full HTTPS outage on the primary
app domain").
- **Severity** — critical / high / medium / low (impact × reachability). A dangling CNAME on the
apex or a 6-days-to-expiry primary cert is critical; a missing `iodef` in CAA is low.
- **Recommended fix (described, not applied)** — the specific DNS / registrar / TLS change:
e.g. "delete the dangling `api` CNAME (or re-create the CloudFront distribution and reclaim
it)"; "publish `example.com. CAA 0 issue \"letsencrypt.org\"` (+ `issuewild`, + `iodef`)";
"set `ssl_protocols TLSv1.2 TLSv1.3;` and a modern cipher list, enable OCSP stapling";
"enable ACME auto-renew via cert-manager / ACM and add expiry alerting at 30/14/7 days";
"enable registrar lock (clientTransferProhibited/clientUpdateProhibited) and turn on
auto-renew"; "tighten SPF to `-all` and a minimal include set; publish DMARC `p=reject` with
`rua`"; "enable DNSSEC (sign the zone, publish DS at the parent)"; "add HSTS
`max-age=31536000; includeSubDomains; preload` and submit to the preload list". Reference the
secure record/config, not a diff. **You must not edit DNS, the registrar, TLS config, IaC, or
code.**
If a control exists and is correct (CAA present and minimal, DNSSEC signed, TLS 1.2/1.3-only with
PFS, registrar locked, auto-renew proven, DMARC `p=reject`, HSTS preloaded, no dangling records),
note it as a **strength** — the posture must be honest. Absence of a control where the name/
endpoint exists is itself a finding ("no CAA on any zone", "no expiry monitoring on any cert").
For **Phase 1 dangling-record candidates**, each unclaimed/deleted target is a finding (severity
by what the subdomain is used for and whether session cookies are scoped to the parent domain — a
takeover of `*.example.com`-cookie-scoped subdomain is critical). For **CT-log leakage**, list the
internal/staging hostnames discoverable and recommend removing them from public DNS / using a
separate non-public zone. **No artifact is modified — this is an audit.**
---
## Phase 4 — Map every modeled threat to SecureNow detection + mitigation
Classify each threat with exactly one coverage badge. **For this domain the honest distribution
skews 🔴/🟡** — say so in the executive summary.
- 🟢 **COVERED** — detectable + mitigable with SecureNow today. **Only the traffic-observable
slice qualifies**: requests to an unexpected / takeover-prone Host header (catalog M), and
containing the **source IP** of an attack that rides over a name you control. These run on
traces SecureNow already captures — no instrumentation.
- 🟡 **PARTIAL** — SecureNow can *partly* help: it can **detect** abuse traffic on a name you
control or **contain** the abusing source IP at the edge, but **the real fix is a DNS /
registrar / TLS config change** (remove the dangling record, publish CAA, renew/rotate the
cert, enable DNSSEC/HSTS). Pair the SecureNow containment with the config fix on every such row.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this; it emits no traffic and no event
SecureNow can see (missing CAA, weak cipher suite, expired cert, no DNSSEC, no registrar lock,
permissive SPF/DMARC, CT-log leakage). **Still include it**: give the DNS/registrar/TLS-config
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. **Most rows in this model are 🔴 — that is
correct and must not be inflated.**
> **Be brutally honest about detect-vs-fix.** SecureNow sees **traffic** and **events**; it
> contains actors via firewall / rate-limit / challenge / block. It **cannot** see DNS records,
> registrar state, certificate chains, or cipher suites — those are out-of-band config. A dangling
> CNAME that nobody has claimed yet, a missing CAA, an expiring cert, a `p=none` DMARC: all 🔴,
> all fixed in DNS/registrar/TLS. SecureNow's genuine wins are: (a) a **takeover that is being
> used** shows up as **traffic to a Host you didn't deploy** — detectable; (b) any attack that
> **rides over a name you control** exposes a **source IP** — containable. Everything else is a
> config fix this model *audits and reports* but SecureNow does not enforce.
Use **only** the SecureNow building blocks below. Never invent CLI flags, event names, or SQL
columns.
### 4a. Instrumentation (what — little — DNS/TLS detection feeds on)
The honest baseline: **most DNS/TLS posture emits no application telemetry.** A missing CAA, a
weak cipher, an expired cert, an unsigned zone — the app never sees these, so there is **nothing
to instrument** and nothing for a rule to query. Do not pretend otherwise.
The **one** signal traffic already carries is the **Host header**, captured automatically once the
app runs under `securenow run` / `securenow/register` / `securenow init`. That powers the Phase
4b "unexpected Host" detection with **no code changes**.
`securenow/events` `track()` is only useful for **app-internal naming/transport signals the app
itself can observe at runtime** (it never throws). Add these *only where your own code already
makes such a check* — do not add code purely to satisfy this model:
```js
const { track } = require('securenow/events');
// Your app received a request for a Host it does not serve (you have a Host allowlist middleware):
track('dns.host.unexpected', { ip, attributes: { host: 'unknown.example.com', expected: 'app.example.com', reason: 'not_in_allowlist' } });
// Your app/edge detected a plain-HTTP request that had to be upgraded/redirected (downgrade telemetry):
track('tls.downgrade.detected', { ip, attributes: { host: 'app.example.com', scheme: 'http', action: 'redirected' } });
// A cert-/TLS-health check your app runs reported a problem (e.g. an outbound dependency cert near expiry, mismatch, self-signed):
track('tls.cert.problem', { attributes: { host: 'upstream.partner.com', reason: 'expiring|expired|self_signed|hostname_mismatch|weak_chain', days_left: '5' } });
```
> Hash or omit any PII before it becomes an attribute value. Host names are generally not PII, but
> per-tenant vanity domains can be — treat them per the Phase 1 telemetry-redaction posture. These
> events are *optional enrichment*; the model does not depend on them, and most projects will emit
> none of them.
Recommended (optional) DNS/TLS event taxonomy — rules match these **exact strings**:
| Event | Emit when |
|---|---|
| `dns.host.unexpected` | the app/edge receives a request for a Host not in its allowlist |
| `tls.downgrade.detected` | a plain-HTTP request is observed and upgraded/redirected |
| `tls.cert.problem` | an app-run cert/TLS health check flags expiry/mismatch/self-signed/weak chain |
Custom `attributes` become queryable as `attributes_string['<key>']` (e.g.
`attributes_string['host']`). Ingest enriches every IP with **ASN/org** (`client.asn`,
`client.as_org`) — useful for spotting datacenter-origin probing of takeover-prone names.
### 4b. Detection rules — SQL conventions
The detections that work here are **traffic-based on the Host header**. Both query shapes **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`, the Host header (`attributes_string['http.host']` — confirm
with a `--mode dry_run`; OTEL SDK versions vary, it may surface as `attributes_string['http.host']`,
`attributes_string['server.address']`, or `attributes_string['net.host.name']`), and the
`client_ip` coalesce below. Confirm any other column before relying on it.
**Traffic-based — request to an UNEXPECTED Host (takeover-in-use / direct-origin probe / host-spoof recon).**
Replace the allowlist with the **expected-Host list captured in Phase 0/1**:
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip,
coalesce(nullIf(attributes_string['http.host'], ''), nullIf(attributes_string['server.address'], ''), nullIf(attributes_string['net.host.name'], '')) AS req_host
SELECT client_ip AS ip,
req_host AS host,
count() AS hits
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 15 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
AND kind = 2
AND req_host NOT IN ('app.example.com','api.example.com','www.example.com','example.com') -- <-- expected-Host allowlist from Phase 1
GROUP BY ip, host
HAVING ip != '' AND host != '' AND hits >= 1
```
**Traffic-based — request to a TAKEOVER-PRONE subdomain (the dangling candidates from Phase 1).**
Replace the IN-list with the **dangling-record candidate hostnames** you identified:
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip,
coalesce(nullIf(attributes_string['http.host'], ''), nullIf(attributes_string['server.address'], ''), nullIf(attributes_string['net.host.name'], '')) AS req_host
SELECT client_ip AS ip,
req_host AS host,
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 req_host IN ('legacy.example.com','status.example.com','old-cdn.example.com') -- <-- takeover-prone candidates from Phase 1
GROUP BY ip, host
HAVING ip != '' AND host != '' AND hits >= 1
```
**Traffic-based — attack traffic riding over a name you control (containable source IP).** Once an
attacker uses your name, the source IP is exposed; standard per-IP error/volume aggregation (kept
here for completeness) lets you contain it while you fix the DNS/TLS root cause:
```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 IN ('400','401','403','404','405','422')) AS client_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 15 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
AND kind = 2
GROUP BY ip
HAVING ip != '' AND client_errors >= 50
```
**Events-based — unexpected-Host / downgrade signals (only if you emit the optional events from 4a;
query the logs table):**
```sql
SELECT
attributes_string['http.client_ip'] AS ip,
attributes_string['event.type'] AS signal,
attributes_string['host'] AS host,
count() AS attempts
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] IN ('dns.host.unexpected','tls.downgrade.detected')
AND timestamp >= now() - INTERVAL 15 MINUTE
GROUP BY ip, signal, host
HAVING ip != '' AND attempts >= 1
```
> **There is no SQL for the config-audit threats** (CAA, DNSSEC, cipher suites, cert expiry,
> registrar lock, SPF/DKIM/DMARC, CT-log leakage). They emit no telemetry. Do **not** author rules
> for them — they are 🔴 config findings, surfaced in Phase 3 and the gaps section. The
> `tls.cert.problem` event is the only way an *app-run* cert check becomes queryable, and only if
> the customer already runs such a check.
Useful attributes/columns: `event.type`, `http.client_ip`, `http.host` / `server.address`,
`response_status_code`, `kind`, `client.asn`, `client.as_org`, and your optional attributes
(`host`, `expected`, `reason`, `scheme`).
**Ready-to-copy command unit (required for every traffic rule).** Each detection ships as a
**complete, copyable unit** — never a fragment. For every rule emit, in order: (1) the SQL, (2) a
line saving it to `rules/<name>.sql`, (3) the full `securenow alerts rules create …` command, and
(4) the dry-run test. In Markdown each is its own fenced block so it copies cleanly; the exact
flags **must match `securenow alerts rules --help` from Phase 0.5**. Save each rule's SQL to
`rules/<name>.sql` so `--sql @rules/<name>.sql` resolves. Note pre-existing/system rules from
Phase 0 instead of duplicating them. Example:
```sql
-- rules/dns-unexpected-host.sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip,
coalesce(nullIf(attributes_string['http.host'], ''), nullIf(attributes_string['server.address'], ''), nullIf(attributes_string['net.host.name'], '')) AS req_host
SELECT client_ip AS ip, req_host AS host, count() AS hits
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 15 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 15 MINUTE)) - 1800
AND kind = 2
AND req_host NOT IN ('app.example.com','api.example.com','www.example.com','example.com')
GROUP BY ip, host
HAVING ip != '' AND host != '' AND hits >= 1
```
```bash
securenow alerts rules create \
--name "DNS: request to unexpected Host header" \
--sql @rules/dns-unexpected-host.sql \
--apps <APP_KEY> \
--severity high \
--schedule "*/15 * * * *" \
--nlp "HTTP request arriving for a Host not in the expected allowlist"
securenow alerts rules test <RULE_ID> --mode dry_run --wait # validate before it runs live
```
Apply the same four-part unit to the takeover-prone-Host rule, the attack-riding-your-name rule,
and the optional events-based rule above. Keep the existing conventions — `__USER_APP_KEYS__`
tenant scope (correct table column), the `ts_bucket_start` + `kind = 2` guards, the
`HAVING ip != ''` guard, and the **expected-Host allowlist / takeover-prone candidates from
Phase 1**. Remember the framing for this domain: these Host-header traffic rules are the **only**
SecureNow-runnable detections; **the DNS / registrar / TLS-config fix is the primary remediation**
on nearly every row and lives in the Code Findings report, not here.
#### Test mode for false-positive-prone rules — tag every rule `test-first` or `prod-ready`
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 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 (exploit-signature SQLi/XSS/RCE
matches, exact-match IoCs, known-bad ASN hits) may go straight to `prod`. In the report, **tag each
rule `test-first` or `prod-ready`** and say why. (`securenow alerts rules test <id> --mode dry_run
--wait` is the separate one-off *query* validation — run it before either.)
For **this domain** that means: the **unexpected-Host** and **takeover-prone-Host** rules are
**`test-first`** — uptime monitors, SSL scanners, CDN health-checkers, and partner probes
legitimately hit origins by IP / with odd Host headers, so run them detect-only, add
`securenow trusted`/`securenow fp` exclusions for the known-good infra, then promote. The
**attack-riding-your-name** per-IP error/volume rule is likewise heuristic → **`test-first`**. Only
the **injection payload riding your name** path (handled by the system signature rules +
`instant.block`, exact-match) is **`prod-ready`**. Emit the `--mode test` → observe (3–7 days) →
`--mode prod` promotion step on every `test-first` rule's command unit.
### 4c. Mitigation toolbox (the full SecureNow remediation surface)
For DNS/TLS, the **DNS / registrar / TLS-config fix (row 15) is the primary remediation on nearly
every row**; SecureNow can only **contain a source IP** once an attack rides over a name you
control, and **detect** traffic to unexpected/takeover-prone Hosts. **Always lead with the config
fix**, then layer edge mitigation mainly on **traffic to unexpected / takeover-prone hosts**. Once
a threat is confirmed, **choose the narrowest effective mitigation(s) from ALL of these** and
combine them (e.g. rate-limit a takeover-prone host + 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 probing your names / takeover-prone hosts 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 when an attacker rides a name you control to deliver an injection payload. Don't duplicate pattern SQL. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "..."` | confirmed-malicious source hitting an unexpected Host / takeover-prone name, all routes. |
| 4 | **IP block — scoped to route (+ method)** | `securenow blocklist add <ip> --route /admin* --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 of a name you control; least collateral. |
| 5 | **IP block — temporary / time-boxed** | `securenow blocklist add <ip> --duration 24h --reason "..."` (`30m`,`24h`,`7d`) · reverse `securenow blocklist unblock <id> --reason "..."` | auto-expiring containment of a source riding your name; audit-preserving unblock. |
| 6 | **Rate limit — per IP** | `securenow ratelimit add <ip> --limit 100 --window 1m --duration 24h --reason "..."` | throttle one source probing/abusing a name you control 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 on a name you control for everyone, budgeted per IP. |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /api/login --mode exact --method POST --limit 5 --window 1m --duration 24h` · NL `securenow ratelimit from-text "rate limit /api/login to 5/min for 24h" --yes` · test `securenow ratelimit test <ip> --path /api/login --method POST` | precise throttle of one source on one route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /login --difficulty 16 --clearance 30m` (route-wide) **or** `securenow challenge add <ip> --route /api/search --difficulty 18 --clearance 30m` · test `securenow challenge test <ip> --path /login --method GET` | bot probing of takeover-prone hosts 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 riding your names; actions include block / rate_limit / requireCaptcha. |
| 11 | **Session revocation** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | if a takeover-in-use yields a hijacked session — kill the stolen session, not the IP. (Rare on this domain.) |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "Office VPN / uptime monitor / SSL scanner / partner"` | stop false positives from known-good infra (uptime/SSL monitors that hit origins by IP / odd Host) — 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 the noisy unexpected-Host rule quiet (health-checkers / uptime monitors hitting by IP) without weakening it. |
| 15 | **DNS / registrar / TLS-config fix (PRIMARY for root cause)** | *DNS-record, registrar, or TLS-config change, described in the Code-Findings report, never auto-applied* | the actual fix: delete the dangling record, publish CAA, enable DNSSEC, renew/rotate the cert, set TLS 1.2/1.3 + modern ciphers + OCSP stapling, enable registrar lock + auto-renew, add HSTS, tighten SPF/DMARC, remove internal hostnames from public DNS. SecureNow contains; the config fix removes. |
**Choosing per threat** — for this audit-led domain the **config fix (row 15) is mandatory and
primary on nearly every row**: a SecureNow block on the source IP does **not** remove a dangling
CNAME, fix a missing CAA, renew a cert, or sign a zone — it only buys time against one attacker
while you fix DNS/registrar/TLS. Layer edge mitigation only on **traffic to unexpected /
takeover-prone hosts** and on **attacks riding a name you control**, and pick by **confidence**:
exploit-signature/exact IoC riding your name → instant-block (row 2) or block (rows 3–5); probable
bot probing a takeover-prone host on shared egress → **challenge** (row 9); noisy/legit-mixed
unexpected-Host traffic → **rate-limit (test-mode first)** (rows 6–8) and **trusted/fp** for
uptime/SSL monitors (rows 12, 14); 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 the edge mitigation with the
DNS/registrar/TLS-config fix** (row 15, Code-Findings report) — SecureNow can only contain the
actor; the config fix removes the root cause.
### 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 unexpected-Host / downgrade events — exercise the events-based rule (only if you emit them):
securenow event send dns.host.unexpected --ip 203.0.113.40 \
--attrs host=unknown.example.com,expected=app.example.com,reason=not_in_allowlist,test=true
securenow event send tls.downgrade.detected --ip 203.0.113.40 \
--attrs host=app.example.com,scheme=http,action=redirected,test=true
# Synthetic cert-health signal (only if your app runs a cert check that emits it):
securenow event send tls.cert.problem \
--attrs host=upstream.partner.com,reason=expiring,days_left=5,test=true
# Traffic-based unexpected-Host rule — send a request with an off-allowlist Host to a staging URL,
# then validate the rule query without waiting for the schedule:
# curl -H 'Host: unknown.example.com' https://<staging-origin>/ (against an app you own)
securenow alerts rules test <RULE_ID> --mode dry_run --wait
# Generate spans + inspect by Host/IP through the normal pipeline:
securenow test-span "threat-model.dns.smoke"
securenow forensics "requests grouped by Host header and IP in the last hour" --env production
# Mitigation verification (containment of a source riding a name you control):
securenow firewall test-ip 203.0.113.40 --app <APP_KEY> --env production
securenow ratelimit test 203.0.113.40 --path / --method GET
securenow challenge test 203.0.113.40 --path / --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>
```
> The **config-audit threats have no SecureNow test** — you verify them out-of-band: re-resolve
> the DNS record to confirm the dangling target, `dig CAA example.com` to confirm CAA presence,
> connect with a TLS client to confirm protocol/cipher/expiry, check the registrar UI for the
> lock, and query a CT-log service for unexpected certs. State the verification command for each
> in the runbook; none of them is a `securenow` command.
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). Every 🔴 row
states the **out-of-band verification step** instead.
---
## Phase 5 — Write the four reports (two tracks)
Write **four** files into `threat/21-dns-tls-certificates/` — a **Detection & Mitigation** track
(what to run in SecureNow) and a **Code Findings & Recommendations** track (the DNS/registrar/TLS
audit). Each track is one Markdown file **and** one self-contained HTML file. The two tracks
**cross-link** each other. For this audit-led domain the **Code Findings report carries most of the
content** (the config fixes), while the Detection report stays small (only the Host-header traffic
rules). Never drop anything security-relevant — split it honestly across the two tracks.
### 5a. Detection & Mitigation report — sections (both `.md` and `.html`), in order
File: `dns-tls-certificates-detection-mitigation.md` / `.html`.
1. **Executive summary** — stats line (threats modeled · covered · partial · gaps · rules to
create · mitigations), top 3 **detectable** DNS/TLS risks, installed `securenow` version + app
key + firewall state, and **an explicit statement that native SecureNow coverage is LOW here —
this is an audit-led model whose primary fixes (in the code-findings report) are
DNS/registrar/TLS config**. One-line OWASP 2021 coverage note (A02 / A05 / A08).
2. **SDK & environment** — installed SDK version (from `node_modules/securenow`, Phase 0.5), app
key(s), environment, firewall state, existing rules/automations/challenge rules (from Phase 0),
system signature rules present, and the **service name / configured hostnames** (the expected-
Host allowlist source).
3. **Threat → Detection → Mitigation matrix** — one row per modeled threat:
`# | Threat | OWASP | Coverage 🟢/🟡/🔴 | Detection rule (or "—") | Mode (test-first / prod-ready) | Signal (threshold+window / "config-audit finding") | Schedule | Sev | Mitigation (config fix primary)`.
Severity ∈ {critical, high, medium, low}. Each row's **Mitigation cell must pick specific,
scoped mitigation(s) by number from the §4c toolbox** (e.g. "row 15 config fix [primary] + row 9
challenge scoped to `legacy.example.com`" or "row 15 + row 5 block <ip> 24h") — never a generic
"block the IP." Each row with a detection rule carries a **`test-first` / `prod-ready` tag** in
the Mode column (config-audit "—" rows leave it blank). **Most rows will carry "config-audit
finding" as the signal and "—" as the rule, and link to the matching finding in the code-findings
report** — that is correct. Include the deferred rows (48–51) pointing to the sibling models, plus
the "Out of scope" N/A list.
4. **Detection rules to create** — each (few) traffic rule as the **ready-to-copy command unit**
from Phase 4 (SQL → save to `rules/<name>.sql` → full `securenow alerts rules create …` →
dry-run test). **Tag every rule `test-first` or `prod-ready`** (per Phase 4b): the
unexpected-Host, takeover-prone-Host, and attack-riding-your-name rules are **`test-first`** —
their command unit must include the `--mode test` → observe (3–7 days) → `--mode prod` promotion
step; only the injection-signature path is **`prod-ready`**. Injection-class abuse riding your
names references the **system signature rules + `instant.block`**, not duplicate SQL. **State
plainly that the config-audit threats have no rule** and link to the code-findings report. Note
rules that already exist (Phase 0) instead of duplicating them.
5. **Instrumentation the detections need** — only the `track('…')` events the rules above consume
(`dns.host.unexpected` / `tls.downgrade.detected` / `tls.cert.problem`), each a copyable
snippet, with the honest note that **most DNS/TLS posture emits no telemetry at all** and the
Host header is auto-captured. Point to the code-findings report for *where* to add them.
6. **Mitigation mechanisms** — render the **full §4c 15-row mitigation toolbox table verbatim**
(firewall · exploit-signature instant-block · block [global / route+method / temporary] ·
rate-limit [IP / route / IP+route] · challenge · auto-block · revoke · trusted · allowlist · fp ·
**DNS/registrar/TLS-config fix [row 15, primary]**) followed by the "Choosing per threat"
paragraph, then per-threat ready-to-copy mitigation command(s) selected **by number** from the
toolbox + reversibility. Make explicit that the config fix (row 15) is primary on nearly every
row and the SecureNow control is only source-IP containment for attacks riding your names /
traffic to unexpected/takeover-prone hosts.
7. **Action plan (copy-paste, ordered)** — ① firewall + signature instant-block, ② add (optional)
instrumentation where the app already checks, ③ create the unexpected-Host / takeover-prone-Host /
attack-riding-your-name rules **in `--mode test` (detect-only — they are FP-prone)** with an
explicit **"promote to `--mode prod` after 3–7 days of observation + `fp`/`trusted` tuning"**
step; create only the injection-signature path armed (`prod-ready`), ④ enable
automations/challenge, ⑤ test, ⑥ verify in dashboard, ⑦ **schedule the DNS/registrar/TLS
config-fix work from the code-findings report — this is the bulk of the remediation** + set up
out-of-band cert-expiry/CAA/CT-log monitoring. Real commands, `<APP_KEY>` substituted.
8. **Testing & validation** — per-rule recipe: `securenow event send …` / `test-span` / dry-run +
expected outcome + cleanup (TEST-NET IPs `192.0.2`/`198.51.100`/`203.0.113`). For every 🔴
config row, state the **out-of-band verification step** (`dig CAA`, TLS client, registrar UI,
CT-log query) instead — none is a `securenow` command.
9. **Response runbooks** — per notification type (unexpected-Host, takeover-prone-Host,
attack-riding-your-name): confirm TP → contain command (copy) → reverse command (copy) — **and**
the DNS/registrar/TLS action that is the real fix (link to the code-findings row). Plus an
out-of-band runbook for cert-expiry and rogue-cert (CT) alerts.
10. **Known gaps & SecureNow feature requests** — **the largest detection-side section here.** Each
🔴: why it's not coverable (emits no traffic/event), interim fix (link to the code-findings
report), and the *"Requires SecureNow team — contact your SecureNow account contact (or
in-dashboard support) to request support for this threat."* line.
11. **Appendix** — resolved SDK/CLI version (Phase 0.5), app key, environment, rule IDs created,
domains audited, date, link to the code-findings report.
### 5b. Code Findings & Recommendations report — sections (both `.md` and `.html`), in order
File: `dns-tls-certificates-code-findings.md` / `.html`. State at the top: *"Findings only — no
DNS, registrar, TLS config, IaC, or application code was modified."*
1. **Executive summary** — findings by severity (critical/high/med/low), top 3 DNS/TLS config
risks, one-paragraph posture verdict (honest: this is the heaviest report of the two — the
primary remediation surface is DNS/registrar/TLS config).
2. **Surface & inventory** — the Phase 1 naming & transport inventory: hostname→resolution→owner
map, dangling-candidate list, registrar posture table, DNS/DNSSEC posture, CAA posture, TLS
protocol/cipher table, certificate inventory, renewal/monitoring status, HTTP/HSTS/mixed-content
posture, expected-Host allowlist, email-auth DNS records, CT-log exposure notes.
3. **Threat catalog** — the exhaustive Phase 2 catalog (groups A–M), each tagged OWASP (A02 / A05 /
A08), modeled or explicit N/A. Include the deferred rows (48–51) pointing to the sibling models.
4. **Code-level findings (audit)** — the Phase 3 findings as a table
`# | Location (DNS record / registrar / cert / IaC file:line / endpoint) | Threat | OWASP | Sev | Issue | Recommended fix`,
each with the quoted record/config/cert field and the described fix (never applied). Cross-link
any row that has a backing detection rule to the detection-report row.
5. **Strengths** — controls already present and correct (CAA present and minimal, DNSSEC signed,
TLS 1.2/1.3-only with PFS, registrar locked, auto-renew proven, DMARC `p=reject`, HSTS
preloaded, no dangling records) — keep the posture honest.
6. **App / config fixes (primary remediation)** — the concrete DNS / registrar / TLS-config changes
that remove the root cause (described, not applied): delete the dangling record, publish CAA,
enable DNSSEC, renew/rotate the cert, set TLS 1.2/1.3 + modern ciphers + OCSP stapling, enable
registrar lock + auto-renew, add HSTS, tighten SPF/DMARC, remove internal hostnames from public
DNS. Each linked to the detection-report row it backs (if any).
7. **Instrumentation recommendations** — the `track('…')` calls to add and the exact `file:line` to
add them (only where the app already makes such a check), so the detection rules light up.
8. **Appendix** — files/records/endpoints reviewed, resolved SDK version (Phase 0.5), date, link to
the detection-mitigation report.
### 5c. HTML — two self-contained skeletons (offline; inline CSS + copy JS; no network)
Both HTML files share the `<head>` below (the existing brand tokens **plus** copy-button styles)
and the copy `<script>` at the end of `<body>`. Change only the `<title>`, the sidebar subtitle,
the `<h1>`, and the per-track section content. **Wrap EVERY command/SQL block as a `.cmd`** so it
gets a Copy button (the Code-Findings HTML may omit copy buttons on prose, but still wraps any
example/fix command in `.cmd`). Stats numbers MUST equal the matrix/findings row counts.
**Shared head + copy CSS + copy script (used by BOTH files):**
```html
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title><!-- "Detection & Mitigation — DNS / TLS / Certificates — SecureNow" OR "Code Findings — DNS / TLS / Certificates — 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 · DNS / TLS / Certificates" OR "Code Findings · DNS / TLS / Certificates" --></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><!-- primary domain(s) --></code> · <span class="pill">securenow <!-- installed version --></span></p></header>
<div class="stats"><!-- 5 .stat cards; numbers MUST equal the table/finding counts of THIS track --></div>
<!-- <section id="…"> blocks mirroring the Markdown sections of THIS track (5a or 5b) -->
<footer>Generated by the SecureNow DNS / TLS / certificates 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>
```
**Detection & Mitigation HTML** — `<title>` = "Detection & Mitigation — DNS / TLS / Certificates —
SecureNow", subtitle = "Detection & Mitigation · DNS / TLS / Certificates", `<h1>` = "DNS / TLS /
Certificates — Detection & Mitigation"; stats cards = threats modeled · covered · partial · gaps ·
rules to create; one `<section>` per 5a section. Wrap **every** SQL/command block in the copyable
wrapper:
```html
<div class="cmd"><button class="copy" type="button">Copy</button><pre>securenow alerts rules create \
--name "DNS: request to unexpected Host header" --sql @rules/dns-unexpected-host.sql \
--apps <APP_KEY> --severity high --schedule "*/15 * * * *" \
--nlp "HTTP request arriving for a Host not in the expected allowlist"</pre></div>
```
**Code Findings HTML** — `<title>` = "Code Findings — DNS / TLS / Certificates — SecureNow",
subtitle = "Code Findings · DNS / TLS / Certificates", `<h1>` = "DNS / TLS / Certificates — Code
Findings & Recommendations"; stats cards = findings (critical · high · medium · low) + total; one
`<section>` per 5b section. Wrap any example/fix command in `.cmd`.
Badge usage (both files): severity `<span class="b crit|high|med|low">`; coverage
`<span class="c cov|part|gap">`; OWASP `<span class="owasp">A05</span>`; CWE `<span class="cwe">`;
mitigation `<span class="m firewall|signature|rate|challenge|block|notify|appfix">` (use `appfix`
for the **DNS/registrar/TLS-config fix**); rule IDs `<span class="rid">`. Stats numbers must equal
the matrix/findings row counts.
---
## Quality bar (the report is rejected if any of these fail)
- Every catalog item A1–M54 is either a matrix row or an explicit N/A line; each modeled row
carries its OWASP 2021 tag (**A02 / A05 / A08**, or a combination).
- The model is **honest about LOW native coverage**: the executive summary states it is an
audit-led model, the bulk of rows are 🔴/🟡 with a **DNS/registrar/TLS-config fix as the primary
remediation**, and no config-audit threat is dressed up as a fabricated detection rule.
- The deferred rows (48–51) point to the **numbered sibling paths** (`../13-legacy-endpoints/`,
`../19-cloud-infrastructure/`, `../11-messaging-notifications/`, `../20-secrets-and-cloud-iam/`)
and are **not re-derived** — only the DNS/TLS slice this model owns is noted.
- Every matrix row has a concrete signal (a traffic threshold + window **or** the explicit
"config-audit finding"), severity, and mitigation — no "monitor for suspicious activity" filler.
- Every config finding in section 4 cites a concrete artifact (DNS record / registrar setting /
certificate field / IaC `file:line` / live endpoint), quotes the record/config, and gives a
described fix — and **no DNS, registrar, TLS config, IaC, or code was modified** (this is an audit).
- Every traffic 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 and the
`HAVING ip != ''` guard; the Host-based rules use the **expected-Host allowlist / takeover-prone
candidates from Phase 1**.
- The only telemetry-backed detections are the **Host-header traffic rules** (and the optional
`dns.*`/`tls.*` events); the config-audit threats are explicitly **not** given SQL.
- Only commands, flags, events, and SQL columns from this prompt's building blocks appear
(including `securenow challenge …`, `firewall`, and the `dns.host.unexpected` /
`tls.downgrade.detected` / `tls.cert.problem` events).
- Detection vs. fix is honest: on every row SecureNow can only **contain the source / detect
unexpected Hosts**, while the **DNS/registrar/TLS-config fix is paired as primary**.
- Every 🔴 gap appears in the gaps section with an interim DNS/registrar/TLS-config fix **and** the
"contact the SecureNow team" line, plus an out-of-band verification step.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted in and puts the config-fix work
front and centre.
- **Phase 0.5 ran**: the resolved installed `securenow` version appears in **both** reports'
appendix, and no command/flag/event/column is emitted that the installed SDK/CLI does not expose
(else it is annotated `# requires securenow >= <version>`).
- Every detection rule is a **complete copyable unit** (SQL → `rules/<name>.sql` → full
`securenow alerts rules create …` → dry-run test); flags match `alerts rules --help`.
- **Four** files are written to `threat/21-dns-tls-certificates/`
(`dns-tls-certificates-detection-mitigation.md`+`.html`,
`dns-tls-certificates-code-findings.md`+`.html`); the two tracks **cross-link**; **both HTML
files are self-contained** (inline CSS/JS, no CDN/fonts/network) and **every command block in the
detection HTML has a working Copy button** (`.cmd` + `<button class="copy">`); the stats cards
match the table/finding counts.
- The split is **honest**: SecureNow-runnable detections/mitigations live in the Detection report;
the DNS/registrar/TLS-config changes (the bulk of this domain) live in the Code-Findings report;
nothing security-relevant is dropped.
- A one-line summary is printed back to the user: **per-track file paths**, threat counts,
rules-to-create count, code/config findings by severity, gaps, OWASP coverage, and the resolved
SDK version.
- The Detection report's mitigation section presents the **full toolbox** (§4c: firewall ·
instant-block · block [global / route / method / temporary] · rate-limit [IP / route / IP+route] ·
challenge · auto-block · revoke · trusted · allowlist · fp · **DNS/registrar/TLS-config fix
[primary]**), and **each modeled threat's matrix row selects specific, scoped mitigation(s) from
it by number** — 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 (the
injection-signature path) 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 ════════════════ -->