feat: add a dedicated Security Auditor agent with veto power

Security was one section among seven in the QA Engineer. That is structurally
weak: the agent that wants to recommend "production ready: YES" also judged
security, and as a subsection it competes for attention with acceptance
criteria, regression and test authoring.

The new agent:
- has no Write/Edit tools, so "never fix what you find" is enforced by the tool
  list rather than by instruction — it cannot launder a finding into a patch
- reports PASS / FAIL / UNVERIFIED, and UNVERIFIED must reach the spec; an audit
  that omits what it could not check reads as all-clear
- audits against .claude/rules/security.md, the same standard the developer
  agents are held to, so every mandatory item gets a verdict
- holds a veto: BLOCK stops /deploy regardless of functional test results

/qa now runs both agents in parallel and keeps the verdicts separate. The QA
Engineer is explicitly forbidden from speaking for security.
This commit is contained in:
Florian Wilde
2026-08-09 01:22:05 +02:00
parent 75e99f75c6
commit f432122abf
6 changed files with 281 additions and 39 deletions
+5
View File
@@ -18,6 +18,11 @@ on the host.
**No QA section:** → "Für PROJ-X gibt es noch kein QA-Ergebnis. Führe erst `/qa` aus." → Stop.
**Critical or High bugs open:** → name them and stop. Those get fixed first.
**Security verdict is BLOCK, or missing entirely:** → stop. A feature without a
Security Auditor verdict has not been security-reviewed, no matter how green the
functional tests are.
**UNVERIFIED security items:** → list them and ask the user explicitly whether to
ship with that surface unchecked. Do not decide this silently on their behalf.
## 2. Pre-flight
+58 -19
View File
@@ -23,14 +23,27 @@ user.
Update `features/INDEX.md` and the spec header to **In Review**. Verify by
re-reading.
## 3. Delegate testing
## 3. Delegate — two agents, in parallel
Invoke the **QA Engineer** sub-agent with:
Invoke both in the same turn so they run concurrently. They answer different
questions and must not be merged into one agent.
**QA Engineer** — does the feature do what the spec promises?
- the spec path
- the list of deployed features to regression-check
- any areas the developer flagged as risky
Wait for its report.
**Security Auditor** — is it safe to expose to the internet?
- the spec path
- which routes, auth flows and compose services this feature touched
- whether a deployment already exists (so it can check live headers)
Wait for both reports.
The Security Auditor has a **veto**. If it returns `BLOCK`, the feature is not
Approved — regardless of how clean the functional test run was. Never average the
two verdicts into an optimistic summary, and never let the QA Engineer's
"production ready: YES" stand in for a security verdict it is not allowed to give.
## 4. Write results into the spec
@@ -42,7 +55,7 @@ Append this to the feature spec — not to a separate file:
## QA Test Results
**Getestet:** YYYY-MM-DD
**Tester:** QA Engineer (AI)
**Geprüft von:** QA Engineer + Security Auditor (AI)
### Acceptance Criteria
- [x] AC-1: [Kriterium]
@@ -51,13 +64,32 @@ Append this to the feature spec — not to a separate file:
### Edge Cases
- [x] EC-1: [Randfall] — korrekt behandelt
### Security-Audit
- [x] Ohne Session kein Zugriff
- [x] Kein Zugriff auf fremde Datensätze
- [x] Passkey ist der primäre Anmeldeweg
- [x] Eingaben validiert, keine Injection
### Security-Audit (Security Auditor)
### Gefundene Bugs
**Verdikt: BLOCK | PASS-MIT-BEFUNDEN | PASS**
| Bereich | Ergebnis | Beleg |
|---------|----------|-------|
| Authentifizierung | PASS | Passkey primär, `src/lib/auth.ts:24` |
| Autorisierung | FAIL | siehe SEC-1 |
| Eingabevalidierung | PASS | Zod auf allen Write-Routen |
| Secrets | PASS | nichts in der History, `.env.example` vollständig |
| Container-Exposure | UNGEPRÜFT | Docker in dieser Umgebung nicht verfügbar |
| Security-Header | PASS | `next.config.ts:8-20` |
| Abhängigkeiten | PASS | 0 High/Critical in Prod-Deps |
| Fehler- und Log-Hygiene | PASS | |
**UNGEPRÜFT ist kein Bestanden.** Jeder solche Punkt bleibt hier stehen, bis ihn
jemand tatsächlich geprüft hat.
#### SEC-1: [Titel]
- **Severity:** Critical | High | Medium | Low
- **Ort:** `datei.ts:zeile`
- **Angriff:** was ein Angreifer konkret tut
- **Ursache:** warum es funktioniert
- **Fix gehört zu:** /backend | /frontend
### Gefundene Bugs (QA Engineer)
#### BUG-1: [Titel]
- **Severity:** Critical | High | Medium | Low
@@ -68,33 +100,40 @@ Append this to the feature spec — not to a separate file:
### Zusammenfassung
- **Acceptance Criteria:** X von Y bestanden
- **Bugs:** N gesamt (C critical, H high, M medium, L low)
- **Produktionsreif:** JA / NEIN
- **Security:** Verdikt + Anzahl Befunde + Anzahl UNGEPRÜFT
- **Produktionsreif:** JA / NEIN — bei Security-Verdikt BLOCK immer NEIN
```
Re-read the file afterwards to confirm it landed.
## 5. Review with the user
Present: criteria passed/failed, bugs by severity, security findings, verdict.
Then ask:
> "Welche Bugs sollen zuerst behoben werden?"
Present both verdicts separately — functional and security. Name every UNVERIFIED
security item out loud; those are the parts nobody has checked, and burying them in
the table is how they get forgotten. Then ask:
> "Welche Befunde sollen zuerst behoben werden?"
## 6. Wrap up
- [ ] Every acceptance criterion has an individual pass/fail
- [ ] Documented and undocumented edge cases tested
- [ ] Security audit done from a red-team angle
- [ ] Security Auditor ran and returned a verdict
- [ ] Every UNVERIFIED security item is listed in the spec, not silently dropped
- [ ] Regression check against deployed features
- [ ] Unit tests for non-trivial logic (`npm test` passes)
- [ ] E2E test per passing criterion (`npm run test:e2e` passes)
- [ ] QA section written into the spec and verified
- [ ] `features/INDEX.md` → **Approved** if no Critical/High, otherwise stays **In Review**
- [ ] `features/INDEX.md` → **Approved** only if there are no Critical/High bugs
**and** the security verdict is not BLOCK; otherwise stays **In Review**
## Handoff
- Clean → "Alle Tests bestanden, Status **Approved**. Nächster Schritt: `/deploy`."
- Bugs → "[N] Bugs gefunden ([Aufschlüsselung]). Status bleibt **In Review**.
Erst `/frontend` bzw. `/backend` zum Beheben, danach nochmal `/qa`."
- Both clean → "Tests bestanden, Security-Verdikt PASS. Status **Approved**.
Nächster Schritt: `/deploy`."
- Security BLOCK → "Security-Audit blockiert: [Befunde]. Status bleibt **In Review**,
unabhängig von den Funktionstests. Erst `/backend` bzw. `/frontend` zum Beheben,
danach nochmal `/qa`."
- Nur Bugs → "[N] Bugs gefunden ([Aufschlüsselung]). Status bleibt **In Review**."
## Commit