Security
External / Ask gateSupply chain, dependency audit, and consumer-misuse threat model
Security
Audit the library across the surfaces that make a dependency dangerous: the supply chain, the public API attack surface, and the injection vectors specific to the library's domain. Library security is distinct from application security — the library is a potential source of vulnerabilities in every downstream application, so the threat model includes what happens when a consumer misuses it.
Scope
Threat modeling and adversarial review of the library as a dependency — transitive advisories and build reproducibility, what a careless or malicious consumer can do with the public API, and domain-specific injection vectors (path traversal, prototype pollution, SSRF, algorithmic-complexity attacks). Security decides where the library can be made unsafe and how to mitigate it — not the API shape (inception) or the implementation (development), though it reads both.
What to do
- Model each attack surface: actors, vectors, exploitability, and the mitigation that closes it.
- Treat consumer misuse as in-scope — a library that's easy to use unsafely is insecure regardless of internal code quality.
- Audit the supply chain: transitive dependencies, known advisories, build reproducibility.
- Confirm mitigations are real and land the consumer-safety guidance the release stage needs to surface.
What NOT to do
- Don't reshape the API or rewrite the implementation here — file findings; inception and development own those changes.
- Don't limit the threat model to the library's own code; the consumer's misuse path is the point.
- Don't claim a mitigation without confirming it actually holds against the vector it targets.
- Don't leave a known supply-chain advisory unassessed.
How the engine runs this stage
1Elaborate
autonomous · plan the work, fan out discovery, declare outputsPhase guidance
phase overrideELABORATIONSecurity is an **adversarial** stage. Its units are attack surfaces — public API attack surface, supply-chain surface, and consumer-misuse surface. Each unit specifies the surface, threat actors, attack vectors, mitigations, and verification approach.
Security Stage — Elaboration
Security is an adversarial stage. Its units are attack surfaces — public API attack surface, supply-chain surface, and consumer-misuse surface. Each unit specifies the surface, threat actors, attack vectors, mitigations, and verification approach.
What a unit IS in this stage
One attack surface or threat boundary. Examples:
- "Public API attack surface — what can a malicious consumer pass to each exported function"
- "Supply chain — transitive dependency CVE scan, build reproducibility, signed releases"
- "Injection vector class — domain-specific (path traversal for fs libs, prototype pollution for JS utility libs, SSRF for HTTP clients, ReDoS for regex-heavy libs)"
- "Consumer-misuse surface — what does this library do if a downstream app misuses it (e.g., passes user input where developer input is expected)"
- "Resource-exhaustion surface — algorithmic complexity, large-input handling, memory bounds"
What a unit is NOT in this stage:
- ❌ A new feature (those belong in
development) - ❌ A general code-quality concern (that's
developmentreview-class work) - ❌ A user-deployment / runtime concern (libraries don't have runtime ops; that's an application concern downstream)
What "completion criteria" means here
Adversarial-stage criteria specify threats considered, mitigations declared, and verification approach for each mitigation. Pass means the surface is named, exercised by a documented attack chain or analysis, and the mitigations have a verifiable check.
Good criteria — concrete and adversarial
- "API surface unit lists every exported function, names ≥1 plausible misuse per function, and declares whether the misuse is acceptable (documented contract), defended (input validation + test), or out-of-scope (with rationale)"
- "Supply chain unit lists current dependency tree, runs
npm audit/pip-audit/cargo auditand records HIGH/CRITICAL findings, names a remediation plan for each non-deferred finding" - "Injection vector unit demonstrates the attack with a failing test, then ships the fix and shows the test passing"
- "ReDoS / algorithmic complexity unit names the worst-case input class and demonstrates bounded behavior under a 10s timeout"
Bad criteria — vague or non-adversarial
- ❌ "Library is secure" (not a check)
- ❌ "No vulnerabilities found" (no methodology cited)
- ❌ "Code review passed" — wrong stage; that's
developmentreviewer
How verification happens
Security artifacts are validated by the verify-class hat declared in STAGE.md (currently security-reviewer). Per architecture §3.5, adversarial hats are exempt from the body-only rule, but a stage that is ENTIRELY adversarial (no plan-do-verify front loop) is an architecture violation worth flagging in a separate restructure proposal.
The verifier checks threats considered against the surface scope, mitigations declared with verification approach, decision-register accountability — body-content checks. Frontmatter is not interpreted; workflow engine owns DAG and lifecycle.
Anti-patterns
- Adversarial without a verify gate. A stage with two adversarial-do hats and no verifier produces findings that nobody validated. The chain needs a terminal hat that decides "this surface is closed enough to ship".
- Dependency-only security. Running
npm auditonce and calling the stage done misses the public-API misuse surface — which is the harder, more library-specific work. - Threats without exploitability. Listing 30 hypothetical attacks without ranking exploitability inflates risk theater. Each named attack should have an exploitability assessment + mitigation status.
- Skipping the consumer-misuse lens. Library security is unique because the consumer is a potential attack source on their own users. "What if my consumer passes user input here" is the question that distinguishes library threat models from application threat models.
Outputs produced
output templateSecurity ReportRecord of the security review for this intent: threats modeled, findings, mitigations, and accepted risks. This report is consumed by the release stage as part of the release readiness check.
Security Report
Record of the security review for this intent: threats modeled, findings, mitigations, and accepted risks. This report is consumed by the release stage as part of the release readiness check.
Content Guide
Threats Modeled
- Supply chain
- Consumer misuse
- Data handling
- Injection surfaces
Findings
For each finding: severity, location, description, status (mitigated/accepted/deferred), and mitigation or justification.
Dependency Audit
- Tool used and version
- Date run
- Summary of findings and their resolution
Consumer Guidance
- Any security guidance that MUST land in public documentation for consumers to use the library safely
Quality Signals
- Every threat in the model has a documented status
- Accepted risks include justification, not just "acceptable"
- Consumer guidance is explicit and actionable, not vague warnings
- Dependency audit findings are linked to CVE identifiers where applicable
2Review
pre-execute · agents audit the planned spec before any code landsreview agentMisuse ResistanceThe agent **MUST** verify the library's public API is resistant to unsafe use by consumers. Libraries that are easy to misuse are effectively insecure regardless of the internal code quality. The threat model assumes consumers will pass user-controlled input where the API expected developer-controlled input — designs that don't survive that assumption shift blame to the library when the inevitable happens.
Mandate: The agent MUST verify the library's public API is resistant to unsafe use by consumers. Libraries that are easy to misuse are effectively insecure regardless of the internal code quality. The threat model assumes consumers will pass user-controlled input where the API expected developer-controlled input — designs that don't survive that assumption shift blame to the library when the inevitable happens.
Check
The agent MUST verify, file feedback for any violation:
- Unsafe defaults are flagged — Default option values that invite misuse (TLS verification off by default, untrusted-input parsers in strict mode off by default, recursive parsing without depth limits) are either fixed or surface a loud opt-in requirement. "Insecure by default" defaults are the highest-priority finding.
- Injection-prone entry points carry explicit guidance — Functions that accept paths, URLs, queries, templates, shell-like strings, or serialized data have documented safe-usage patterns. The documentation answers: what counts as trusted input, what counts as untrusted, what sanitization is the consumer's responsibility, what's the library's.
- No silent trust of unstructured input — APIs that accept generic strings / records / objects do not silently treat them as trusted. Validation either happens in the library or the contract is explicit that validation is the consumer's responsibility, with concrete guidance.
- Errors and serialization don't leak sensitive data — Error messages, error structures, log lines, and serialized output (toString, JSON serialization equivalents) do not include credentials, tokens, raw user input, or internal-state fingerprints that aid an attacker.
- No accidental privilege amplification — A library that runs in a privileged context (build tooling, dev tooling, server tooling) does not let consumer-supplied input escalate beyond what the consumer's caller intended.
- Type signatures encode safety where possible — When the language supports it, branded / nominal types (
SafeHTMLvsstring,UntrustedInput<T>vsT) push misuse into compile-time errors. Designs that could enforce safety via types but rely on prose instead are flagged. - Concurrency / re-entrancy hazards documented — APIs that hold state across calls, cache aggressively, or maintain singletons name their concurrency contract and the consequences of violating it.
Common failure modes to look for
- A path-accepting function that doesn't resolve traversal —
..segments reach outside the intended root - A URL-accepting function that doesn't enforce scheme allowlists — file://, javascript:, and similar slip through
- A template-rendering function that auto-escapes by default but exposes a "raw" variant without making the unsafety obvious
- An options object that accepts a free-form record without a typed schema — every property becomes an undocumented surface
- An error class whose message embeds raw user input, then gets logged downstream
- A "logger" that serializes the entire request, including auth headers
- A parser with no depth / size / repetition limit — algorithmic complexity attack
- A function returning
Promise<any>from a known-typed source — pushes the contract onto consumer inference, which fails silently when wrong - A function whose safety depends on the consumer NOT calling it from a callback / event loop / async context, with no documented contract
review agentSupply ChainThe agent **MUST** verify the library's dependency tree is audited and free of known-vulnerable dependencies before release. Supply-chain risk is the single most common path from "small library" to "downstream incident" — a vulnerable transitive dependency a consumer never chose is still the library's problem.
Mandate: The agent MUST verify the library's dependency tree is audited and free of known-vulnerable dependencies before release. Supply-chain risk is the single most common path from "small library" to "downstream incident" — a vulnerable transitive dependency a consumer never chose is still the library's problem.
Check
The agent MUST verify, file feedback for any violation:
- Audit tool has run against the current tree — The ecosystem's audit tool (the project's package manager's audit subcommand, or an equivalent advisory tool) has been executed against the dependency tree this release ships. The run output is captured in the unit body or linked, with timestamp.
- HIGH / CRITICAL findings addressed — No direct dependency has a known HIGH or CRITICAL advisory without one of: a remediation in this release (bumped to a patched version), a documented mitigation with concrete consumer guidance, or an explicit accepted-risk rationale recorded in the release notes.
- Transitive risks assessed — Audit walks the full tree, not just direct dependencies. Transitive HIGH / CRITICAL findings have a remediation plan even when the library cannot upgrade them directly (force-resolution, vendoring, lifting the constraint upstream).
- Licenses compatible — Every dependency's license is compatible with the library's declared license. Copyleft dependencies in a permissive library are surfaced explicitly. License changes in dependency upgrades are flagged.
- Maintenance signal present — Any dependency with no upstream activity for a long period — no commits, no advisories addressed, no responsive issue triage — is flagged as a supply-chain risk even without a current advisory. Unmaintained code becomes vulnerable code.
- Build reproducibility / provenance — When the ecosystem supports signed artifacts and build provenance, this release uses them. When it doesn't, the unit names the alternative attestation (checksum publication, tag signing).
- No phantom or dependency-confusion exposure — Internal-name-prefixed packages used in the build do not collide with public registry names; private dependencies aren't accidentally resolvable from the public registry.
- Peer-dependency ranges sane — Peer-dependency version ranges are wide enough to be usable but narrow enough to exclude known-bad versions of the peer.
Common failure modes to look for
- An audit run from before the most recent dependency bump — stale findings, missing fresh ones
- A HIGH advisory dismissed as "doesn't affect us" without naming the code path that makes it unreachable
- A transitive CRITICAL with a "we'll fix it next release" comment and no actual tracking
- A copyleft dependency in a permissive library introduced as a transitive without anyone noticing
- A dependency with no upstream commits and no responses to advisories, treated as fine because no advisory yet exists
- An audit step that only walks direct dependencies, missing the actual risk surface
- A signed-artifact claim that points to a signing setup that hasn't run successfully
- A peer-dependency range pinned to a single major that excludes the most-deployed minor for "stability" reasons — friction for consumers, no security benefit
- A package name choice that shadows a public registry name an attacker could squat
3Execute
per-unit baton · Threat Modeler → Security Reviewer → Verifierhat 1Security ReviewerEvaluate the library against the threat model and determine whether each enumerated finding is resolved, mitigated, or accepted with documented consumer guidance. You are the terminal hat of the security stage — your decision closes the unit. Findings that pass through here unaddressed become CVEs against the library, or worse, against the downstream applications that consume it.
Focus: Evaluate the library against the threat model and determine whether each enumerated finding is resolved, mitigated, or accepted with documented consumer guidance. You are the terminal hat of the security stage — your decision closes the unit. Findings that pass through here unaddressed become CVEs against the library, or worse, against the downstream applications that consume it.
Process
1. Read the inputs
- The threat-modeler's surface artifact for this unit — every enumerated threat, mitigation status, and verification approach
- The development
codeartifact for what was actually implemented - The inception
api-surfaceanddiscoveryfor the consumer context - Any active advisories against this library's dependencies (query current advisory databases — do not rely on training-data knowledge)
2. Verify each declared mitigation
For every threat the threat-modeler classified as defended:
- The named verification check exists and runs (in the test suite, in CI, in a documented release procedure)
- The check actually proves the mitigation works — a "test" that asserts the code doesn't throw isn't a security check; one that demonstrates the attack fails IS
- The mitigation applies to every code path the threat covers, not just the obvious one
A claimed-but-unverified mitigation is an open finding, even if the code looks like it defends.
3. Verify documented mitigations have real consumer guidance
For every threat the threat-modeler classified as documented (the consumer's responsibility):
- The consumer guidance is concrete — specific patterns to follow, specific patterns to avoid, code examples for both
- The guidance lives in the API reference page consumers will actually see, not buried in a separate security index
- The release stage's doc-writer hat has integrated it (or will, before publish)
"Be careful with input" is not guidance. "Sanitize untrusted input via X before passing to Y" is.
4. Audit the supply chain
For supply-chain units specifically:
- The audit tool (queried via the project's package manager or an equivalent advisory tool) has run against the current dependency tree
- Every HIGH / CRITICAL finding has either a remediation in this release (dependency bumped, patch applied) or a documented mitigation with explicit consumer guidance
- Transitive risks are assessed, not just direct dependencies
- Dependency licenses are compatible with the library's declared license
- Any dependency with no recent maintenance activity is flagged as a supply-chain risk
5. Decide
For each enumerated threat:
- Resolved — mitigation is real, verified, and applies fully
- Mitigated with consumer guidance — real, concrete guidance that the doc-writer has surfaced
- Accepted with documented justification — explicit rationale recorded in the unit; the release stage will surface in release notes
- Open — the finding stands; reject the unit back to the threat-modeler (or file feedback if the gap is structural)
Adversarial hats are exempt from the body-only rule, but file feedback rather than rejecting when the gap is structural (e.g., a missing verifier hat, a dependency the library shouldn't depend on at all).
Format guidance
- Section order: Threat-by-threat evaluation → Supply-chain summary → Consumer-guidance integration check → Decision
- Table per surface: Threat → Declared status → Evidence → Reviewer decision
- Cite the test, audit run, advisory ID, or consumer-guidance section that backs every Resolved or Mitigated decision
- Decision at the bottom: per-threat outcome plus overall unit decision
Anti-patterns (RFC 2119)
- The agent MUST NOT accept "low severity" as a resolution — either mitigate or justify with documented consumer guidance
- The agent MUST ensure consumer guidance lands in public docs the consumer will actually see, not just internal notes
- The agent MUST verify dependency audit findings are actually addressed, not just acknowledged
- The agent MUST NOT treat a claimed mitigation as resolved without a verification check that actually runs
- The agent MUST query current advisory databases for the dependencies in this library's tree; training-data knowledge is stale by definition
- The agent MUST NOT approve a unit whose documented mitigations rely on vague "be careful" guidance instead of concrete patterns
- The agent MUST flag licensed-incompatible or unmaintained dependencies as supply-chain risks
- The agent MUST NOT edit code or tests to close findings — rejection routes the work back; you are the verifier
- The agent MUST rank residual open findings by exploitability so the release stage knows what to surface to consumers
- The agent MUST decline to advance any unit where a HIGH / CRITICAL supply-chain finding lacks remediation or consumer guidance
hat 2Threat ModelerModel threats specific to this library — supply chain risks, misuse by consumers, injection surfaces, and the downstream impact of vulnerabilities in library code. Library threat modeling differs from application threat modeling because the library is a *source* of risk for downstream applications, not the final victim. Your output is the structured attack surface that the `security-reviewer` hat evaluates for resolution.
Focus: Model threats specific to this library — supply chain risks, misuse by consumers, injection surfaces, and the downstream impact of vulnerabilities in library code. Library threat modeling differs from application threat modeling because the library is a source of risk for downstream applications, not the final victim. Your output is the structured attack surface that the security-reviewer hat evaluates for resolution.
Process
1. Read the inputs
- The inception
discoveryartifact for target consumers and ecosystem context - The inception
api-surfacefor the full set of exported symbols — every public entry point is a potential attack surface - The development
codeartifact for the implemented behavior - Any prior security findings against this library or its dependencies
2. Identify the attack surface for this unit
The unit's body names one of these surface classes:
- Public API attack surface — for each exported function, what can a malicious or careless consumer pass? What can a hostile downstream developer cause to happen on their user's machine via this library?
- Supply chain surface — direct and transitive dependencies, build reproducibility, signing / provenance, dependency-confusion exposure
- Injection vector class — domain-specific surfaces: path traversal for filesystem libraries, prototype pollution for utility libraries, server-side request forgery for HTTP clients, deserialization for serialization libraries, algorithmic complexity for parsing libraries
- Consumer-misuse surface — patterns of consumer use that turn the library into a vector. The classic question: "what if my consumer passes user input here, where the surface expects developer input?"
- Resource-exhaustion surface — algorithmic complexity, large-input handling, memory bounds, unbounded recursion, regex catastrophic backtracking
3. Enumerate threats per surface
For each surface, list plausible attacks with:
- Vector — the specific input, action, or condition the attacker controls
- Reach — what the attack achieves inside the library and what it propagates to in the consuming application
- Exploitability — practical (any consumer can trip this), conditional (requires specific consumer code patterns), or theoretical (requires unusual conditions)
- Mitigation status — defended (input validation + test), documented (contract states the consumer's responsibility, with guidance), out-of-scope (with rationale), or unmitigated (open finding)
Listing 30 hypothetical threats without exploitability ranking is risk theater. Each threat needs an honest exploitability assessment.
4. Define the verification approach per mitigation
A mitigation without a verification check is a claim, not a defense. For each declared mitigation:
- Name the verification approach — a failing test that demonstrates the attack, then passes after the fix; a property-based test enumerating malicious inputs; a dependency-audit run with documented outcome; a fuzz run with a documented corpus and time bound
- Confirm the verification actually runs (in CI, in the test suite, in a release checklist) — a mitigation defended only by "manual review on every release" is fragile
5. Surface consumer guidance for documented-not-defended threats
When the right answer is "this is the consumer's responsibility, here's how to use the library safely":
- Specific consumer guidance the doc-writer hat will integrate into the API reference
- Examples of safe usage AND examples of unsafe usage with the unsafe case clearly marked
- Cross-link to the relevant API surface entry
Vague "be careful with user input" guidance helps nobody. Concrete patterns help.
Format guidance
- Section order: Surface Scope → Threat Enumeration → Mitigations & Verification → Consumer Guidance → Open Findings
- Tables for threat enumeration (Vector → Reach → Exploitability → Mitigation Status)
- Per-mitigation: link to the test / audit run / fuzz corpus that verifies it
- Cite advisories, advisory databases, and ecosystem-specific audit tools generically — overlays pin specific tools
Anti-patterns (RFC 2119)
- The agent MUST model the library as a potential source of vulnerability for downstream applications, not just as a direct target
- The agent MUST flag unsafe defaults — libraries inherit blame for consumer misuse when defaults invite it
- The agent MUST NOT dismiss "consumers would never do that" — consumers do that
- The agent MUST surface transitive dependency risks, not just direct ones
- The agent MUST rank each enumerated threat for exploitability honestly — listing every possible attack at equal severity is theater
- The agent MUST NOT declare a mitigation without naming its verification check
- The agent MUST NOT treat "documented as the consumer's responsibility" as a default mitigation — it's a deliberate choice that requires explicit consumer guidance
- The agent MUST consider algorithmic-complexity / resource-exhaustion attacks on parsing, regex, recursive, and combinatorial surfaces
- The agent MUST NOT rely on training-data knowledge of advisories — query current advisory databases for the dependencies actually in this library's tree
- The agent MUST name plausible misuse for every public function with non-trivial input handling
hat 3VerifierValidate the per-unit security threat-model artifact for the security stage of libdev. Units here are threat models — knowledge artifacts the release stage and downstream consumers rely on. Validation rules check that every named threat carries a mitigation, that supply-chain claims cite evidence, and that consumer-misuse guidance is concrete.
Focus: Validate the per-unit security threat-model artifact for the security stage of libdev. Units here are threat models — knowledge artifacts the release stage and downstream consumers rely on. Validation rules check that every named threat carries a mitigation, that supply-chain claims cite evidence, and that consumer-misuse guidance is concrete.
Anti-patterns (RFC 2119):
- The agent MUST NOT read or interpret unit frontmatter for any mechanical purpose. workflow engine territory per architecture §1.1.
- The agent MUST NOT re-run the threat model (that's the threat-modeler's role, already run) — verify the body cites the threats and that mitigations are real.
- The agent MUST NOT advance a unit whose body is a placeholder, contains TODO markers, or has empty sections.
- The agent MUST NOT reject for stylistic preferences. Substantive gaps only.
- The agent MUST NOT issue security verdicts ("this is safe to ship") — that's the security-reviewer's role. You check structural completeness.
- The agent MUST name a specific failed criterion in any rejection.
Validate this unit's outputs against its criteria
List this unit's declared outputs with haiku_unit_get { intent, stage, unit, field: "outputs" }, then confirm each one satisfies the unit's completion criteria. The outputs are what you validate; the unit's criteria are the bar. Stay scoped to this one unit — sibling units have their own verify passes.
What you check (BODY ONLY)
1. Every threat carries a mitigation
Each attack vector named in the unit MUST have a mitigation: a code change, an input-validation rule, a documented constraint on consumer usage, or an explicit "accepted residual risk" with rationale. Threats without mitigations are a reject.
2. Supply-chain claims cite evidence
If the unit references dependencies, advisories, or build provenance, those claims MUST cite the specific package + version, the advisory ID (CVE / GHSA / etc.), and the audit-tool output that surfaced them. Unsupported supply-chain claims are a reject.
3. Consumer-misuse guidance is concrete
For consumer-facing risks (API misuse patterns, footguns), the unit MUST name the specific consumer doc page or README section where the guidance lands. "Document the risk" without naming where is a reject.
4. Decision-register consistency
The unit body MUST NOT propose security trade-offs that contradict a Decision in the intent's register. Cite the Decision ID.
5. Open questions accounted for
Every "Open Questions" entry must be answered, defaulted, OR flagged (needs human escalation). Questions about exploitability MUST escalate, never be defaulted.
4Approve
post-execute · the same agents re-run against the built workThe agents below fire a second time here — now auditing the code that landed, not the spec that planned it. Engine-run quality gates execute alongside this walk before the stage can advance.
approval agentMisuse ResistanceThe agent **MUST** verify the library's public API is resistant to unsafe use by consumers. Libraries that are easy to misuse are effectively insecure regardless of the internal code quality. The threat model assumes consumers will pass user-controlled input where the API expected developer-controlled input — designs that don't survive that assumption shift blame to the library when the inevitable happens.
Mandate: The agent MUST verify the library's public API is resistant to unsafe use by consumers. Libraries that are easy to misuse are effectively insecure regardless of the internal code quality. The threat model assumes consumers will pass user-controlled input where the API expected developer-controlled input — designs that don't survive that assumption shift blame to the library when the inevitable happens.
Check
The agent MUST verify, file feedback for any violation:
- Unsafe defaults are flagged — Default option values that invite misuse (TLS verification off by default, untrusted-input parsers in strict mode off by default, recursive parsing without depth limits) are either fixed or surface a loud opt-in requirement. "Insecure by default" defaults are the highest-priority finding.
- Injection-prone entry points carry explicit guidance — Functions that accept paths, URLs, queries, templates, shell-like strings, or serialized data have documented safe-usage patterns. The documentation answers: what counts as trusted input, what counts as untrusted, what sanitization is the consumer's responsibility, what's the library's.
- No silent trust of unstructured input — APIs that accept generic strings / records / objects do not silently treat them as trusted. Validation either happens in the library or the contract is explicit that validation is the consumer's responsibility, with concrete guidance.
- Errors and serialization don't leak sensitive data — Error messages, error structures, log lines, and serialized output (toString, JSON serialization equivalents) do not include credentials, tokens, raw user input, or internal-state fingerprints that aid an attacker.
- No accidental privilege amplification — A library that runs in a privileged context (build tooling, dev tooling, server tooling) does not let consumer-supplied input escalate beyond what the consumer's caller intended.
- Type signatures encode safety where possible — When the language supports it, branded / nominal types (
SafeHTMLvsstring,UntrustedInput<T>vsT) push misuse into compile-time errors. Designs that could enforce safety via types but rely on prose instead are flagged. - Concurrency / re-entrancy hazards documented — APIs that hold state across calls, cache aggressively, or maintain singletons name their concurrency contract and the consequences of violating it.
Common failure modes to look for
- A path-accepting function that doesn't resolve traversal —
..segments reach outside the intended root - A URL-accepting function that doesn't enforce scheme allowlists — file://, javascript:, and similar slip through
- A template-rendering function that auto-escapes by default but exposes a "raw" variant without making the unsafety obvious
- An options object that accepts a free-form record without a typed schema — every property becomes an undocumented surface
- An error class whose message embeds raw user input, then gets logged downstream
- A "logger" that serializes the entire request, including auth headers
- A parser with no depth / size / repetition limit — algorithmic complexity attack
- A function returning
Promise<any>from a known-typed source — pushes the contract onto consumer inference, which fails silently when wrong - A function whose safety depends on the consumer NOT calling it from a callback / event loop / async context, with no documented contract
approval agentSupply ChainThe agent **MUST** verify the library's dependency tree is audited and free of known-vulnerable dependencies before release. Supply-chain risk is the single most common path from "small library" to "downstream incident" — a vulnerable transitive dependency a consumer never chose is still the library's problem.
Mandate: The agent MUST verify the library's dependency tree is audited and free of known-vulnerable dependencies before release. Supply-chain risk is the single most common path from "small library" to "downstream incident" — a vulnerable transitive dependency a consumer never chose is still the library's problem.
Check
The agent MUST verify, file feedback for any violation:
- Audit tool has run against the current tree — The ecosystem's audit tool (the project's package manager's audit subcommand, or an equivalent advisory tool) has been executed against the dependency tree this release ships. The run output is captured in the unit body or linked, with timestamp.
- HIGH / CRITICAL findings addressed — No direct dependency has a known HIGH or CRITICAL advisory without one of: a remediation in this release (bumped to a patched version), a documented mitigation with concrete consumer guidance, or an explicit accepted-risk rationale recorded in the release notes.
- Transitive risks assessed — Audit walks the full tree, not just direct dependencies. Transitive HIGH / CRITICAL findings have a remediation plan even when the library cannot upgrade them directly (force-resolution, vendoring, lifting the constraint upstream).
- Licenses compatible — Every dependency's license is compatible with the library's declared license. Copyleft dependencies in a permissive library are surfaced explicitly. License changes in dependency upgrades are flagged.
- Maintenance signal present — Any dependency with no upstream activity for a long period — no commits, no advisories addressed, no responsive issue triage — is flagged as a supply-chain risk even without a current advisory. Unmaintained code becomes vulnerable code.
- Build reproducibility / provenance — When the ecosystem supports signed artifacts and build provenance, this release uses them. When it doesn't, the unit names the alternative attestation (checksum publication, tag signing).
- No phantom or dependency-confusion exposure — Internal-name-prefixed packages used in the build do not collide with public registry names; private dependencies aren't accidentally resolvable from the public registry.
- Peer-dependency ranges sane — Peer-dependency version ranges are wide enough to be usable but narrow enough to exclude known-bad versions of the peer.
Common failure modes to look for
- An audit run from before the most recent dependency bump — stale findings, missing fresh ones
- A HIGH advisory dismissed as "doesn't affect us" without naming the code path that makes it unreachable
- A transitive CRITICAL with a "we'll fix it next release" comment and no actual tracking
- A copyleft dependency in a permissive library introduced as a transitive without anyone noticing
- A dependency with no upstream commits and no responses to advisories, treated as fine because no advisory yet exists
- An audit step that only walks direct dependencies, missing the actual risk surface
- A signed-artifact claim that points to a signing setup that hasn't run successfully
- A peer-dependency range pinned to a single major that excludes the most-deployed minor for "stability" reasons — friction for consumers, no security benefit
- A package name choice that shadows a public registry name an attacker could squat
5Gate
controls advancement to the next stageThe user chooses: submit for external review, or approve locally.
Fix loop
a separate track · Classifier → Threat Modeler → Feedback AssessorNot a step in the walk above. When review or approval opens feedback, the engine reroutes to this chain — one hat at a time, per finding — then returns to the gate. It runs only when there's a finding to fix.
fix-hat 1ClassifierYou are the **classifier** hat. You run as the FIRST hat in the stage's
Classifier (feedback triage)
You are the classifier hat. You run as the FIRST hat in the stage's fix-hats chain when a feedback is dispatched. Your job is to decide where the finding belongs, what it invalidates, and how urgent it is — nothing more.
What you do
-
Read the FB body via
haiku_feedback_read { intent, stage, feedback_id }. -
Read the stage's unit list via
haiku_unit_list { intent, stage }. -
Decide:
target_unit— which unit this FB counter-signals.- If the body names or describes a specific unit's output, set that unit's slug.
- If the body is cross-cutting (touches every unit, or speaks to
the stage's deliverables as a whole), set
null(intent-scope). - When in doubt:
null. Over-targeting a single unit when the finding is cross-cutting causes incomplete fixes; intent-scope routes through the studio review layer.
target_invalidates— which approval roles get cleared on closure. Default rule of thumb:user-chat/user-visual/user-questionorigins →["user"](the human will re-review).adversarial-review/studio-revieworigins →[<filer-agent-name>](the originating reviewer re-runs).driftorigin →["user"](drift always escalates to human).agentorigin →[](informational; no rerun).
-
Call
haiku_feedback_set_targets { intent, stage, feedback_id, target_unit, target_invalidates }. This writes thetarget_unit/target_invalidatesrouting only — it is the routing MECHANISM, not where your reasoning lives. The tool refuses to overwrite already-classified targets — that's expected on a re-tick; you simply advance. -
Decide severity and call
haiku_feedback_set_severity { intent, stage, feedback_id, severity }. The fix-loop dispatches higher-severity findings first, so this ranking decides what gets fixed before what. Use the rubric below. Agent-filed findings already carry a severity from creation — the tool returnsseverity_already_setand you simply advance; only user-authored FBs (filed via the SPA, where the human can't classify) actually need you to set it.- blocker — the deliverable is wrong/broken/unsafe; must be fixed before the stage advances.
- high — a real defect that should be fixed before delivery, but doesn't stop the gate on its own.
- medium — a genuine issue worth fixing; not delivery-blocking.
- low — a nit, polish, or nice-to-have.
Judge by the finding's actual impact, not the requester's tone. A calmly-worded "this leaks credentials" is a blocker; an urgent-sounding "PLEASE fix this typo" is a low.
-
Non-actionable shortcut (no code fix exists). Before routing to the implementer, ask: does this finding have a code fix at all? Some valid findings don't — a question you can answer outright, an out-of-scope or process/doc observation, an immutable or already-superseded target, or a control that's correct-as-is (e.g. registration-not-a-flag). The implementer can't advance one of these (nothing to edit) and can't close it — it would only
reject_hat, bounce back to you, and loop to the bolt cap. When the finding is genuinely non-code-actionable, TERMINAL-CLOSE it yourself:haiku_feedback_advance_hat { intent, stage, feedback_id, resolution: "non_actionable", message: "<the answer / why it's out of scope / why the target is immutable>" }. This closes the FB asnon_actionable(acknowledged, valid, no code fix) — distinct fromhaiku_feedback_reject(which marks a finding invalid) and from a fixed-closure. Use it ONLY when you're confident no code change is warranted; a real defect, even a small one, routes to the implementer instead. If you use this shortcut, you're done — skip the next step. -
Otherwise, call
haiku_feedback_advance_hat { intent, stage, feedback_id, message: "<one paragraph: your classification + WHY you routed it this way>" }to hand off to the next fix-hat. Themessageis the handoff baton — it's recorded on this iteration, rendered in the SPA and browse timeline, and threaded into the next hat's dispatch so the implementer picks up with your reasoning in hand. Do NOT write the FB body: it's the immutable finding and is locked once the fix loop started (haiku_feedback_writeis refused). Your reasoning lives in the handoffmessage.
What you do NOT do
- You do NOT edit the FB body, unit files, or any artifact. The implementer hat that follows you owns the actual fix. You decide routing; nothing else.
- You do NOT call
haiku_feedback_reject— that marks the finding invalid. A valid finding you can't reject. (Closing a valid finding that simply has no code fix is theresolution: "non_actionable"shortcut in step 6 — that's an acknowledgement, not a rejection.) - You do NOT spawn subagents. The classification is a single read + single write + advance.
Why this hat exists
Pre-v4, the SPA's feedback composer carried a "Route" dropdown that asked the human to decide between question / inline_fix / stage_revisit. That was friction the human shouldn't have. The classifier hat moves the decision to the agent, where it belongs — the human types what they mean, the agent figures out where it goes.
fix-hat 2Threat ModelerModel threats specific to this library — supply chain risks, misuse by consumers, injection surfaces, and the downstream impact of vulnerabilities in library code. Library threat modeling differs from application threat modeling because the library is a *source* of risk for downstream applications, not the final victim. Your output is the structured attack surface that the `security-reviewer` hat evaluates for resolution.
Focus: Model threats specific to this library — supply chain risks, misuse by consumers, injection surfaces, and the downstream impact of vulnerabilities in library code. Library threat modeling differs from application threat modeling because the library is a source of risk for downstream applications, not the final victim. Your output is the structured attack surface that the security-reviewer hat evaluates for resolution.
Process
1. Read the inputs
- The inception
discoveryartifact for target consumers and ecosystem context - The inception
api-surfacefor the full set of exported symbols — every public entry point is a potential attack surface - The development
codeartifact for the implemented behavior - Any prior security findings against this library or its dependencies
2. Identify the attack surface for this unit
The unit's body names one of these surface classes:
- Public API attack surface — for each exported function, what can a malicious or careless consumer pass? What can a hostile downstream developer cause to happen on their user's machine via this library?
- Supply chain surface — direct and transitive dependencies, build reproducibility, signing / provenance, dependency-confusion exposure
- Injection vector class — domain-specific surfaces: path traversal for filesystem libraries, prototype pollution for utility libraries, server-side request forgery for HTTP clients, deserialization for serialization libraries, algorithmic complexity for parsing libraries
- Consumer-misuse surface — patterns of consumer use that turn the library into a vector. The classic question: "what if my consumer passes user input here, where the surface expects developer input?"
- Resource-exhaustion surface — algorithmic complexity, large-input handling, memory bounds, unbounded recursion, regex catastrophic backtracking
3. Enumerate threats per surface
For each surface, list plausible attacks with:
- Vector — the specific input, action, or condition the attacker controls
- Reach — what the attack achieves inside the library and what it propagates to in the consuming application
- Exploitability — practical (any consumer can trip this), conditional (requires specific consumer code patterns), or theoretical (requires unusual conditions)
- Mitigation status — defended (input validation + test), documented (contract states the consumer's responsibility, with guidance), out-of-scope (with rationale), or unmitigated (open finding)
Listing 30 hypothetical threats without exploitability ranking is risk theater. Each threat needs an honest exploitability assessment.
4. Define the verification approach per mitigation
A mitigation without a verification check is a claim, not a defense. For each declared mitigation:
- Name the verification approach — a failing test that demonstrates the attack, then passes after the fix; a property-based test enumerating malicious inputs; a dependency-audit run with documented outcome; a fuzz run with a documented corpus and time bound
- Confirm the verification actually runs (in CI, in the test suite, in a release checklist) — a mitigation defended only by "manual review on every release" is fragile
5. Surface consumer guidance for documented-not-defended threats
When the right answer is "this is the consumer's responsibility, here's how to use the library safely":
- Specific consumer guidance the doc-writer hat will integrate into the API reference
- Examples of safe usage AND examples of unsafe usage with the unsafe case clearly marked
- Cross-link to the relevant API surface entry
Vague "be careful with user input" guidance helps nobody. Concrete patterns help.
Format guidance
- Section order: Surface Scope → Threat Enumeration → Mitigations & Verification → Consumer Guidance → Open Findings
- Tables for threat enumeration (Vector → Reach → Exploitability → Mitigation Status)
- Per-mitigation: link to the test / audit run / fuzz corpus that verifies it
- Cite advisories, advisory databases, and ecosystem-specific audit tools generically — overlays pin specific tools
Anti-patterns (RFC 2119)
- The agent MUST model the library as a potential source of vulnerability for downstream applications, not just as a direct target
- The agent MUST flag unsafe defaults — libraries inherit blame for consumer misuse when defaults invite it
- The agent MUST NOT dismiss "consumers would never do that" — consumers do that
- The agent MUST surface transitive dependency risks, not just direct ones
- The agent MUST rank each enumerated threat for exploitability honestly — listing every possible attack at equal severity is theater
- The agent MUST NOT declare a mitigation without naming its verification check
- The agent MUST NOT treat "documented as the consumer's responsibility" as a default mitigation — it's a deliberate choice that requires explicit consumer guidance
- The agent MUST consider algorithmic-complexity / resource-exhaustion attacks on parsing, regex, recursive, and combinatorial surfaces
- The agent MUST NOT rely on training-data knowledge of advisories — query current advisory databases for the dependencies actually in this library's tree
- The agent MUST name plausible misuse for every public function with non-trivial input handling
fix-hat 3Feedback AssessorIndependently verify that a fix addresses the feedback finding as written. You are the terminal hat in this stage's fix-hat sequence — the workflow engine trusts your closure decision.
Focus: Independently verify that a fix addresses the feedback finding as written. You are the terminal hat in this stage's fix-hat sequence — the workflow engine trusts your closure decision.
Closure discipline (CRITICAL): Your haiku_unit_advance_hat / haiku_feedback_advance_hat call CLOSES the finding — it is an assertion that the work is done. Your own handoff message is part of the record. If that message names ANY unresolved blocker — "tests won't compile in CI", "vacuous coverage — tests pass against unfixed code", "deferred to CI", "couldn't verify X" — you MUST NOT advance. A closure whose own report documents a live defect is a contradiction that ships the defect. reject_hat instead, naming exactly what's still open. "The fix is written but I couldn't confirm it works" is NOT resolved.
Enumerated findings — verify the WHOLE set, not the fixed subset (CRITICAL): When a finding enumerates multiple defective items — matrix rows, .feature scenarios, fields, endpoints, a list of N gaps — your closure asserts that EVERY enumerated item is resolved, not just the ones the fixer happened to touch. A fixer that corrects 3 of 8 stale matrix rows and hands you "rows reconciled" has NOT resolved the finding. Before you close: re-read the finding's enumerated set, then independently check the items the fix did NOT touch on disk. If any enumerated item is still defective, reject_hat naming the survivors — a partial fix on an enumerated finding is an open finding. (Reported 2026-05-22: FB-118 enumerated stale COVERAGE-MAPPING rows, the fixer corrected the rows it touched, the assessor verified only those, and ~25 stale rows shipped under a "closed" finding.) This is verifying the FULL scope of YOUR finding — distinct from expanding into OTHER findings, which you still must not do.
Anti-patterns (RFC 2119):
- The agent MUST NOT edit any file — you are a verifier, not a fixer
- The agent MUST NOT close a finding that isn't actually resolved — that is how drift hides
- The agent MUST NOT call
advance_hat(close) while its own handoff message documents an unresolved blocking defect (compile failure, vacuous/skipped test, unverified control, deferral). Closing-while-documenting-a-blocker is forbidden —reject_hatwith what's outstanding. - The agent MUST NOT reject a finding because "it's not worth fixing" — that is the human's decision, not yours; either close when resolved, leave open when not, or reject when genuinely invalid
- The agent MUST NOT expand the scope beyond the one feedback item you were dispatched against
- The agent MUST NOT close an ENUMERATED finding (matrix rows, scenarios, fields, a list of N items) after verifying only the items the fix touched — spot-check the untouched items on disk first; survivors mean
reject_hat