diff --git a/agents/gsd-executor.md b/agents/gsd-executor.md index 03cbf9b3..d258fe7b 100644 --- a/agents/gsd-executor.md +++ b/agents/gsd-executor.md @@ -133,6 +133,8 @@ No user permission needed for Rules 1-3. **Critical = required for correct/secure/performant operation.** These aren't "features" — they're correctness requirements. +**Threat model reference:** Before starting each task, check if the plan's `` assigns `mitigate` dispositions to this task's files. Mitigations in the threat register are correctness requirements — apply Rule 2 if absent from implementation. + --- **RULE 3: Auto-fix blocking issues** @@ -394,6 +396,18 @@ Or: "None - plan executed exactly as written." - Components with no data source wired (props always receiving empty/mock data) If any stubs exist, add a `## Known Stubs` section to the SUMMARY listing each stub with its file, line, and reason. These are tracked for the verifier to catch. Do NOT mark a plan as complete if stubs exist that prevent the plan's goal from being achieved — either wire the data or document in the plan why the stub is intentional and which future plan will resolve it. + +**Threat surface scan:** Before writing the SUMMARY, check if any files created/modified introduce security-relevant surface NOT in the plan's `` — new network endpoints, auth paths, file access patterns, or schema changes at trust boundaries. If found, add: + +```markdown +## Threat Flags + +| Flag | File | Description | +|------|------|-------------| +| threat_flag: {type} | {file} | {new surface description} | +``` + +Omit section if nothing found. diff --git a/agents/gsd-phase-researcher.md b/agents/gsd-phase-researcher.md index 28a7a00f..f8c89c2e 100644 --- a/agents/gsd-phase-researcher.md +++ b/agents/gsd-phase-researcher.md @@ -222,6 +222,8 @@ Priority: Context7 > Exa (verified) > Firecrawl (official docs) > Official GitHu - [ ] Confidence levels assigned honestly - [ ] "What might I have missed?" review completed - [ ] **If rename/refactor phase:** Runtime State Inventory completed — all 5 categories answered explicitly (not left blank) +- [ ] Security domain included (or `security_enforcement: false` confirmed) +- [ ] ASVS categories verified against phase tech stack @@ -393,6 +395,27 @@ Verified patterns from official sources: *(If no gaps: "None — existing test infrastructure covers all phase requirements")* +## Security Domain + +> Required when `security_enforcement` is enabled (absent = enabled). Omit only if explicitly `false` in config. + +### Applicable ASVS Categories + +| ASVS Category | Applies | Standard Control | +|---------------|---------|-----------------| +| V2 Authentication | {yes/no} | {library or pattern} | +| V3 Session Management | {yes/no} | {library or pattern} | +| V4 Access Control | {yes/no} | {library or pattern} | +| V5 Input Validation | yes | {e.g., zod / joi / pydantic} | +| V6 Cryptography | {yes/no} | {library — never hand-roll} | + +### Known Threat Patterns for {stack} + +| Pattern | STRIDE | Standard Mitigation | +|---------|--------|---------------------| +| {e.g., SQL injection} | Tampering | {parameterized queries / ORM} | +| {pattern} | {category} | {mitigation} | + ## Sources ### Primary (HIGH confidence) diff --git a/agents/gsd-planner.md b/agents/gsd-planner.md index 9c01b4bd..90fcd9f5 100644 --- a/agents/gsd-planner.md +++ b/agents/gsd-planner.md @@ -454,6 +454,21 @@ Output: [Artifacts created] + +## Trust Boundaries + +| Boundary | Description | +|----------|-------------| +| {e.g., client→API} | {untrusted input crosses here} | + +## STRIDE Threat Register + +| Threat ID | Category | Component | Disposition | Mitigation Plan | +|-----------|----------|-----------|-------------|-----------------| +| T-{phase}-01 | {S/T/R/I/D/E} | {function/endpoint/file} | mitigate | {specific: e.g., "validate input with zod at route entry"} | +| T-{phase}-02 | {category} | {component} | accept | {rationale: e.g., "no PII, low-value target"} | + + [Overall phase checks] @@ -585,6 +600,8 @@ Only include what Claude literally cannot do. **Step 0: Extract Requirement IDs** Read ROADMAP.md `**Requirements:**` line for this phase. Strip brackets if present (e.g., `[AUTH-01, AUTH-02]` → `AUTH-01, AUTH-02`). Distribute requirement IDs across plans — each plan's `requirements` frontmatter field MUST list the IDs its tasks address. **CRITICAL:** Every requirement ID MUST appear in at least one plan. Plans with an empty `requirements` field are invalid. +**Security (when `security_enforcement` enabled — absent = enabled):** Identify trust boundaries in this phase's scope. Map STRIDE categories to applicable tech stack from RESEARCH.md security domain. For each threat: assign disposition (mitigate if ASVS L1 requires it, accept if low risk, transfer if third-party). Every plan MUST include `` when security_enforcement is enabled. + **Step 1: State the Goal** Take phase goal from ROADMAP.md. Must be outcome-shaped, not task-shaped. - Good: "Working chat interface" (outcome) @@ -1338,6 +1355,9 @@ Phase planning complete when: - [ ] Wave structure maximizes parallelism - [ ] PLAN file(s) committed to git - [ ] User knows next steps and wave structure +- [ ] `` present with STRIDE register (when `security_enforcement` enabled) +- [ ] Every threat has a disposition (mitigate / accept / transfer) +- [ ] Mitigations reference specific implementation (not generic advice) ## Gap Closure Mode diff --git a/agents/gsd-security-auditor.md b/agents/gsd-security-auditor.md new file mode 100644 index 00000000..836cfffe --- /dev/null +++ b/agents/gsd-security-auditor.md @@ -0,0 +1,128 @@ +--- +name: gsd-security-auditor +description: Verifies threat mitigations from PLAN.md threat model exist in implemented code. Produces SECURITY.md. Spawned by /gsd:secure-phase. +tools: + - Read + - Write + - Edit + - Bash + - Glob + - Grep +color: "#EF4444" +--- + + +GSD security auditor. Spawned by /gsd:secure-phase to verify that threat mitigations declared in PLAN.md are present in implemented code. + +Does NOT scan blindly for new vulnerabilities. Verifies each threat in `` by its declared disposition (mitigate / accept / transfer). Reports gaps. Writes SECURITY.md. + +**Mandatory Initial Read:** If prompt contains ``, load ALL listed files before any action. + +**Implementation files are READ-ONLY.** Only create/modify: SECURITY.md. Implementation security gaps → OPEN_THREATS or ESCALATE. Never patch implementation. + + + + + +Read ALL files from ``. Extract: +- PLAN.md `` block: full threat register with IDs, categories, dispositions, mitigation plans +- SUMMARY.md `## Threat Flags` section: new attack surface detected by executor during implementation +- `` block: `asvs_level` (1/2/3), `block_on` (open / unregistered / none) +- Implementation files: exports, auth patterns, input handling, data flows + + + +For each threat in ``, determine verification method by disposition: + +| Disposition | Verification Method | +|-------------|---------------------| +| `mitigate` | Grep for mitigation pattern in files cited in mitigation plan | +| `accept` | Verify entry present in SECURITY.md accepted risks log | +| `transfer` | Verify transfer documentation present (insurance, vendor SLA, etc.) | + +Classify each threat before verification. Record classification for every threat — no threat skipped. + + + +For each `mitigate` threat: grep for declared mitigation pattern in cited files → found = `CLOSED`, not found = `OPEN`. +For `accept` threats: check SECURITY.md accepted risks log → entry present = `CLOSED`, absent = `OPEN`. +For `transfer` threats: check for transfer documentation → present = `CLOSED`, absent = `OPEN`. + +For each `threat_flag` in SUMMARY.md `## Threat Flags`: if maps to existing threat ID → informational. If no mapping → log as `unregistered_flag` in SECURITY.md (not a blocker). + +Write SECURITY.md. Set `threats_open` count. Return structured result. + + + + + + +## SECURED + +```markdown +## SECURED + +**Phase:** {N} — {name} +**Threats Closed:** {count}/{total} +**ASVS Level:** {1/2/3} + +### Threat Verification +| Threat ID | Category | Disposition | Evidence | +|-----------|----------|-------------|----------| +| {id} | {category} | {mitigate/accept/transfer} | {file:line or doc reference} | + +### Unregistered Flags +{none / list from SUMMARY.md ## Threat Flags with no threat mapping} + +SECURITY.md: {path} +``` + +## OPEN_THREATS + +```markdown +## OPEN_THREATS + +**Phase:** {N} — {name} +**Closed:** {M}/{total} | **Open:** {K}/{total} +**ASVS Level:** {1/2/3} + +### Closed +| Threat ID | Category | Disposition | Evidence | +|-----------|----------|-------------|----------| +| {id} | {category} | {disposition} | {evidence} | + +### Open +| Threat ID | Category | Mitigation Expected | Files Searched | +|-----------|----------|---------------------|----------------| +| {id} | {category} | {pattern not found} | {file paths} | + +Next: Implement mitigations or document as accepted in SECURITY.md accepted risks log, then re-run /gsd:secure-phase. + +SECURITY.md: {path} +``` + +## ESCALATE + +```markdown +## ESCALATE + +**Phase:** {N} — {name} +**Closed:** 0/{total} + +### Details +| Threat ID | Reason Blocked | Suggested Action | +|-----------|----------------|------------------| +| {id} | {reason} | {action} | +``` + + + + +- [ ] All `` loaded before any analysis +- [ ] Threat register extracted from PLAN.md `` block +- [ ] Each threat verified by disposition type (mitigate / accept / transfer) +- [ ] Threat flags from SUMMARY.md `## Threat Flags` incorporated +- [ ] Implementation files never modified +- [ ] SECURITY.md written to correct path +- [ ] Structured return: SECURED / OPEN_THREATS / ESCALATE + diff --git a/commands/gsd/secure-phase.md b/commands/gsd/secure-phase.md new file mode 100644 index 00000000..a4984396 --- /dev/null +++ b/commands/gsd/secure-phase.md @@ -0,0 +1,35 @@ +--- +name: gsd:secure-phase +description: Retroactively verify threat mitigations for a completed phase +argument-hint: "[phase number]" +allowed-tools: + - Read + - Write + - Edit + - Bash + - Glob + - Grep + - Task + - AskUserQuestion +--- + +Verify threat mitigations for a completed phase. Three states: +- (A) SECURITY.md exists — audit and verify mitigations +- (B) No SECURITY.md, PLAN.md with threat model exists — run from artifacts +- (C) Phase not executed — exit with guidance + +Output: updated SECURITY.md. + + + +@~/.claude/get-shit-done/workflows/secure-phase.md + + + +Phase: $ARGUMENTS — optional, defaults to last completed phase. + + + +Execute @~/.claude/get-shit-done/workflows/secure-phase.md. +Preserve all workflow gates. + diff --git a/get-shit-done/templates/SECURITY.md b/get-shit-done/templates/SECURITY.md new file mode 100644 index 00000000..77f5c4da --- /dev/null +++ b/get-shit-done/templates/SECURITY.md @@ -0,0 +1,61 @@ +--- +phase: {N} +slug: {phase-slug} +status: draft +threats_open: 0 +asvs_level: 1 +created: {date} +--- + +# Phase {N} — Security + +> Per-phase security contract: threat register, accepted risks, and audit trail. + +--- + +## Trust Boundaries + +| Boundary | Description | Data Crossing | +|----------|-------------|---------------| +| {boundary} | {description} | {data type / sensitivity} | + +--- + +## Threat Register + +| Threat ID | Category | Component | Disposition | Mitigation | Status | +|-----------|----------|-----------|-------------|------------|--------| +| T-{N}-01 | {STRIDE category} | {component} | {mitigate / accept / transfer} | {control or reference} | open | + +*Status: open · closed* +*Disposition: mitigate (implementation required) · accept (documented risk) · transfer (third-party)* + +--- + +## Accepted Risks Log + +| Risk ID | Threat Ref | Rationale | Accepted By | Date | +|---------|------------|-----------|-------------|------| + +*Accepted risks do not resurface in future audit runs.* + +*If none: "No accepted risks."* + +--- + +## Security Audit Trail + +| Audit Date | Threats Total | Closed | Open | Run By | +|------------|---------------|--------|------|--------| +| {YYYY-MM-DD} | {N} | {N} | {N} | {name / agent} | + +--- + +## Sign-Off + +- [ ] All threats have a disposition (mitigate / accept / transfer) +- [ ] Accepted risks documented in Accepted Risks Log +- [ ] `threats_open: 0` confirmed +- [ ] `status: verified` set in frontmatter + +**Approval:** {pending / verified YYYY-MM-DD} diff --git a/get-shit-done/templates/VALIDATION.md b/get-shit-done/templates/VALIDATION.md index d569841e..6adaf46d 100644 --- a/get-shit-done/templates/VALIDATION.md +++ b/get-shit-done/templates/VALIDATION.md @@ -36,9 +36,9 @@ created: {date} ## Per-Task Verification Map -| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | -|---------|------|------|-------------|-----------|-------------------|-------------|--------| -| {N}-01-01 | 01 | 1 | REQ-{XX} | unit | `{command}` | ✅ / ❌ W0 | ⬜ pending | +| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status | +|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------| +| {N}-01-01 | 01 | 1 | REQ-{XX} | T-{N}-01 / — | {expected secure behavior or "N/A"} | unit | `{command}` | ✅ / ❌ W0 | ⬜ pending | *Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* diff --git a/get-shit-done/templates/config.json b/get-shit-done/templates/config.json index fd147b6f..8fb0ecc6 100644 --- a/get-shit-done/templates/config.json +++ b/get-shit-done/templates/config.json @@ -7,6 +7,9 @@ "verifier": true, "auto_advance": false, "nyquist_validation": true, + "security_enforcement": true, + "security_asvs_level": 1, + "security_block_on": "high", "discuss_mode": "discuss", "research_before_questions": false }, diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 0bdb1117..83817919 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -436,6 +436,27 @@ After all waves: ### Issues Encountered [Aggregate from SUMMARYs, or "None"] ``` + +**Security gate check:** +```bash +SECURITY_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") +SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1) +``` + +If `SECURITY_CFG` is `false`: skip. + +If `SECURITY_CFG` is `true` AND `SECURITY_FILE` is empty (no SECURITY.md yet): +Include in the next-steps routing output: +``` +⚠ Security enforcement enabled — run before advancing: + /gsd:secure-phase {PHASE} ${GSD_WS} +``` + +If `SECURITY_CFG` is `true` AND SECURITY.md exists: check frontmatter `threats_open`. If > 0: +``` +⚠ Security gate: {threats_open} threats open + /gsd:secure-phase {PHASE} — resolve before advancing +``` diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index b13dc034..f72dd581 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -360,6 +360,32 @@ test -f "${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md" && echo "VALIDATION_CREATED **If not found:** Warn and continue — plans may fail Dimension 8. +## 5.55. Security Threat Model Gate + +> Skip if `workflow.security_enforcement` is explicitly `false`. Absent = enabled. + +```bash +SECURITY_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") +SECURITY_ASVS=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1") +SECURITY_BLOCK=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_block_on --raw 2>/dev/null || echo "high") +``` + +**If `SECURITY_CFG` is `false`:** Skip to step 5.6. + +**If `SECURITY_CFG` is `true`:** Display banner: + +``` +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► SECURITY THREAT MODEL REQUIRED (ASVS L{SECURITY_ASVS}) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Each PLAN.md must include a block. +Block on: {SECURITY_BLOCK} severity threats. +Opt out: set security_enforcement: false in .planning/config.json +``` + +Continue to step 5.6. Security config is passed to the planner in step 8. + ## 5.6. UI Design Contract Gate > Skip if `workflow.ui_phase` is explicitly `false` AND `workflow.ui_safety_gate` is explicitly `false` in `.planning/config.json`. If keys are absent, treat as enabled. @@ -496,6 +522,7 @@ ${AGENT_SKILLS_PLANNER} **Project instructions:** Read ./CLAUDE.md if exists — follow project-specific guidelines **Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — read SKILL.md files, plans should account for project skill rules + diff --git a/get-shit-done/workflows/secure-phase.md b/get-shit-done/workflows/secure-phase.md new file mode 100644 index 00000000..2497479b --- /dev/null +++ b/get-shit-done/workflows/secure-phase.md @@ -0,0 +1,164 @@ + +Verify threat mitigations for a completed phase. Confirm PLAN.md threat register dispositions are resolved. Update SECURITY.md. + + + +@~/.claude/get-shit-done/references/ui-brand.md + + + +Valid GSD subagent types (use exact names — do not fall back to 'general-purpose'): +- gsd-security-auditor — Verifies threat mitigation coverage + + + + +## 0. Initialize + +```bash +INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init phase-op "${PHASE_ARG}") +if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi +AGENT_SKILLS_AUDITOR=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" agent-skills gsd-security-auditor 2>/dev/null) +``` + +Parse: `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`. + +```bash +AUDITOR_MODEL=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" resolve-model gsd-security-auditor --raw) +SECURITY_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") +``` + +If `SECURITY_CFG` is `false`: exit with "Security enforcement disabled. Enable via /gsd:settings." + +Display banner: `GSD > SECURE PHASE {N}: {name}` + +## 1. Detect Input State + +```bash +SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1) +PLAN_FILES=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null) +SUMMARY_FILES=$(ls "${PHASE_DIR}"/*-SUMMARY.md 2>/dev/null) +``` + +- **State A** (`SECURITY_FILE` non-empty): Audit existing +- **State B** (`SECURITY_FILE` empty, `PLAN_FILES` and `SUMMARY_FILES` non-empty): Run from artifacts +- **State C** (`SUMMARY_FILES` empty): Exit — "Phase {N} not executed. Run /gsd:execute-phase {N} first." + +## 2. Discovery + +### 2a. Read Phase Artifacts + +Read PLAN.md — extract `` block: trust boundaries, STRIDE register (`threat_id`, `category`, `component`, `disposition`, `mitigation_plan`). + +### 2b. Read Summary Threat Flags + +Read SUMMARY.md — extract `## Threat Flags` entries. + +### 2c. Build Threat Register + +Per threat: `{ threat_id, category, component, disposition, mitigation_pattern, files_to_check }` + +## 3. Threat Classification + +Classify each threat: + +| Status | Criteria | +|--------|----------| +| CLOSED | mitigation found OR accepted risk documented in SECURITY.md OR transfer documented | +| OPEN | none of the above | + +Build: `{ threat_id, category, component, disposition, status, evidence }` + +If `threats_open: 0` → skip to Step 6 directly. + +## 4. Present Threat Plan + +Call AskUserQuestion with threat table and options: +1. "Verify all open threats" → Step 5 +2. "Accept all open — document in accepted risks log" → add to SECURITY.md accepted risks, set all CLOSED, Step 6 +3. "Cancel" → exit + +## 5. Spawn gsd-security-auditor + +``` +Task( + prompt="Read ~/.claude/agents/gsd-security-auditor.md for instructions.\n\n" + + "{PLAN, SUMMARY, impl files, SECURITY.md}" + + "{threat register}" + + "asvs_level: {SECURITY_ASVS}, block_on: {SECURITY_BLOCK_ON}" + + "Never modify implementation files. Verify mitigations exist — do not scan for new threats. Escalate implementation gaps." + + "${AGENT_SKILLS_AUDITOR}", + subagent_type="gsd-security-auditor", + model="{AUDITOR_MODEL}", + description="Verify threat mitigations for Phase {N}" +) +``` + +Handle return: +- `## SECURED` → record closures → Step 6 +- `## OPEN_THREATS` → record closed + open, present user with accept/block choice → Step 6 +- `## ESCALATE` → present to user → Step 6 + +## 6. Write/Update SECURITY.md + +**State B (create):** +1. Read template from `~/.claude/get-shit-done/templates/SECURITY.md` +2. Fill: frontmatter, threat register, accepted risks, audit trail +3. Write to `${PHASE_DIR}/${PADDED_PHASE}-SECURITY.md` + +**State A (update):** +1. Update threat register statuses, append to audit trail: + +```markdown +## Security Audit {date} +| Metric | Count | +|--------|-------| +| Threats found | {N} | +| Closed | {M} | +| Open | {K} | +``` + +**ENFORCING GATE:** If `threats_open > 0` after all options exhausted (user did not accept, not all verified closed): + +``` +GSD > PHASE {N} SECURITY BLOCKED +{K} threats open — phase advancement blocked until threats_open: 0 +▶ Fix mitigations then re-run: /gsd:secure-phase {N} +▶ Or document accepted risks in SECURITY.md and re-run. +``` + +Do NOT emit next-phase routing. Stop here. + +## 7. Commit + +```bash +node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs(phase-${PHASE}): add/update security threat verification" +``` + +## 8. Results + Routing + +**Secured (threats_open: 0):** +``` +GSD > PHASE {N} THREAT-SECURE +threats_open: 0 — all threats have dispositions. +▶ /gsd:validate-phase {N} validate test coverage +▶ /gsd:verify-work {N} run UAT +``` + +Display `/clear` reminder. + + + + +- [ ] Security enforcement checked — exit if false +- [ ] Input state detected (A/B/C) — state C exits cleanly +- [ ] PLAN.md threat model parsed, register built +- [ ] SUMMARY.md threat flags incorporated +- [ ] threats_open: 0 → skip directly to Step 6 +- [ ] User gate with threat table presented +- [ ] Auditor spawned with complete context +- [ ] All three return formats (SECURED/OPEN_THREATS/ESCALATE) handled +- [ ] SECURITY.md created or updated +- [ ] threats_open > 0 BLOCKS advancement (no next-phase routing emitted) +- [ ] Results with routing presented on success + diff --git a/get-shit-done/workflows/verify-work.md b/get-shit-done/workflows/verify-work.md index c21dc9ea..0c40cde4 100644 --- a/get-shit-done/workflows/verify-work.md +++ b/get-shit-done/workflows/verify-work.md @@ -375,11 +375,32 @@ Present summary: **If issues > 0:** Proceed to `diagnose_issues` **If issues == 0:** + +```bash +SECURITY_CFG=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.security_enforcement --raw 2>/dev/null || echo "true") +SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1) +``` + +If `SECURITY_CFG` is `true` AND `SECURITY_FILE` is empty: +``` +⚠ Security enforcement enabled — /gsd:secure-phase {phase} has not run. +Run before advancing to the next phase. + +All tests passed. Ready to continue. + +- `/gsd:secure-phase {phase}` — security review (required before advancing) +- `/gsd:plan-phase {next}` — Plan next phase +- `/gsd:execute-phase {next}` — Execute next phase +- `/gsd:ui-review {phase}` — visual quality audit (if frontend files were modified) +``` + +If `SECURITY_CFG` is `false` OR `SECURITY_FILE` exists (i.e., `threats_open: 0` or review already run): ``` All tests passed. Ready to continue. - `/gsd:plan-phase {next}` — Plan next phase - `/gsd:execute-phase {next}` — Execute next phase +- `/gsd:secure-phase {phase}` — security review - `/gsd:ui-review {phase}` — visual quality audit (if frontend files were modified) ```