# Security audit (Phase 3 Step 2.7)

The mechanics of the conditional security audit that runs inside the Phase 3 review window and merges at Step 3.0. The phase doc carries the trigger and the merge; the rest is here.

## Trigger (deterministic, no new classifier)

Run the audit when any of these holds:

- Step 1.75 scored `security_path` on a file in the diff (the same signal that already forces `full` review at Step 1.77).
- The base branch is a release branch.
- The run is the standalone `/multi-agent:security-review` command, which dispatches straight to this step.

There is no `--audit` flag: the trigger is the diff, not a word the user has to remember. When none of these holds, the audit does not run, no `security-audit-$ITERATION.json` is written, and the Step 3.0 merge and the review-decision `--source` are no-ops.

## Threat model first

The auditor reads `.pipeline/threat-model.md` if Phase 1 or a prior step wrote it, and produces it (four sections) if absent. Contract: `$HOME/.claude/multi-agent-refs/threat-model.md`. It is keyed to repo+branch and mirrored to `state.threatModel` so a resume reuses it rather than re-deriving it.

## Dispatch

One `Agent(subagent_type: "security-auditor")` on the same capped diff the reviewers saw, plus the threat model and the resolved `${CRITERIA}` block:

```
Agent(subagent_type: "security-auditor", prompt: "<threat-model + capped diff + ${CRITERIA}>")
```

Persona contract: `~/.claude/agents/security-auditor.md`.

## Output is reviewer-shaped, and that is the point

The auditor returns one object conforming to `$HOME/.claude/schemas/reviewer-output.schema.json`, whose `findings[]` each carry the `security` envelope of `security-finding.schema.json`: OWASP category, CWE, CVSS vector + band, evidence, counterevidence, confidence, remediation, and the before/after fix. Severity is the reviewer enum, derived from the CVSS band:

| CVSS band | severity |
| --------- | -------- |
| critical / high | blocking |
| medium | important |
| low / none | suggestion |

Compute `security.cvss.baseScore` + `band` with the toolkit `security_cvss_score` tool, not by hand, so the score cannot drift from the vector.

## Validate, persist, merge

Validate with the same gate protocol as a reviewer  -  the exit code decides, not the LLM turn:

```bash
printf '%s' "$SECURITY_AUDIT_JSON" | node "$HOME/.claude/scripts/validate-reviewer.mjs" -
```

On validator failure: one self-correction rework, then HALT the phase (identical to the reviewer output-contract gate). Persist the object to `state.reviewIterations[<iteration>].securityAudit` and write it to a file, which Step 3.0 merges and `review-decision-gate.mjs` reads as one independent `--source`:

```bash
printf '%s' "$SECURITY_AUDIT_JSON" > "$WORKTREE/.pipeline/security-audit-$ITERATION.json"
```

Because the output is reviewer-shaped, the Step 3.0 merge appends its `findings[]` alongside the test-integrity findings. A `blocking` security finding then reaches triage, and a triage-accepted blocker blocks Phase 4  -  the "critical security items block the commit" contract is wired here, not merely stated.

## Store-compliance cross-reference

On store-relevant diffs the auditor also cites the Apple ITMS / Google Play catalog rule on the finding via `ruleId` + `criteriaSource` (`apple-archive-compliance` / `google-play-compliance`). This is annotation on the same finding, not a second pass; the device-level `store-ready` Gate under `/multi-agent:test` stays separate.
