executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
hardenSecurity audit: threat model, OWASP Top 10 (and LLM Top 10), abuse cases, dependency and secret scan, with ranked findings and concrete fixes. Read-only.
Independent GitHub snapshot · Automated classification · Original documentation governs
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), judgment (§4.1), 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).
Journal every run (§2.1). The first write of the run, once the task folder is resolved
(§20.1), is a START entry in the task's log.md; then set this phase in_progress in
state.json. Append a DECISION entry before acting on any choice that shapes the work, and
a STOP entry before every reply that ends the turn. Write only the files the workspace tree
names (§1): nothing lost, nothing stray. Before asking a question or deciding, check the
user's judgment (§4.1) for a confirmed rule that covers it, and journal who decided.
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:
Load security.md for the checklist that the audit works through.
Work the checklist against the code and config:
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: model a network-internal attacker too. But rank by who can reach it, verified, not by the worst case imagined. |
| "You'd have to be logged in to hit it" | Check whether anyone can register (the signup route or its config, cited). Authenticated ≠ trusted either way. |
| "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.
Write security-review.md in the task folder (bootstrap per §1), ranked by severity:
# Security review — <target> · <date>
### Coverage — entry points reviewed (file:line), and for each checklist item the read or
search that cleared it (absence claims show their searches, §5.2)
### Critical — exploitable now: <boundary violated> → <impact> → <fix>
### High — exploitable given a verified precondition / sensitive-data exposure
### Medium — defense-in-depth gaps
### Info — hardening suggestions
Every finding carries its evidence: a source-to-sink trace (where untrusted input
enters, file:line, through each step, to the dangerous operation, file:line) and its
reachability, verified (the route, the auth guard or its absence, the signup config,
cited). That is enough to rank it: harden reads and does not attack, so a traced,
reachable hole is ranked by the table above even though nothing was fired. A path with a step
you could not trace is labelled a hypothesis, and says which step is missing; unverified
reachability takes the lower rank, labelled unconfirmed. A committed secret is ranked by
what it grants, never "reproduced" by using it: when the grants cannot be confirmed without
using it, rank by what its name and location indicate (a key in prod.env grants production
access), labelled unconfirmed, and let the user confirm.
Calibrate severity by reachability × impact, not by how alarming the category sounds. When the two columns point at different rows (reachable by anyone, but the impact reads like the High row), take the more severe row and say which column set it:
| 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 precondition shown to hold | 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.
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.
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
that has a Coverage section (no coverage, no pass: a clean result is a set of absence
claims, and each needs its search), 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.
List the findings and let the user choose, per finding: fix now, accept (with its risk
recorded in decisions.md), or a follow-up. For a Critical or High, a follow-up still
ships the hole, so it is acceptance: say so, and record it only on the user's explicit yes as
waived: true, with the risk in decisions.md. A plain follow-up is for Medium and Info. Variant analysis can surface a whole class of
siblings; fixing all of them grows the task, so that is the user's call (§4.1, §4.2). Chosen
fixes go through construct → verify, and a finding is resolved only on evidence
(§5.1), not because the code changed: verify writes and runs a reproduction (a request or
failing test) that worked before the fix and fails after it. A leaked secret is resolved by
the user confirming it was rotated, and its removal from history is a separate decision.
security.md, project memory, current advisories (web).security-review.md; fixes routed to construct/verify.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.construct (fixes), release (blocks ship on unresolved Critical).
Distinct from inspect (general code review) and assess (whole-app feature health).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.