harden — dedicated security review
Look at the system the way an attacker would, then report what a defender must fix. This is
the focused security pass; inspect covers general quality with a security axis, but a
real hardening review deserves its own depth.
Read references/disciplines/security.md — the
shared security reference (STRIDE, OWASP Top 10 + LLM Top 10, secrets, abuse cases). Read
CONVENTIONS.md for the workspace (§1), the ledger (§2), the resume sweep (§5), git isolation (§11), multi-agent
rules (§8), grounding (§14), and freshness (§17 — check current CVEs/advisories against
today's date), and workspace integrity (§20).
Step 1 — Scope & threat model
The iron rule: no finding without a reachable path from an attacker to an asset. A
checklist item that fails in the abstract is not a finding until you can name who reaches it
and what they get. Build the model before auditing, in this order:
- Assets — what is actually worth stealing or breaking here? Credentials, personal data,
money movement, the ability to act as another user, availability. Rank them; the audit's
attention follows this list, not the checklist's order.
- Entry points — every place untrusted input enters: routes and handlers, webhooks,
queue consumers, file/image uploads, third-party callbacks, admin surfaces, CLI flags,
environment and config. The API is the surface, not the UI — enumerate endpoints from
the router, never from the screens.
- Actors and their starting privilege — anonymous · self-registered user · another
tenant's user · low-privilege staff · compromised dependency · someone with a stolen
session. For each finding later, you will name which of these can reach it.
- Trust boundaries — draw the lines where data crosses from one privilege level to
another. Every boundary is a place validation and authorization must exist; missing checks
cluster there.
- STRIDE per boundary — spoofing · tampering · repudiation · information disclosure ·
denial of service · elevation of privilege. Walk the six against each boundary and keep
what is concrete for this system.
Load security.md for the checklist that the audit works through.
Step 2 — Audit (read-only)
Work the checklist against the code and config:
- Access control — authz on every route/action, deny-by-default, no IDOR.
- Injection & input — parameterized queries, boundary validation.
- Crypto & secrets — TLS, encryption at rest, strong hashing; no secrets in code/logs
(scan the diff).
- Auth & sessions — rate-limited login, safe session/JWT handling.
- Dependencies/supply chain — known-vulnerable packages (web-check current advisories, §17).
- SSRF, deserialization, misconfig, debug endpoints.
- LLM features — prompt injection, unsafe tool permissions, data leakage.
In multi-agent mode, dispatch parallel auditors by category and merge; the orchestrator owns
writes (§8). Single-agent: work the checklist inline with fresh eyes.
These thoughts mean stop — you are about to dismiss a real finding:
| The thought |
The reality |
| "It's internal-only, not exposed" |
Internal networks get breached. Assume the attacker is already inside. |
| "You'd have to be logged in to hit it" |
Registration is usually free. Authenticated ≠ trusted. |
| "Another tenant couldn't reach this" |
That is the claim to test, not assume — it is the definition of IDOR. |
| "No one would think to try that" |
Attackers do this full-time and share notes. |
| "It's validated on the client" |
Client validation is UX. The server is the boundary. |
| "That endpoint is old, nobody uses it" |
Unused, reachable, and unmaintained is the ideal target. |
| "The framework probably handles it" |
Probably is not a control. Read the config and confirm. |
Dismissing a finding is a decision with the same weight as raising one — it needs the same
evidence.
Step 3 — Report
Write security-review.md in the task folder (bootstrap per §1), ranked by severity:
# Security review — <target> · <date>
### Critical — exploitable now: <boundary violated> → <impact> → <fix>
### High — likely exploitable / sensitive-data exposure
### Medium — defense-in-depth gaps
### Info — hardening suggestions
Calibrate severity by reachability × impact, not by how alarming the category sounds:
| Rank |
Who can reach it |
What they get |
| Critical |
anonymous, or any self-registered user |
auth bypass · RCE · another tenant's data · money movement · mass data exposure |
| High |
authenticated, or a plausible precondition |
a single user's sensitive data · privilege escalation within a tenant · destructive action |
| Medium |
needs an unlikely chain, or is a missing layer behind a working control |
defense-in-depth gap · information leak that aids a bigger attack |
| Info |
not reachable today |
hardening that prevents a future mistake |
A category does not set the rank — a hardcoded key in a repo nobody can read is not Critical,
and a missing authorization check on one route is not Info.
A finding is valid only with a violated boundary + demonstrated impact + a concrete fix,
and each one names which actor reaches it (Step 1) — no speculative "could be risky".
Never invent a CVE or a vulnerability — verify and cite, or label it a hypothesis to confirm
(§14). No secret is ever written into the report: name the file and line, never the
value, and if a live credential is found say so plainly and treat rotation as part of the
fix — a leaked secret that was committed is already public to anyone with repo history.
Step 3b — Variant analysis (report the class, not the instance)
Every confirmed finding triggers a hunt: search the whole codebase for the same pattern
(same sink, same missing check, same unsafe construct) before closing. One SQL-injection
finding usually means several; fixing the instance while its siblings ship is a false
sense of security. Report the class with all its locations.
Step 4 — Close the loop
Write security-review.md to disk and confirm it is non-empty (§20.2) before touching the
ledger — then record a harden entry in state.json.phases — approved: true on a clean pass, or
waived: true only on the user's explicit acceptance of open findings (§2) — so a resumed
run re-proves the security gate instead of trusting it. Critical/High must be fixed (or
defensibly, explicitly accepted by the user) before ship.
Route fixes through construct → verify, then re-audit the changed part. Record accepted
risks in decisions.md.
Composition
- Consumes: the code/diff/config,
security.md, project memory, current advisories (web).
- Produces:
security-review.md; fixes routed to construct/verify.
- Receives from:
engineer (auto-scheduled when a change touches auth, PII, payments,
secrets, or a new public surface — a rule, not luck) or a direct user request.
- Hands off to:
construct (fixes), release (blocks ship on unresolved Critical).
Distinct from inspect (general code review) and assess (whole-app feature health).
Self-review (author's notes)
- Mis-routed?
engineer routes here when a change touches auth, secrets, payments, or
untrusted input; wrong for general quality review (inspect) or app health (assess). Pick
this over inspect when the threat model, not the diff, is the question.
- Single-agent safe? Yes — the checklist runs inline; parallel auditors are optional.
- Leaks specifics? No — checklist is framework-neutral; findings cite real boundaries.
- Grounded? CVEs/advisories web-checked with today's date; nothing invented (§14, §17).
1---2name: harden3description: Security audit: threat model, OWASP Top 10 (and LLM Top 10), abuse cases, dependency and secret scan, with ranked findings and concrete fixes. Read-only.4---56# harden — dedicated security review78Look at the system the way an attacker would, then report what a defender must fix. This is9the focused security pass; `inspect` covers general quality with a security *axis*, but a10real hardening review deserves its own depth.1112Read [references/disciplines/security.md](../../references/disciplines/security.md) — the13shared security reference (STRIDE, OWASP Top 10 + LLM Top 10, secrets, abuse cases). Read14[CONVENTIONS.md](../../CONVENTIONS.md) for the workspace (§1), the ledger (§2), the resume sweep (§5), git isolation (§11), multi-agent15rules (§8), grounding (§14), and freshness (§17 — check current CVEs/advisories against16today's date), and workspace integrity (§20).1718## Step 1 — Scope & threat model1920**The iron rule: no finding without a reachable path from an attacker to an asset.** A21checklist item that fails in the abstract is not a finding until you can name who reaches it22and what they get. Build the model before auditing, in this order:23241. **Assets** — what is actually worth stealing or breaking here? Credentials, personal data,25 money movement, the ability to act as another user, availability. Rank them; the audit's26 attention follows this list, not the checklist's order.272. **Entry points** — every place untrusted input enters: routes and handlers, webhooks,28 queue consumers, file/image uploads, third-party callbacks, admin surfaces, CLI flags,29 environment and config. **The API is the surface, not the UI** — enumerate endpoints from30 the router, never from the screens.313. **Actors and their starting privilege** — anonymous · self-registered user · another32 tenant's user · low-privilege staff · compromised dependency · someone with a stolen33 session. For each finding later, you will name which of these can reach it.344. **Trust boundaries** — draw the lines where data crosses from one privilege level to35 another. Every boundary is a place validation and authorization must exist; missing checks36 cluster there.375. **STRIDE per boundary** — spoofing · tampering · repudiation · information disclosure ·38 denial of service · elevation of privilege. Walk the six against each boundary and keep39 what is concrete for *this* system.4041Load `security.md` for the checklist that the audit works through.4243## Step 2 — Audit (read-only)44Work the checklist against the code and config:45- **Access control** — authz on every route/action, deny-by-default, no IDOR.46- **Injection & input** — parameterized queries, boundary validation.47- **Crypto & secrets** — TLS, encryption at rest, strong hashing; no secrets in code/logs48 (scan the diff).49- **Auth & sessions** — rate-limited login, safe session/JWT handling.50- **Dependencies/supply chain** — known-vulnerable packages (web-check current advisories, §17).51- **SSRF, deserialization, misconfig, debug endpoints.**52- **LLM features** — prompt injection, unsafe tool permissions, data leakage.5354In multi-agent mode, dispatch parallel auditors by category and merge; the orchestrator owns55writes (§8). Single-agent: work the checklist inline with fresh eyes.5657**These thoughts mean stop — you are about to dismiss a real finding:**5859| The thought | The reality |60|---|---|61| "It's internal-only, not exposed" | Internal networks get breached. Assume the attacker is already inside. |62| "You'd have to be logged in to hit it" | Registration is usually free. Authenticated ≠ trusted. |63| "Another tenant couldn't reach this" | That is the claim to *test*, not assume — it is the definition of IDOR. |64| "No one would think to try that" | Attackers do this full-time and share notes. |65| "It's validated on the client" | Client validation is UX. The server is the boundary. |66| "That endpoint is old, nobody uses it" | Unused, reachable, and unmaintained is the ideal target. |67| "The framework probably handles it" | Probably is not a control. Read the config and confirm. |6869Dismissing a finding is a decision with the same weight as raising one — it needs the same70evidence.7172## Step 3 — Report73Write `security-review.md` in the task folder (bootstrap per §1), ranked by severity:7475```76# Security review — <target> · <date>77### Critical — exploitable now: <boundary violated> → <impact> → <fix>78### High — likely exploitable / sensitive-data exposure79### Medium — defense-in-depth gaps80### Info — hardening suggestions81```8283**Calibrate severity by reachability × impact, not by how alarming the category sounds:**8485| Rank | Who can reach it | What they get |86|---|---|---|87| **Critical** | anonymous, or any self-registered user | auth bypass · RCE · another tenant's data · money movement · mass data exposure |88| **High** | authenticated, or a plausible precondition | a single user's sensitive data · privilege escalation within a tenant · destructive action |89| **Medium** | needs an unlikely chain, or is a missing layer behind a working control | defense-in-depth gap · information leak that aids a bigger attack |90| **Info** | not reachable today | hardening that prevents a future mistake |9192A category does not set the rank — a hardcoded key in a repo nobody can read is not Critical,93and a missing authorization check on one route is not Info.9495A finding is valid only with a **violated boundary + demonstrated impact + a concrete fix**,96and each one names **which actor reaches it** (Step 1) — no speculative "could be risky".97Never invent a CVE or a vulnerability — verify and cite, or label it a hypothesis to confirm98(§14). **No secret is ever written into the report**: name the file and line, never the99value, and if a live credential is found say so plainly and treat rotation as part of the100fix — a leaked secret that was committed is already public to anyone with repo history.101102## Step 3b — Variant analysis (report the class, not the instance)103104Every confirmed finding triggers a hunt: search the whole codebase for the **same pattern**105(same sink, same missing check, same unsafe construct) before closing. One SQL-injection106finding usually means several; fixing the instance while its siblings ship is a false107sense of security. Report the class with all its locations.108109## Step 4 — Close the loop110Write `security-review.md` to disk and confirm it is non-empty (§20.2) **before** touching the111ledger — then record a `harden` entry in `state.json.phases` — `approved: true` on a clean pass, or112`waived: true` only on the user's explicit acceptance of open findings (§2) — so a resumed113run re-proves the security gate instead of trusting it. Critical/High must be fixed (or114defensibly, explicitly accepted by the user) before ship.115Route fixes through `construct` → `verify`, then re-audit the changed part. Record accepted116risks in `decisions.md`.117118## Composition119- **Consumes:** the code/diff/config, `security.md`, project memory, current advisories (web).120- **Produces:** `security-review.md`; fixes routed to construct/verify.121- **Receives from:** `engineer` (auto-scheduled when a change touches auth, PII, payments,122 secrets, or a new public surface — a rule, not luck) or a direct user request.123- **Hands off to:** `construct` (fixes), `release` (blocks ship on unresolved Critical).124 Distinct from `inspect` (general code review) and `assess` (whole-app feature health).125126## Self-review (author's notes)127- *Mis-routed?* `engineer` routes here when a change touches auth, secrets, payments, or128 untrusted input; wrong for general quality review (`inspect`) or app health (`assess`). Pick129 this over `inspect` when the threat model, not the diff, is the question.130- *Single-agent safe?* Yes — the checklist runs inline; parallel auditors are optional.131- *Leaks specifics?* No — checklist is framework-neutral; findings cite real boundaries.132- *Grounded?* CVEs/advisories web-checked with today's date; nothing invented (§14, §17).