#19A05 · Cloud-Native · CIS
Cloud Infrastructure
Network/SG, compute, containers/k8s, and IaC misconfiguration.
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/19-cloud-infrastructure/— opencloud-infrastructure-code-findings.html(the audit) andcloud-infrastructure-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
# Cloud Infrastructure 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 contains your
**Infrastructure-as-Code** (Terraform / CloudFormation / CDK / Pulumi / Kubernetes manifests /
Helm / Dockerfiles) and has the `securenow` CLI installed and logged in. The agent will inventory
the cloud surface, build an exhaustive **cloud infrastructure / CSPM** threat model mapped to
**OWASP A05:2021 (Security Misconfiguration)**, the **OWASP Cloud-Native / Kubernetes security
guidance**, and the **CIS Benchmarks**, audit the IaC and config for posture flaws, and emit a
**two-track, SecureNow-branded** deliverable set in **Markdown + self-contained HTML** — a
**Detection & Mitigation** runbook (the few detection rules SecureNow can run + the
mitigation/containment commands available) and a **Code Findings & Recommendations** audit (the
**cloud/IaC config fixes that are the primary remediation**, audited, **not** fixed) — plus which
threats need the SecureNow team. **For this lowest-native-coverage domain the code-findings report
carries most of the content** (IaC / cloud-config fixes), while the detection report covers only
the handful of app-fronted / SSRF-to-metadata rules SecureNow can actually run — keep that honest
split throughout. Every rule and command is grounded in the **installed** `securenow` SDK and
emitted as a **ready-to-copy** block (SQL → save → create → dry-run).
> **Read this first — what SecureNow can and cannot do here.** SecureNow is a runtime
> **API / traffic** security layer (firewall, rate-limit, challenge, exploit-signature
> instant-block, ASN enrichment, forensics). It runs *inside or in front of your application*. It
> **cannot see your cloud control plane**: it does not read your security groups, your IAM, your
> bucket ACLs, your CloudTrail config, or your Kubernetes RBAC. So this is the **lowest
> native-coverage** model of the set. Most rows here are 🔴 **GAP** or 🟡 **PARTIAL** with a
> **cloud/IaC config fix as the primary remediation** — this is fundamentally an **audit +
> findings + IaC-hardening** model. What SecureNow *does* add: it detects **abuse that reaches the
> application** (traffic to an exposed service, scanning, floods), it detects **SSRF-to-metadata**
> attempts (the one cloud-infra attack that shows up as app traffic/events), and it can
> **firewall / rate-limit / challenge / block the malicious source** to contain the blast radius
> while you fix the config. Be honest about this on every row.
This is the **cloud-posture (CSPM) layer**, sitting *below* the application. It owns "what is
reachable on the network and how the compute / container / cluster is hardened," plus the IaC that
defines all of it. It **defers** three adjacent cloud concerns to their own numbered models and
references them rather than re-deriving:
- **Cloud identity / IAM** (roles, policies, trust relationships, key management) →
[../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/)
- **Object-storage / bucket / object ACLs & data exposure** →
[../23-storage-and-logs/](../23-storage-and-logs/)
- **Serverless / FaaS / edge** (Lambda, Functions, Workers, edge runtime) →
[../18-serverless-and-edge/](../18-serverless-and-edge/)
Requirements on the customer machine: `npm i -g securenow && securenow login` (admin auth + app
runtime connected), and the repository should contain the IaC/manifests that define your cloud
footprint. Everything else is discovered by the agent.
---
<!-- ════════════════ COPY EVERYTHING BELOW THIS LINE ════════════════ -->
# Generate a Cloud Infrastructure Threat Model Report (SecureNow)
You are a senior cloud-security / appsec engineer specializing in cloud-native posture (CSPM).
Produce an **exhaustive cloud-infrastructure threat model for THIS codebase's infrastructure**,
organized along **OWASP A05:2021 (Security Misconfiguration)**, the **OWASP Cloud-Native /
Kubernetes security guidance**, and the relevant **CIS Benchmarks** (CIS AWS/Azure/GCP Foundations,
CIS Kubernetes, CIS Docker), mapped to **SecureNow** detections and mitigations where they apply,
with a ready-to-run action plan **and** a code-level audit of every IaC / config flaw you find.
You write **four** deliverables (two tracks × MD + HTML) into `threat/19-cloud-infrastructure/`
(create the folder if needed):
1. `cloud-infrastructure-detection-mitigation.md` — the **operational runbook**: what to run in
SecureNow (the few detection rules, mitigation/containment commands, action plan).
2. `cloud-infrastructure-detection-mitigation.html` — the same runbook as a **self-contained** HTML
page (inline CSS + JS, no network requests) with **per-command Copy buttons**.
3. `cloud-infrastructure-code-findings.md` — the **code/IaC audit**: the exhaustive threat catalog,
the IaC / cloud-config findings, and the **config fixes that are the primary remediation**
(described, never applied). For this domain **this report carries most of the content.**
4. `cloud-infrastructure-code-findings.html` — the same audit as a **self-contained** HTML page.
The two tracks **cross-link**: the gaps / instrumentation rows in the detection report link to the
relevant code finding, and the config-fix rows in the code-findings report link back to the
detection-report containment they pair with. Every rule and command is **grounded in the installed
SecureNow SDK** (Phase 0.5) and emitted as a **ready-to-copy** unit (Phase 4).
Work in the seven phases below, in order. **Never invent facts**: if a resource is not in the IaC,
or a CLI command returns nothing, say "not found" — do not guess at the live cloud state from the
code (note when the IaC may have drifted from what is actually deployed). **Do not modify
application code, IaC, or any cloud infrastructure.** You are auditing: every fix is *described in
the report*, never applied to the repo, never run against the cloud account.
**Coverage honesty (read before you badge anything).** SecureNow sees **traffic** and **events**
from the running app; it does **not** see cloud config. Therefore:
- A misconfigured security group, a public RDS instance, an open k8s API server, a privileged pod,
a disabled CloudTrail, an unencrypted volume, or a public AMI is a **config/IaC fix** — SecureNow
cannot detect the *misconfiguration* itself. Most such rows are 🔴 **GAP** (no app signal) or 🟡
**PARTIAL** (SecureNow can only detect/contain *abuse that reaches the app* once an attacker uses
the exposure).
- The **one** cloud-infra attack class with strong native coverage is **SSRF-to-cloud-metadata**
(IMDS `169.254.169.254`, GCP/Azure metadata) — that surfaces as app traffic/events and is 🟢/🟡.
- For everything else, pair the row with the **cloud/IaC config fix as the primary remediation**,
and where SecureNow can contain a malicious source reaching the app, note the edge containment as
secondary. Do not inflate coverage. A flaw that emits no app traffic and no event is 🔴.
**Scope discipline.** This model owns: network exposure, compute hardening, containers &
Kubernetes, and IaC misconfiguration. It does **not** re-derive cloud **identity/IAM**, object
**storage/bucket ACLs**, or **serverless/edge** — list those in a "Deferred to sibling models"
subsection, link the numbered reports, and only model the *posture-adjacent* overlap (e.g. "IMDS
reachable from a pod" is in scope here; "the IAM role IMDS hands out" is deferred to ../20-).
---
## 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 / infrastructure maps to before continuing. Note the **firewall state** and any
**system signature rules** already present.
> Reminder for this domain: SecureNow has **no cloud-account read access**. Phase 0 confirms the
> *app-side* tooling only. The bulk of this model's evidence comes from **reading the IaC in the
> repo** (Phase 1 + Phase 3), not from any SecureNow command. Do **not** attempt to call cloud
> provider CLIs (`aws`, `az`, `gcloud`, `kubectl`) against a live account — audit the committed
> IaC and config files. If the user *volunteers* read-only cloud CLI access, you may cross-check
> drift, but never write.
---
## 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 cloud surface (IaC + config analysis)
Cloud security starts with an **inventory of what is actually defined and reachable**, not what is
intended. Read the IaC and config in the repo and document at minimum:
- **IaC toolchain & layout** — Terraform (`*.tf`, modules, `.tfvars`, remote backend config),
CloudFormation/SAM (`*.yaml`/`*.json` templates), AWS CDK / Pulumi (TS/Python/Go), Kubernetes
manifests (`*.yaml`, kustomize), Helm charts (`Chart.yaml`, `values.yaml`, templates),
Dockerfiles / `docker-compose.yml`, Ansible, Serverless Framework. Note the cloud provider(s),
regions, and account/subscription/project structure referenced.
- **Network exposure** — every security group / NSG / firewall rule / NACL: which **ingress** rules
allow `0.0.0.0/0` (or `::/0`) and on what ports. Flag management/admin ports open to the world:
SSH (22), RDP (3389), database ports (5432/3306/1433/27017/6379/9200/5984/9092), the k8s API
(6443), kubelet (10250), etcd (2379/2380), Docker (2375/2376), admin/management consoles. List
**egress** rules — is egress unrestricted (`0.0.0.0/0` all ports)?
- **Network topology & segmentation** — VPCs/VNets, public vs private subnets, route tables, NAT
gateways, internet gateways, peering, transit gateways, VPC endpoints/PrivateLink. Is there a
**flat network** (everything in one subnet, no tiering)? Are databases / internal services in
**public subnets**? Is there micro-segmentation between tiers or is it all-allow internally?
- **Public compute & data resources** — EC2/VM instances with public IPs; load balancers; databases
(RDS/Aurora/Cloud SQL/Azure SQL/managed Mongo/Redis/Elasticsearch) with `publicly_accessible` or
public endpoints; **public snapshots, AMIs, machine images, EBS/disk volumes**; container
registries (ECR/GCR/ACR) with public/anonymous pull.
- **Encryption posture** — encryption-at-rest on volumes/disks/databases/snapshots (KMS or default,
on/off); encryption-in-transit between internal services (TLS on internal LBs / service mesh /
database connections, or cleartext); TLS termination points.
- **Kubernetes** — API server exposure (public endpoint? authorized networks?); dashboard exposed;
kubelet read-only/anonymous auth; **RBAC** (cluster-admin bindings, wildcard verbs/resources,
default service-account auto-mount); **Pod Security** (PodSecurity admission / PSP / OPA-Gatekeeper
/ Kyverno present?); **privileged pods**, `hostPath`, `hostNetwork`, `hostPID`, `hostIPC`,
`allowPrivilegeEscalation`, added Linux capabilities, `runAsRoot`; **NetworkPolicy** (any defined,
or default-allow?); default **service-account token** mounting; secrets stored as plain
ConfigMaps; admission controllers; the cluster's IMDS reachability from pods.
- **Containers** — Dockerfiles: running as **root** (no `USER`), writable/`--privileged`
containers, base-image freshness/pinning, image scanning in the pipeline (or none), `latest`
tags, missing `seccomp`/`AppArmor`/`no-new-privileges`, secrets baked into image layers,
unnecessary packages / large attack surface.
- **IMDS / metadata reachability** — IMDSv1 (token-less) vs IMDSv2 (hop-limit, token-required) on
instances; whether pods/containers can reach `169.254.169.254` (no metadata firewall / no
`hostNetwork`-level isolation) → container-escape-to-cloud-credential path.
- **Cloud audit & flow logging** — CloudTrail / Azure Activity Log / GCP Audit Logs enabled,
multi-region, log-file validation; VPC Flow Logs / NSG Flow Logs; load-balancer access logs;
whether logs go to a tamper-resistant store. Flag **disabled** audit logging.
- **State & secrets in IaC** — Terraform **state** backend (remote + encrypted + locked, or local
`terraform.tfstate` **committed to the repo**?); secrets/keys/passwords hardcoded in `.tf`,
templates, `values.yaml`, `.tfvars`, compose files, or env files; `*.tfstate` containing
plaintext secrets.
- **Region / account sprawl & shadow resources** — multiple regions/accounts referenced; resources
defined outside the managed IaC (manual/console-created, "shadow cloud"); deprecated stacks still
defined; resources with no owner/tags.
- **App ↔ infra binding** — which of the above hosts the SecureNow-instrumented app (so we know
which exposures sit *in front of* SecureNow vs entirely outside its visibility), and whether the
app makes outbound fetches of **user-influenced URLs** (the SSRF-to-metadata sink that *is*
detectable — cross-reference [../14-api-security/](../14-api-security/) for the deep SSRF model).
- **SecureNow instrumentation already present** — `securenow/register` / `securenow run` /
`securenow init`, any `securenow/events` `track()` calls, firewall state. This determines what
app-side signal exists at all.
Output of this phase = the report's **Cloud surface & inventory** section: the IaC toolchain
summary; the **network exposure table** (resource / rule / ports / source CIDR / public?); the
topology/segmentation note; the **public-resource list**; the **encryption posture table**; the
**Kubernetes posture summary**; the **container posture summary**; the **IMDS reachability note**;
the **audit/flow-logging status**; the **IaC state & secrets status**; the **region/account sprawl
note**; and a short paragraph naming the real cloud attack surface for this stack and which parts
SecureNow can even see.
---
## 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. "Kubernetes items: N/A, no cluster — single VM + systemd"). Never silently drop an
item. Add stack-specific threats you discover that are not listed — this catalog is the floor, not
the ceiling. Tag each modeled row with its framework code: **A05** (OWASP A05:2021), **CN-***
(OWASP Cloud-Native / Kubernetes guidance), and/or the relevant **CIS** control family
(`CIS-AWS`, `CIS-K8s`, `CIS-Docker`, `CIS-Azure`, `CIS-GCP`).
> Most rows in this catalog are **config-audit findings, not traffic-detectable**. For each, state
> explicitly whether there is *any* app-observable signal. If there is none, mark it
> "config-audit finding, not traffic-detectable" in the Signal column and badge it 🔴 (or 🟡 if
> SecureNow can contain the *resulting* abuse once it reaches the app).
**A. Exposed management / admin ports (A05 · CIS-AWS 5.x · CIS-K8s)**
1. SSH (22) open to `0.0.0.0/0`
2. RDP (3389) open to `0.0.0.0/0`
3. Database port open to the internet (Postgres 5432 / MySQL 3306 / MSSQL 1433 / Mongo 27017)
4. Redis (6379) / Memcached (11211) reachable from `0.0.0.0/0` (often unauthenticated)
5. Elasticsearch / OpenSearch (9200/9300) / Kibana exposed to the world
6. Kubernetes API server (6443) public with no authorized-networks restriction
7. Kubelet (10250) / etcd (2379-2380) / Docker daemon (2375/2376) reachable externally
8. Admin/management consoles & dashboards (k8s dashboard, RabbitMQ, Kafka, Grafana, Jenkins) open
**B. Over-permissive network rules & missing segmentation (A05 · CIS-AWS 5.x)**
9. Security group / NSG ingress allowing all ports (`0-65535`) from any source
10. Overly broad CIDR on a sensitive rule (e.g. `/8` or a whole cloud provider range)
11. Unrestricted egress (`0.0.0.0/0` all ports) enabling exfil / C2 / SSRF pivot
12. Default security group used (leaving default-allow rules in place)
13. Flat network — no public/private subnet tiering, no per-tier segmentation
14. Missing micro-segmentation — any internal host can reach any other (lateral movement)
15. NACL/firewall rule ordering or `allow-all` overriding a restrictive intent
**C. Public compute, databases, snapshots & images (A05 · CIS-AWS 2.x/5.x)**
16. Managed database with `publicly_accessible = true` / public endpoint
17. Compute instance with a public IP that need not have one (no bastion / no private subnet)
18. Public EBS/disk **snapshot** (data leak via snapshot copy)
19. Public **AMI / machine image** (may embed secrets / proprietary code)
20. Public/unencrypted **volume** or volume shared cross-account
21. Container registry (ECR/GCR/ACR) allowing anonymous/public **pull** of private images
22. Managed cache (ElastiCache/MemoryStore) or search cluster with a public endpoint
**D. Missing isolation, encryption & egress controls (A05 · CIS-AWS 2.x/3.x)**
23. No private subnets / no VPC isolation for data tier (DB in a public subnet)
24. No VPC endpoints / PrivateLink — internal AWS/Azure traffic traverses the public internet
25. Encryption-at-rest disabled on volumes / databases / snapshots
26. Encryption-in-transit missing between internal services (cleartext DB/cache/service traffic)
27. No egress filtering / no NAT-with-allowlist — instances can call anywhere (SSRF + exfil)
28. Default / weak KMS key usage, no key rotation, shared keys across environments
**E. Kubernetes cluster & control-plane (CN-* · CIS-K8s)**
29. API server publicly reachable without authorized IP ranges
30. Kubernetes dashboard exposed (and/or with cluster-admin service account)
31. Kubelet anonymous/read-only auth enabled (node takeover, pod exec)
32. Weak RBAC — cluster-admin bindings to users/SAs, wildcard `verbs`/`resources`/`apiGroups`
33. Default service-account token auto-mounted into pods (`automountServiceAccountToken` not false)
34. No PodSecurity admission / no PSP replacement / no OPA-Gatekeeper or Kyverno policy
35. No NetworkPolicy — default-allow pod-to-pod traffic (flat cluster network)
36. Secrets stored in plain ConfigMaps / unencrypted etcd / committed manifests
**F. Pod & workload hardening (CN-* · CIS-K8s)**
37. Privileged pods (`securityContext.privileged: true`)
38. `hostNetwork` / `hostPID` / `hostIPC` — namespace sharing with the node
39. `hostPath` volume mounts (node filesystem access / escape primitive)
40. `allowPrivilegeEscalation: true` / added Linux capabilities (`SYS_ADMIN`, `NET_ADMIN`, etc.)
41. Pods running as root (`runAsNonRoot` not set / `runAsUser: 0`)
42. No `seccomp` / `AppArmor` profile; no `readOnlyRootFilesystem`; no resource limits (DoS)
**G. Container image & runtime hardening (CN-* · CIS-Docker)**
43. Dockerfile runs as **root** (no `USER` directive)
44. `--privileged` / writable / `--cap-add` containers at runtime
45. Unscanned images / no vulnerability scanning gate in CI
46. `latest` / unpinned base-image tags; stale/EOL base images with known CVEs
47. Secrets baked into image layers (build args, copied `.env`, leaked in history)
48. No `no-new-privileges`, no seccomp at runtime; oversized image / unnecessary tooling (shells, package managers)
**H. Container escape & metadata reachability (CN-* · A05)**
49. Container escape surface (privileged + hostPath + writable → break to node)
50. **IMDS reachable from a pod/container** → steal node/instance role credentials
51. IMDSv1 (token-less) enabled — SSRF or escape yields credentials with one GET
52. No hop-limit / no metadata firewall — pods can reach `169.254.169.254`
**I. IaC misconfiguration (A05 · CIS-* via IaC)**
53. IaC defines a **public bucket / public-read object store** (defer object ACL depth to ../23-)
54. IaC defines an **open security group** (`0.0.0.0/0` ingress on sensitive ports)
55. IaC **disables encryption** (storage_encrypted=false, no KMS, plaintext)
56. IaC **disables logging** (CloudTrail/flow-logs/access-logs not created or turned off)
57. IaC grants over-broad resource policies / wildcards (defer IAM depth to ../20-)
58. IaC drift — committed code ≠ deployed reality (manual changes outside IaC)
59. No IaC scanning (tfsec / checkov / kics / Trivy / cfn-nag) in the pipeline
**J. IaC state & secret hygiene (A05 · CIS-*)**
60. Terraform **state committed to the repo** (`terraform.tfstate` / `*.tfstate.backup`)
61. Unencrypted / non-locked remote state backend (plaintext secrets in state, race corruption)
62. Hardcoded secrets/keys/passwords in `.tf` / templates / `values.yaml` / `.tfvars` / compose
63. State or plan output in CI logs / artifacts exposing secrets
**K. Audit logging, flow logs & observability gaps (A05 · CIS-AWS 3.x)**
64. CloudTrail / Azure Activity Log / GCP Audit Logs disabled or single-region
65. VPC / NSG **flow logs** disabled (no network forensics)
66. Load-balancer / ingress access logs disabled
67. No log integrity (no log-file validation, logs writable/deletable by workload role)
68. Logs not centralized / no alerting on control-plane changes (defer log-store depth to ../23-)
**L. Region / account sprawl & shadow cloud (A05)**
69. Resources in unmonitored/unexpected regions (cost + unmonitored attack surface)
70. Shadow resources created outside IaC (console/manual) — unmanaged, unscanned
71. Deprecated stacks/environments still deployed and reachable
72. Cross-account / cross-tenant sharing (shared snapshots, AMIs, peering) without review
**M. Negative-space & evasion (A05 · CN-*)**
73. Public resource reachable directly, bypassing the WAF/firewall in front of the app (origin exposure)
74. Egress path used to bypass ingress controls (reverse shell / data exfil over allowed egress)
75. DNS rebinding / metadata-host tricks to reach IMDS through an app-layer SSRF guard
76. Decimal/hex/octal/`[::]`/`0` encodings of `169.254.169.254` to evade naive IMDS blocklists
77. IPv6 ingress (`::/0`) left open while IPv4 is locked down
**N. Observable abuse — what SecureNow telemetry actually catches (the few workhorse rows)**
78. **SSRF-to-cloud-metadata attempt reaching the app** — outbound fetch / request targeting
`169.254.169.254`, `metadata.google.internal`, `169.254.169.254/latest/meta-data`, or the
Azure IMDS — app traffic/event-observable, **the highest-value cloud-infra detection**
79. Scanning / probing of an **exposed service that is in front of SecureNow** (port/path probing,
4xx/5xx spikes, distinct-path fanout) — contain the source at the edge
80. Volumetric flood / brute-force reaching an exposed admin/login surface SecureNow fronts
81. Traffic from a **known-bad / scanner IP** hitting the app (Shodan-style mass scanners) →
free firewall drop
82. Unexpected source ASN / datacenter origin hitting internal-looking paths (lateral/recon signal)
83. Anomalous outbound-error pattern the app emits when an SSRF guard blocks a metadata fetch
> Items A1–M77 are **overwhelmingly config-audit findings** — for almost all of them the Signal is
> "config-audit finding, not traffic-detectable" and the badge is 🔴 (no app signal) or 🟡
> (SecureNow contains the resulting abuse). Only catalog **N** rows have direct app telemetry, and
> even there SSRF-to-metadata (N78) is the standout 🟢/🟡. Be ruthless about not inflating coverage.
**Deferred to sibling models (reference rows — do not re-derive):**
- **Cloud identity / IAM** — over-permissive roles/policies, trust relationships, key sprawl,
privilege escalation paths, the role IMDS hands out → [../20-secrets-and-cloud-iam/](../20-secrets-and-cloud-iam/)
- **Object storage / bucket & object ACLs, data-at-rest exposure, public objects** →
[../23-storage-and-logs/](../23-storage-and-logs/)
- **Serverless / FaaS / edge** (Lambda/Functions/Workers config, function env, edge runtime) →
[../18-serverless-and-edge/](../18-serverless-and-edge/)
> For each deferred area add **one** matrix row marked *"deferred — see linked model"*, note the
> in-scope overlap only (e.g. "IMDS reachable from a pod is modeled here as H50; the IAM role it
> returns is modeled in ../20-"), and do not duplicate the sibling's detection/mitigation.
---
## Phase 3 — Audit the IaC & config (findings only — do not fix, do not deploy)
For **each** modeled threat that maps to real IaC/config, locate the responsible resource
definition and record a **finding** for the report's "Code-level findings" section. A finding is:
- **Location** — `file:line` (clickable), and the **IaC resource** — cite the Terraform resource
address (`aws_security_group.web`), the CloudFormation logical ID, the CDK construct, the
Pulumi resource, the Kubernetes `kind/name`, the Helm chart value path, or the Dockerfile line.
- **Pattern** — quote the 1–8 relevant lines. State the missing/wrong control precisely (e.g.
`cidr_blocks = ["0.0.0.0/0"]` on `from_port = 22`; `publicly_accessible = true` on an
`aws_db_instance`; `storage_encrypted = false`; `privileged: true` in a pod spec; `USER` absent
from a Dockerfile; `terraform.tfstate` tracked in git; CloudTrail resource absent entirely).
- **Why exploitable** — the concrete attack path: who can reach it from where, and what they get
(e.g. "any internet host can open a TCP session to the database and attempt auth / exploit a
known CVE"; "a compromised pod reads IMDS and assumes the node role"; "a public snapshot can be
copied to an attacker account and mounted to read all data").
- **Severity** — critical / high / medium / low (impact × reachability; "open to `0.0.0.0/0` on a
DB port" is critical, "no flow logs" is medium).
- **Recommended fix (described, not applied)** — the specific change in IaC terms: e.g. "restrict
`cidr_blocks` to the bastion SG / corporate CIDR and move the DB to a private subnet"; "set
`publicly_accessible = false` and place behind a private endpoint"; "enable `storage_encrypted`
with a customer-managed KMS key"; "add `securityContext: { runAsNonRoot: true, privileged: false,
allowPrivilegeEscalation: false, readOnlyRootFilesystem: true }` and drop all capabilities";
"add a `USER app` directive and a non-root base"; "enforce IMDSv2 (`http_tokens = required`,
`http_put_response_hop_limit = 1`) and block pod egress to `169.254.169.254` via NetworkPolicy";
"remove `terraform.tfstate` from git, migrate to an encrypted+locked remote backend, rotate any
exposed secrets"; "create the CloudTrail/flow-log resources, multi-region, with log-file
validation"; "add tfsec/checkov/Trivy to CI as a gate". Reference the secure pattern, not a diff.
**You must not edit the IaC or run any cloud/`terraform apply`/`kubectl apply` command.**
If a control exists and is correct (DB in a private subnet, IMDSv2 enforced, encryption on,
NetworkPolicy default-deny, non-root containers, remote encrypted state, CloudTrail multi-region),
note it as a **strength** — the posture must be honest. Absence of a control where the resource
exists is itself a finding ("no NetworkPolicy anywhere → flat cluster network").
Look specifically for, and record findings on:
**Network exposure flaws** — `0.0.0.0/0` / `::/0` ingress on management/DB/admin ports; all-port
rules; default security group in use; unrestricted egress; missing private subnets; DBs/caches in
public subnets. *Fixes must mention* CIDR tightening to bastion/corp ranges, private subnets,
removing public IPs, bastion/SSM-style access instead of open SSH, egress allowlists.
**Public-resource flaws** — `publicly_accessible`, public IP assignment, public snapshots/AMIs,
public registry pulls, internet-facing managed data stores. *Fixes must mention* flipping to
private endpoints, private subnets + bastion/VPN, removing public snapshot/AMI sharing,
authenticated registry access.
**Encryption flaws** — `storage_encrypted = false`, missing KMS, cleartext internal traffic, no
TLS on internal LBs / DB connections. *Fixes must mention* encryption-at-rest with CMKs, key
rotation, TLS for internal/service-to-service traffic, enforced TLS on DB connections.
**Kubernetes / pod flaws** — public API server without authorized ranges; exposed dashboard;
kubelet anonymous auth; cluster-admin / wildcard RBAC; auto-mounted default SA tokens; no
PodSecurity/Gatekeeper/Kyverno; no NetworkPolicy; `privileged`, `hostPath`, `hostNetwork`,
`hostPID`, added caps, root containers, no seccomp, no resource limits; secrets in ConfigMaps.
*Fixes must mention* authorized networks / private API endpoint, removing the dashboard or scoping
its SA, disabling anonymous kubelet, least-privilege RBAC, `automountServiceAccountToken: false`,
PodSecurity `restricted`, default-deny NetworkPolicy, dropping privileged/host* and capabilities,
non-root + seccomp + readOnlyRootFilesystem + limits, sealed/external secrets.
**Container image flaws** — root user, `latest`/unpinned tags, no scan gate, secrets in layers,
oversized images. *Fixes must mention* non-root `USER`, pinned digests, image scanning in CI,
multi-stage builds with no secrets, minimal/distroless base images.
**IMDS / escape flaws** — IMDSv1 enabled, no hop-limit, pods able to reach metadata, escape
primitives (privileged+hostPath). *Fixes must mention* `http_tokens = required`, hop-limit 1,
NetworkPolicy/iptables block of `169.254.169.254` from workloads, removing escape primitives.
**IaC state & secret flaws** — committed `*.tfstate`, local/unencrypted/non-locked backend,
hardcoded secrets in IaC. *Fixes must mention* `.gitignore` for state, encrypted+locked remote
backend (S3+DynamoDB / GCS / azurerm), secret references (SSM/Secrets Manager/Vault), rotating any
leaked secret, and scanning history.
**Audit/logging flaws** — CloudTrail/Activity/Audit logs disabled or single-region; no flow logs;
no LB access logs; no log integrity. *Fixes must mention* enabling multi-region audit logging with
log-file validation, VPC/NSG flow logs, access logs, immutable/centralized log storage (defer the
log-store hardening itself to ../23-).
**IaC scanning gap** — no tfsec/checkov/kics/Trivy/cfn-nag in CI. *Fixes must mention* adding an
IaC-scan gate to the pipeline that fails on high-severity misconfigurations. (Inventory from the
repo — **do not run destructive updates or apply IaC.**)
---
## Phase 4 — Map every modeled threat to SecureNow detection + mitigation
Classify each threat with exactly one coverage badge. **For this domain the realistic distribution
is mostly 🔴 / 🟡** — say so up front in the report.
- 🟢 **COVERED** — detectable + mitigable with SecureNow today on telemetry already flowing. In
this model this is **rare**: essentially **SSRF-to-cloud-metadata** (N78, once the app emits the
event or the request is traffic-visible) and **abuse reaching an app SecureNow fronts** (scanning,
flood, known-bad source — N79–N83).
- 🟡 **PARTIAL** — SecureNow can only **contain the abuser at the edge** (firewall / rate-limit /
challenge / block) while the **real fix is the cloud/IaC config**; or it works only after the
app adds an event (`track('ssrf.blocked')`, `track('cloud.metadata.access_blocked')`); or the
signal is notify-only. Most "abuse reaches the app" rows land here.
- 🔴 **GAP** — SecureNow cannot detect or mitigate this because it has **no view of cloud config**
(every pure posture row: open SG, public DB, privileged pod, disabled CloudTrail, unencrypted
volume, committed state, etc.). **Still include it**: give the **cloud/IaC config fix** (the
primary remediation), 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 config vs. traffic.** SecureNow sees traffic and events and contains actors via
> firewall / rate-limit / challenge / block / signature instant-block. It **cannot** see a
> security-group rule, a `publicly_accessible` flag, an RBAC binding, a `privileged: true`, a
> disabled trail, or an unencrypted volume — those are **config-audit findings, not
> traffic-detectable**, fixed in IaC. On every such row the **cloud/IaC fix is primary**; pair it
> with edge containment only where the exposure leads to abuse that actually reaches the app. A
> pure posture flaw that emits no app traffic and no event is 🔴 until the app emits the relevant
> event (e.g. an SSRF guard emitting `ssrf.blocked`).
Use **only** the SecureNow building blocks below. Never invent CLI flags, event names, or SQL
columns.
### 4a. Instrumentation (what cloud-infra detections feed on)
Be honest: **most cloud posture emits no application telemetry at all.** SecureNow auto-captures
HTTP traffic once the app runs under `securenow run` / `securenow/register` / `securenow init`
(status codes, methods, paths, client IPs, response sizes) — but that only sees **abuse reaching
the app**, not your cloud control plane. The *only* high-value app signal unique to this domain is
the app's own **SSRF / metadata-access guard** firing.
Add `securenow/events` `track()` at the app's enforcement points (never throws):
```js
const { track } = require('securenow/events');
// An outbound URL fetch was blocked by your SSRF guard, targeting cloud metadata — highest signal:
track('ssrf.blocked', { ip, attributes: { route: '/api/fetch', target_host: '169.254.169.254', reason: 'cloud_metadata' } });
// Your app explicitly blocked an attempt to reach the metadata/IMDS endpoint:
track('cloud.metadata.access_blocked', { ip, attributes: { route: '/api/import', target: 'imds|gcp_metadata|azure_imds', reason: 'link_local|metadata_host' } });
// An outbound fetch tried to reach a private/internal/link-local address (egress/SSRF pivot):
track('cloud.egress.blocked', { ip, attributes: { route: '/api/preview', target_host: '10.0.0.5', reason: 'private_range|link_local|internal_service' } });
// Your app detected access to / via an exposed internal service it fronts (recon/lateral signal):
track('cloud.internal.access', { ip, attributes: { service: 'admin-console', reason: 'unexpected_source|public_origin' } });
```
> Hash or omit any PII / secret before it becomes an attribute value (URLs, tokens, internal
> hostnames you consider sensitive). Attributes feed detection; they must not become a new leak
> path. See the Phase 1 telemetry-redaction expectation.
Recommended cloud-infra event taxonomy — rules match these **exact strings**:
| Event | Emit when |
|---|---|
| `ssrf.blocked` | an outbound fetch is denied by an SSRF allowlist/guard (set `target_host`/`reason`) |
| `cloud.metadata.access_blocked` | an attempt to reach IMDS / GCP / Azure metadata is blocked |
| `cloud.egress.blocked` | an outbound fetch to a private/link-local/internal target is blocked |
| `cloud.internal.access` | the app observes access to an internal/exposed service it fronts |
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 scanner / datacenter-origin detection with no extra code.
> Everything else in this domain (open ports, public DBs, RBAC, privileged pods, disabled audit
> logs, unencrypted volumes, committed state) has **no `track()` hook** because the app never sees
> it. Those rows are detected by the **Phase 3 IaC audit**, fixed in IaC, and badged 🔴.
### 4b. Detection rules — SQL conventions
Two query shapes. Both **must** keep the tenant scope and **must** select an `ip` column (per-IP
aggregation is what remediation/auto-block keys on). **The tenant-scope column differs by table** —
using the wrong one fails with `UNKNOWN_IDENTIFIER`:
- **logs/events** (`signoz_logs.distributed_logs_v2`) → `resources_string['service.name'] IN (__USER_APP_KEYS__)`
- **traces/HTTP** (`signoz_traces.distributed_signoz_index_v3`) → `` `resource_string_service$$name` IN (__USER_APP_KEYS__) ``
When grouping by `ip`, add `HAVING ip != '' AND …` so rows with no client IP don't aggregate into
an empty-key bucket. Traffic columns proven available: `response_status_code`, `kind` (server span
= 2), `ts_bucket_start`, `attributes_string['http.target']`, the `client_ip` coalesce below.
Confirm any other column with a `--mode dry_run` before relying on it; OTEL SDK versions vary.
> Reality check: there are **only a handful** of meaningful cloud-infra SQL rules, because almost
> everything in this domain is invisible to traffic. They are: **SSRF/metadata-access** (the
> standout), **scanning/probing of an app-fronted exposed service**, and **known-bad/scanner
> source contact**. Do not author SQL for posture rows (open SG, public DB, RBAC, etc.) — there is
> no telemetry to query; those are IaC fixes.
**Emit every detection as a ready-to-copy command unit.** A detection is never a fragment: for each
of the few traffic/event rules below, emit, in order, four copyable blocks — (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; in the HTML each is a
`.cmd` with a Copy button. The exact create flags must match `securenow alerts rules --help` from
Phase 0.5 — never invent flags. Saving each rule's SQL to `rules/<name>.sql` is what makes
`--sql @rules/<name>.sql` work. Note pre-existing/system rules instead of duplicating them. Example
shape:
````markdown
**Rule: Cloud — SSRF / metadata access attempt** · 🟢 COVERED · N78 · critical
```sql
-- rules/cloud-ssrf-metadata.sql
SELECT attributes_string['http.client_ip'] AS ip, count() AS attempts
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] IN ('ssrf.blocked','cloud.metadata.access_blocked')
AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip HAVING ip != '' AND attempts >= 1
```
```bash
securenow alerts rules create \
--name "Cloud: SSRF / metadata access attempt" \
--sql @rules/cloud-ssrf-metadata.sql \
--apps <APP_KEY> --severity critical --schedule "*/5 * * * *" \
--nlp "any request whose outbound target is 169.254.169.254 or a cloud metadata host"
securenow alerts rules test <RULE_ID> --mode dry_run --wait # validate before it runs live
```
````
> Reminder: **only catalog-N rows get a command unit** — SSRF/metadata, app-fronted
> scanning/probing, flood, known-bad source. Posture rows (open SG, public DB, RBAC, privileged
> pods, disabled logs, committed state) have **no detection rule**; the **cloud/IaC config fix is
> the primary remediation** and lives in the Code Findings report. Do not author SQL for them.
**Events-based — SSRF / cloud-metadata access attempt (catalog N78) — any hit is high-signal:**
```sql
SELECT
attributes_string['http.client_ip'] AS ip,
attributes_string['target_host'] AS target_host,
attributes_string['event.type'] AS signal,
count() AS attempts
FROM signoz_logs.distributed_logs_v2
WHERE resources_string['service.name'] IN (__USER_APP_KEYS__)
AND attributes_string['event.type'] IN ('ssrf.blocked','cloud.metadata.access_blocked','cloud.egress.blocked')
AND timestamp >= now() - INTERVAL 30 MINUTE
GROUP BY ip, target_host, signal
HAVING ip != '' AND attempts >= 1
```
**Traffic-based — metadata-host in the request target (catches SSRF reflected through a path/query
even without an app event; only matches if the app records the outbound target as a span/attr):**
```sql
WITH coalesce(nullIf(attributes_string['http.client_ip'], ''), nullIf(attributes_string['net.peer.ip'], ''), nullIf(attributes_string['network.peer.address'], '')) AS client_ip
SELECT client_ip AS ip,
attributes_string['http.target'] AS target,
count() AS hits
FROM signoz_traces.distributed_signoz_index_v3
WHERE `resource_string_service$$name` IN (__USER_APP_KEYS__)
AND timestamp >= now64(9) - INTERVAL 30 MINUTE
AND ts_bucket_start >= toUInt64(toUnixTimestamp(now() - INTERVAL 30 MINUTE)) - 1800
AND kind = 2
AND (attributes_string['http.target'] LIKE '%169.254.169.254%'
OR attributes_string['http.target'] LIKE '%metadata.google.internal%'
OR attributes_string['http.target'] LIKE '%metadata%latest%meta-data%')
GROUP BY ip, target
HAVING ip != '' AND hits >= 1
```
**Traffic-based — scanning / probing of an app-fronted exposed service (catalog N79; many distinct
paths + client errors from one source):**
```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 — volumetric flood onto an exposed admin/app surface (catalog N80):**
```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
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
```
The remaining catalog-N events follow the **same logs-table shape** — swap the `event.type` filter
and threshold: `cloud.internal.access` (≥1 → unexpected access to a fronted internal service,
notify-only / investigate), `cloud.egress.blocked` (≥1 → egress/SSRF pivot attempt). ASN/datacenter
recon (N82) can be added by also grouping on `client.as_org` and filtering known datacenter orgs.
**Injection / exploit payloads reaching an exposed service** — if SecureNow fronts the exposed
surface, the **system signature rules** (SQLi/XSS/RCE) with synchronous **`instant.block`** apply;
confirm they're enabled via `securenow alerts rules --json` rather than authoring duplicate pattern
SQL.
Create + validate each rule:
```bash
securenow alerts rules create \
--name "Cloud: SSRF / metadata access attempt" \
--sql @rules/cloud-ssrf-metadata.sql \
--apps <APP_KEY> \
--severity critical \
--schedule "*/5 * * * *" \
--nlp "any request whose outbound target is 169.254.169.254 or a cloud metadata host"
securenow alerts rules test <RULE_ID> --mode dry_run --wait
```
#### 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 (scanning
distinct-path / client-error counts, volumetric flood counts, `cloud.internal.access` /
`cloud.egress.blocked` recon volume), broad patterns, anomaly / volume rules, anything tuned to
YOUR traffic — must ship in **`--mode test` first**. Run it detect-only for **3–7 days of real
traffic**, review what it flags, raise/lower the threshold and add `securenow fp` exclusions for
legitimate hits (e.g. your own vuln scanner tripping the scanning rule), then `--mode prod` to arm
mitigation. Only **high-precision** rules — the SSRF/metadata-access event rule (N78, any hit is a
real attempt), exploit-signature SQLi/XSS/RCE matches, exact-match IoCs, known-bad ASN hits — may go
straight to `prod`. In the report, **tag every 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.)
### 4c. The full SecureNow mitigation toolbox (select per threat)
For cloud-infra threats, **the cloud/IaC config change is the real fix** — SecureNow can only
contain a malicious *source that reaches the app*. **For this lowest-native-coverage domain, almost
every row's mitigation is the cloud/IaC config fix (row 15 below)** — the edge mitigations (rows 1–14)
apply only to the handful of threats that surface as **app-fronted abuse** (scanning, flood,
known-bad source) and **SSRF-to-metadata**. For pure posture rows there is **nothing to contain at
the edge**, only the IaC fix (row 15).
Once a threat is confirmed, **choose the narrowest effective mitigation(s) from ALL of these** and
combine them (e.g. rate-limit a fronted admin route + block the worst scanner 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 Shodan-style scanners before they reach any app-fronted exposed service. 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 a payload-borne exploit hitting an app-fronted exposed service. Don't duplicate pattern SQL. |
| 3 | **IP block — global** | `securenow blocklist add <ip> --app <APP_KEY> --env production --reason "..."` | confirmed-malicious scanner source, 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 the sensitive/fronted path; 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 scanner across the fronted app. |
| 7 | **Rate limit — per route (all clients, per-IP budget)** | `securenow ratelimit add --route /admin --mode prefix --method GET --limit 60 --window 1m --key-by ip` | cap an exposed/probed fronted endpoint for everyone, budgeted per IP. |
| 8 | **Rate limit — per route + IP** | `securenow ratelimit add <ip> --route /admin --mode prefix --method ALL --limit 100 --window 1m --duration 24h` · NL `securenow ratelimit from-text "rate limit /admin to 100/min for 24h" --yes` · test `securenow ratelimit test <ip> --path /admin --method GET` | precise throttle of one client on one fronted route. |
| 9 | **CAPTCHA / proof-of-work challenge** | `securenow challenge add --route /admin --difficulty 16 --clearance 30m` (route-wide) **or** `securenow challenge add <ip> --route /admin --difficulty 18 --clearance 30m` · test `securenow challenge test <ip> --path /admin --method GET` | bot scanning of a fronted surface 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 reaching the app; actions include block / rate_limit / requireCaptcha. |
| 11 | **Session revocation** | `securenow revoke …` (SDK `securenow/sessions` `guard()` / `isRevoked()`) | rarely relevant here — only if an exposed admin surface SecureNow fronts is hijacked via a stolen session; kill the session, not the IP. |
| 12 | **Trusted IP (suppress)** | `securenow trusted add <ip> --label "Vuln scanner / monitor / partner"` | stop false positives from your own cloud scanners / monitoring — 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 fronted 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 SSRF/scan rule quiet without weakening it. |
| 15 | **Cloud / IaC / config fix (PRIMARY for this domain — almost every row)** | *IaC or cloud-config change, described in the Code-Findings report — never auto-applied, never `apply`-ed* | the actual fix: close the SG to `0.0.0.0/0`, make the DB private, enforce IMDSv2, drop `privileged`, add NetworkPolicy, enable CloudTrail/flow logs, encrypt volumes, remove committed state, add IaC scanning, SSRF egress allowlist. SecureNow contains a reaching actor; the IaC fix removes the exposure. |
**Choosing per threat** — for cloud-infra, **start from row 15**: the cloud/IaC config fix is the
primary remediation for the overwhelming majority of rows, because a misconfigured security group,
public DB, privileged pod, disabled trail, or unencrypted volume is **not** reachable through
SecureNow and no edge layer touches it. Layer the edge mitigations (rows 1–14) only on the threats
that surface as app traffic/events. By **confidence**: exploit-signature/exact IoC reaching a
fronted service → instant-block or block (rows 2–4); SSRF/metadata attempt → block the source +
fix the egress allowlist; probable bot scanning on shared egress → **challenge** (row 9);
noisy/legit-mixed scan or flood signal → **rate-limit, test-mode first** (rows 6–8); known-good
noise (your own scanners) → **trusted / fp** (rows 12, 14). 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. Recommend **notify-only** for `cloud.internal.access` and
scanner-recon signals (investigate first; they're often your own tooling — add it to `trusted`).
**Always pair an edge mitigation with the cloud/IaC config fix (row 15, Code-Findings report)** —
SecureNow can only contain the actor; the IaC fix removes the exposure. A database open to
`0.0.0.0/0` is **not** behind SecureNow — no edge layer helps; the only fix is the security-group
change (🔴 / IaC).
### 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`). **Never run
scans or `terraform apply` against a real cloud account from this prompt.**
```bash
# Synthetic SSRF / metadata-access event — exercise the highest-value rule end to end:
securenow event send ssrf.blocked --ip 203.0.113.50 \
--attrs route=/api/fetch,target_host=169.254.169.254,reason=cloud_metadata,test=true
securenow event send cloud.metadata.access_blocked --ip 203.0.113.50 \
--attrs route=/api/import,target=imds,reason=link_local,test=true
# Synthetic egress/SSRF-pivot block:
securenow event send cloud.egress.blocked --ip 203.0.113.51 \
--attrs route=/api/preview,target_host=10.0.0.5,reason=private_range,test=true
# Validate a rule query without waiting for the schedule:
securenow alerts rules test <RULE_ID> --mode dry_run --wait
# Traffic-based rules (scanning / flood / metadata-in-target) — generate spans, then inspect:
securenow test-span "threat-model.cloud.smoke"
securenow forensics "requests and 4xx/5xx by IP in the last hour" --env production
# Mitigation verification (only for app-fronted exposed surfaces):
securenow firewall test-ip 203.0.113.50 --app <APP_KEY> --env production
securenow ratelimit test 203.0.113.50 --path /admin --method GET
securenow challenge test 203.0.113.50 --path /admin --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 🟢/🟡 row must have a concrete test recipe (commands + expected outcome: which rule fires,
which notification appears, what the mitigation does). For 🔴 posture rows, the "test" is the
**IaC-scan / manual config check** described in the finding (e.g. "run tfsec; confirm it flags the
`0.0.0.0/0` SG rule") — there is no SecureNow test because there is no SecureNow signal.
---
## Phase 5 — Write the FOUR reports (two tracks × MD + HTML)
Write **four** files into `threat/19-cloud-infrastructure/` — two tracks, each as Markdown **and**
self-contained HTML. The two tracks **cross-link**. For this domain the **Code Findings** report is
the heavyweight (the IaC/config fixes are the real remediation); the **Detection & Mitigation**
report is short (only SSRF-to-metadata + app-fronted abuse are runnable). Record the resolved
installed `securenow` version (Phase 0.5) in **both** appendices.
1. `cloud-infrastructure-detection-mitigation.md` + `.html` — the operational runbook.
2. `cloud-infrastructure-code-findings.md` + `.html` — the code/IaC audit.
### 5a. Detection & Mitigation report — sections (both .md and .html), in order
1. **Executive summary** — stats line (threats modeled · covered · partial · gaps · rules to create
· mitigations), top 3 *detectable* cloud risks, installed `securenow` version + app key +
firewall state, and the one-line honest coverage note: *"This is primarily an audit +
IaC-hardening model; SecureNow natively covers only SSRF-to-metadata and abuse reaching the app —
most rows are config fixes in the Code Findings report."* Plus the framework note (A05:2021 +
Cloud-Native/K8s + CIS).
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 which
system signature rules are present. Note that SecureNow has **no cloud control-plane visibility**.
3. **Threat → Detection → Mitigation matrix** — one row per modeled threat:
`# | Threat | Framework (A05/CN-*/CIS) | Coverage 🟢/🟡/🔴 | Detection rule (or "—" / "config-audit only") | Tag (test-first/prod-ready, "—" for posture rows) | Signal (threshold+window, or "config-audit finding, not traffic-detectable") | Schedule | Sev | Mitigation (cloud/IaC fix primary; SecureNow containment if any)`.
Severity ∈ {critical, high, medium, low}. The **Mitigation cell must pick specific, scoped
mitigation(s) from the 4c toolbox** (by toolbox row/name, e.g. "row 15 IaC fix: enforce IMDSv2 +
row 3 block scanner IP" — never a generic "block the IP"); for almost every posture row this is
**row 15 (cloud/IaC config fix)**, with rows 1–14 added only where the threat reaches an
app SecureNow fronts. The **Tag cell tags each rule `test-first` or `prod-ready`** per the §4b
test-mode rule of thumb (FP-prone heuristic/volume rules → `test-first`; SSRF/metadata,
exploit-signature, exact-IoC → `prod-ready`; posture rows with no rule → "—"). Each 🔴 posture row
**links to its finding** in the Code Findings report. Include the deferred IAM / storage /
serverless rows pointing to the sibling models, then the "Out of scope" N/A list.
4. **Detection rules to create** — each runnable rule (SSRF/metadata, scanning, flood, egress) as the
**ready-to-copy unit** from Phase 4 (SQL → save to `rules/<name>.sql` → full
`securenow alerts rules create …` → dry-run test). **Mark each rule `test-first` or `prod-ready`**:
`prod-ready` rules (SSRF/metadata N78, exploit-signature) go straight to `--mode prod`; every
`test-first` rule (scanning, flood, egress/internal-access volume — the FP-prone heuristics) carries
the explicit promotion step `securenow alerts rules update <RULE_ID> --mode test` → observe 3–7 days
+ tune threshold + add `securenow fp` exclusions → `securenow alerts rules update <RULE_ID> --mode prod`.
Injection-class rows reference the **system signature rules + `instant.block`**, not duplicate SQL.
State explicitly that **posture rows have no detection rule** — they are findings in the Code
Findings report.
5. **Instrumentation the detections need** — only the `track('…')` events the rules above consume
(`ssrf.blocked` / `cloud.metadata.access_blocked` / `cloud.egress.blocked` /
`cloud.internal.access`), each as a copyable snippet; point to the Code Findings report for
*where* (file:line) to add them. Be explicit that cloud control-plane posture is **not** in this
pipeline.
6. **Mitigation mechanisms** — render the **full 15-row toolbox table from Phase 4c verbatim**
(firewall · exploit-signature instant-block · block [global / route+method / temporary] ·
rate-limit [IP / route / IP+route] · challenge · auto-block · revoke · trusted · allowlist · fp ·
**cloud/IaC config fix [primary, row 15]**), followed by the "Choosing per threat" guidance, then a
per-threat ready-to-copy mitigation command + reversibility for each modeled row. Make explicit
that for this domain the cloud/IaC fix (row 15) is primary for almost every row and the SecureNow
edge controls (rows 1–14) are containment only for exposures reachable through an app it fronts.
7. **Action plan (copy-paste, ordered)** — ① engage the firewall + enable signature instant-block
for app-fronted surfaces; ② add the SSRF/metadata app events where the app fetches user URLs
(see Code Findings); ③ create the SecureNow detection rules — **create every FP-prone rule
(scanning, flood, egress/internal-access volume) in `--mode test`** and the high-precision rules
(SSRF/metadata, exploit-signature) in `--mode prod`; ④ enable automations/challenge; ⑤ test;
⑥ verify in dashboard; ⑦ **promote the test-first rules after N days**: an explicit
`securenow alerts rules update <RULE_ID> --mode prod` step for each, gated on "observed 3–7 days of
real traffic, threshold tuned, FP exclusions added"; ⑧ schedule the cloud/IaC config fixes (from
the Code Findings report) — the primary remediation. 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 / 198.51.100 / 203.0.113). For 🔴
posture rows the "test" is the **IaC-scan / manual config check** described in the finding.
9. **Response runbooks** — per notification type (SSRF/metadata attempt, scanning of a fronted
service, flood, known-bad source): what fired, how to confirm a true positive, the exact respond
command (copy), the exact reverse command (copy), **and** the underlying IaC fix to schedule
(link to the Code Findings row) so the exposure goes away.
10. **Known gaps & SecureNow feature requests** — every 🔴 threat (the majority): why it's not
coverable today (no cloud-control-plane visibility), the interim **cloud/IaC 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, and a link to the Code Findings report.
### 5b. Code Findings & Recommendations report — sections (both .md and .html), in order
State at the top: *"Findings only — no IaC, config, application code, or cloud infrastructure was
modified or applied."*
1. **Executive summary** — findings by severity (critical / high / medium / low), top 3 code/IaC
risks for this specific stack, and a one-paragraph posture verdict.
2. **Cloud surface & inventory** — the Phase 1 IaC toolchain summary, network-exposure table,
topology/segmentation note, public-resource list, encryption table, Kubernetes & container
posture, IMDS reachability, audit/flow-logging status, IaC state & secrets status, region/account
sprawl note, and which parts SecureNow can see at all.
3. **Threat catalog** — the exhaustive Phase 2 catalog (groups A–N), each tagged A05/CN-*/CIS-*,
modeled or explicit N/A, including the deferred IAM / storage / serverless rows linking the
sibling models.
4. **Code-level findings (audit — not applied)** — table
`# | Location (file:line / IaC resource) | Threat | Framework | Sev | Issue | Recommended fix`,
each with the quoted 1–8 line snippet and the **described** IaC/config fix (never applied).
5. **Strengths** — controls already present and correct (DB in a private subnet, IMDSv2 enforced,
encryption on, default-deny NetworkPolicy, non-root containers, encrypted+locked remote state,
CloudTrail multi-region) — the posture must be honest.
6. **Cloud / IaC config fixes (primary remediation)** — the config/IaC changes that remove the root
cause (described, not applied): close `0.0.0.0/0` admin/DB ports, make data stores private,
enforce IMDSv2, drop privileged pods, add NetworkPolicy, enable CloudTrail/flow logs, encrypt
volumes, remove committed state, add IaC scanning. Each links back to the detection-report row
(the edge containment) it pairs with.
7. **Instrumentation recommendations** — the `track('…')` calls to add and the exact file:line to
add them (the app's SSRF/metadata-access guard), so the detection rules in the other report light
up.
8. **Appendix** — files reviewed, IaC toolchain & providers/regions audited, resolved SDK version
(Phase 0.5), date, and a link to the Detection & Mitigation report.
### 5c. HTML reports — two self-contained skeletons (offline; inline CSS + copy JS)
Both HTML files are single self-contained pages: inline CSS, an inline copy `<script>`, **no
external fonts / scripts / network**. They **share** the `<head>` (brand tokens + copy-button
styles) and the copy `<script>` at the end of `<body>` below — change only the `<title>`, the
sidebar subtitle, the stat-card labels, and the section content per track. **Wrap EVERY
command/SQL block in 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 styles + copy `<script>` (identical in 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 — Cloud Infrastructure — SecureNow" OR "Code Findings — Cloud Infrastructure — 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>
```
The copy `<script>` (place identically at the very end of `<body>` in **both** files):
```html
<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>
```
**Skeleton 1 — `cloud-infrastructure-detection-mitigation.html` body** (title:
`Detection & Mitigation — Cloud Infrastructure — SecureNow`; one `<section>` per 5a section 1–11;
every command/SQL block wrapped as a `.cmd` with a Copy button):
```html
<body>
<div class="wrap">
<nav>
<div class="brand">Secure<span>Now</span></div>
<div class="sub">Detection & Mitigation · Cloud Infrastructure</div>
<!-- one <a href="#…"> per 5a section (1–11) -->
</nav>
<main>
<header class="top">
<h1>Cloud Infrastructure — Detection & Mitigation</h1>
<p><code><!-- app name / cloud account / domain --></code> · <span class="pill">securenow <!-- installed version --></span> · <span class="pill"><!-- toolchain, e.g. Terraform + EKS, AWS eu-west-3 --></span></p>
</header>
<div class="stats">
<div class="stat"><div class="n"><!-- N --></div><div class="l">threats modeled</div></div>
<div class="stat"><div class="n" style="color:var(--ok)"><!-- N --></div><div class="l">covered</div></div>
<div class="stat"><div class="n" style="color:var(--med)"><!-- N --></div><div class="l">partial</div></div>
<div class="stat"><div class="n" style="color:var(--crit)"><!-- N --></div><div class="l">gaps — config / SecureNow team</div></div>
<div class="stat"><div class="n" style="color:var(--accent)"><!-- N --></div><div class="l">rules to create</div></div>
</div>
<!-- <section id="…"> blocks mirroring the 5a Markdown sections; wrap EVERY command/SQL in .cmd -->
<footer>Generated by the SecureNow cloud-infrastructure threat-model prompt · <!-- date --> · securenow <!-- version --> · app <code><!-- APP_KEY --></code></footer>
</main>
</div>
<!-- copy <script> from above goes here -->
</body>
</html>
```
**Skeleton 2 — `cloud-infrastructure-code-findings.html` body** (title:
`Code Findings — Cloud Infrastructure — SecureNow`; one `<section>` per 5b section 1–8; prose may
omit copy buttons, but any example/fix command is still wrapped in `.cmd`):
```html
<body>
<div class="wrap">
<nav>
<div class="brand">Secure<span>Now</span></div>
<div class="sub">Code Findings · Cloud Infrastructure</div>
<!-- one <a href="#…"> per 5b section (1–8) -->
</nav>
<main>
<header class="top">
<h1>Cloud Infrastructure — Code Findings & Recommendations</h1>
<p><code><!-- app name / cloud account / domain --></code> · <span class="pill">securenow <!-- installed version --></span> · <span class="pill">findings only — nothing modified</span></p>
</header>
<div class="stats">
<div class="stat"><div class="n" style="color:var(--crit)"><!-- N --></div><div class="l">critical</div></div>
<div class="stat"><div class="n" style="color:var(--high)"><!-- N --></div><div class="l">high</div></div>
<div class="stat"><div class="n" style="color:var(--med)"><!-- N --></div><div class="l">medium</div></div>
<div class="stat"><div class="n" style="color:var(--low)"><!-- N --></div><div class="l">low</div></div>
<div class="stat"><div class="n"><!-- N --></div><div class="l">IaC / config findings</div></div>
</div>
<!-- <section id="…"> blocks mirroring the 5b Markdown sections; wrap any fix/example command in .cmd -->
<footer>Generated by the SecureNow cloud-infrastructure threat-model prompt · <!-- date --> · securenow <!-- version --> · app <code><!-- APP_KEY --></code></footer>
</main>
</div>
<!-- copy <script> from above goes here -->
</body>
</html>
```
Every SQL/command block in the **Detection & Mitigation** HTML uses the copyable `.cmd` wrapper:
```html
<div class="cmd"><button class="copy" type="button">Copy</button><pre>securenow alerts rules create \
--name "Cloud: SSRF / metadata access attempt" --sql @rules/cloud-ssrf-metadata.sql \
--apps <APP_KEY> --severity critical --schedule "*/5 * * * *" \
--nlp "any outbound target is 169.254.169.254 or a cloud metadata host"</pre></div>
```
Badge usage: severity → `<span class="b crit|high|med|low">`, coverage →
`<span class="c cov|part|gap">COVERED|PARTIAL|GAP</span>`, framework tag →
`<span class="owasp">A05</span>` / `<span class="owasp">CIS-K8s</span>`, CWE → `<span class="cwe">CWE-16</span>`,
mitigation type → `<span class="m appfix|firewall|signature|rate|challenge|block|notify">` (use
**appfix** for the cloud/IaC config fix — the primary remediation in this model), 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–N83 is either a matrix row or an explicit N/A line; each modeled row carries
its framework tag (A05 / CN-* / CIS-*, or "—").
- The cloud-infra emphasis areas are each modeled or explicit N/A: exposed admin/DB ports;
over-permissive SG/NACL + missing segmentation; public compute/DB/snapshots/AMIs/volumes; missing
private subnets/VPC isolation/egress controls; Kubernetes (API server/dashboard/kubelet, RBAC,
privileged/hostPath/hostNetwork, PodSecurity/NetworkPolicy, default SA tokens); containers (root,
privileged, unscanned, no seccomp/AppArmor); container escape + IMDS-from-pod; IaC misconfig
(public buckets, open SGs, disabled encryption/logging, committed/unencrypted state); missing
encryption-in-transit; disabled audit/flow logs; region/account sprawl + shadow resources.
- Cloud **identity/IAM**, object **storage/bucket ACLs**, and **serverless/edge** are **deferred**
to ../20-secrets-and-cloud-iam/, ../23-storage-and-logs/, and ../18-serverless-and-edge/ (rows
present, linked by numbered path, not re-derived).
- Coverage is **honest and conservative**: this is presented as an audit + IaC-hardening model;
pure posture rows are 🔴 (or 🟡 only where SecureNow contains the resulting app-reaching abuse);
only SSRF-to-metadata (N78) and app-fronted abuse (N79–N83) reach 🟢/🟡 on traffic.
- Every matrix row has a concrete signal **or** the explicit phrase *"config-audit finding, not
traffic-detectable"*, plus severity and the **cloud/IaC fix** — no "monitor for suspicious
activity" filler.
- Every IaC/config finding in section 4 has a `file:line` **and the IaC resource address**, the
quoted snippet, and a described fix — and **no IaC, config, app code, or cloud infrastructure was
modified or applied** (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 and the `HAVING ip != ''`.
- SSRF/metadata detection uses the `ssrf.blocked` / `cloud.metadata.access_blocked` /
`cloud.egress.blocked` events (or the metadata-host-in-target traffic query); exploit payloads
reaching a fronted service reference 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 `ssrf.blocked` / `cloud.*` events).
- The **cloud/IaC config fix is paired as primary** on every applicable row; SecureNow edge
containment is noted only where the exposure is reachable through an app SecureNow fronts.
- Every 🔴 gap appears in the gaps section with an interim **cloud/IaC config fix** **and** the
"contact the SecureNow team" line.
- The action plan runs top-to-bottom with `<APP_KEY>` substituted in, IaC fixes described but never
applied.
- **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 runnable detection rule is a **complete copyable unit** (SQL → `rules/<name>.sql` → full
`securenow alerts rules create …` → dry-run test); flags match `alerts rules --help`. Posture rows
carry **no** detection rule — their fix lives in the Code Findings report.
- **Four** files are written to `threat/19-cloud-infrastructure/` (detection-mitigation .md + .html,
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` + `.copy`).
- The split is honest: SecureNow-runnable detections/mitigations live in the Detection report;
cloud/IaC config fixes (the primary remediation here) live in the Code Findings report; nothing
security-relevant is dropped.
- A one-line summary is printed back per track: file paths, threat counts, rules-to-create count,
code findings by severity, gaps, 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 · **cloud/IaC config fix [primary, row
15]**), 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 (SSRF/metadata,
exploit-signature, exact-IoC) 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 ════════════════ -->