name: security-strategy
description: |
On-demand maintenance of the per-repo security memory: bootstrap an
initial threat model + add/append incidents to docs/security-strategy.md
with proper marker comments, then refresh the Supabase index. NEVER
gates planning — /plan reads the resulting memory automatically via
Phase 0.5b.
Triggers on: "security strategy", "add security incident", "draft
incident", "/security-strategy", "bootstrap security memory".
Usage:
/security-strategy bootstrap — interview to seed initial threat model + first incident
/security-strategy add-incident — interactive draft of one new incident entry
/security-strategy add-incident from-commit
disable-model-invocation: true
/security-strategy — proactive security memory maintenance
This skill maintains the markdown source-of-truth at
docs/security-strategy.md. After any edit, it triggers
npm run security:refresh so the Supabase embedding index stays current.
The skill is on-demand only — it never blocks /cycle, /plan, or
/ship. The planner (Phase 0.5b) consults the memory via the cross-skill
bridge regardless of whether this skill has run recently.
Step 0 — Parse Mode
| Input | Mode |
|---|---|
/security-strategy bootstrap |
BOOTSTRAP — first-time seed of threat model + optional first incident |
/security-strategy add-incident |
ADD — interactive draft of one new incident |
/security-strategy add-incident from-commit <sha> |
ADD_FROM_COMMIT — pre-fill from a security-relevant commit |
If no mode given → ask the user which one.
Step 1a — BOOTSTRAP
The repo template at docs/security-strategy.md is the canonical
skeleton. Bootstrap fills in placeholders rather than emitting a
parallel template — so changes to the template's structure (parser
sections, comment markers, header text) only need to happen in one
place and bootstrap stays automatically in sync.
If the file is already populated (the threat-model placeholder text
"_(no threat model recorded yet" is absent), ask: "File appears already
bootstrapped — overwrite the threat-model section, or skip to add-incident?"
On overwrite, take a .bak copy first.
Otherwise interview the user briefly:
- "What does this app/repo handle? (1-2 sentences)" → assets
- "Who is the realistic attacker model? (drive-by/insider/state-level)" → actors
- "Compliance regime that applies, if any? (PCI / GDPR / SOC2 / HIPAA / none)"
- "Top 1-2 security concerns you currently have for this repo, in plain English"
Then read the existing checked-in template at
docs/security-strategy.md, replace the ## Threat model body
between the heading and ## Incidents with a paragraph synthesising
the user's answers (Assets / Actors / Compliance / Concerns), and
write back via the round-trip parse + atomicWriteFileSync protocol
(R3-M4):
- Read current file; locate
## Threat model…## Incidentsspan. - Substitute the body, leave headings + the
## Incidentsplaceholder comments untouched. - Pass full new content through
parseSecurityStrategy()— assert no parse warnings; if any, fail loudly and don't write. - Call
atomicWriteFileSync('docs/security-strategy.md', content).
Then run npm run security:refresh and surface its summary line.
Step 1b — ADD or ADD_FROM_COMMIT
Determine the incident's id: scan existing markdown for highest
INC-NNN, increment by 1. Pad to 3 digits (INC-001, INC-002, …).
For ADD_FROM_COMMIT: pre-fill from git show <sha>:
- description: commit subject line (sanitised — no AI co-authoring trailers)
- affected_paths:
git show --name-only <sha>filtered to source files - mitigation_ref: try to detect: if commit added a Semgrep rule under
semgrep/, reference it; else "manual" - lessons_learned: empty initially — prompt user to fill in
For interactive ADD, prompt the user for each field with examples.
Write protocol (R3-M4):
- Read current
docs/security-strategy.md. - Insert the new incident block at the END of the
## Incidentssection, BEFORE any## Historical incidentsheading if present. - The block format:
<!-- incident:start id="INC-NNN" --> **Description**: <text> **Affected paths**: `<path1>`, `<path2>` **Mitigation**: `<semgrep:rule-id | scripts/path | manual>` **Lessons learned**: <text> <!-- incident:end --> - Round-trip parse the new full file content via
parseSecurityStrategy()— assert the new entry appears inincidents[]with non-nulldescriptionAND no parse warnings reference the new ID. - Only on round-trip success →
atomicWriteFileSync(). - Run
npm run security:refreshand surface result summary. - If round-trip fails → output the parsed warnings, do NOT write, ask the user to revise.
Step 2 — Surface results
═══════════════════════════════════════
/security-strategy — DONE
Mode: <BOOTSTRAP | ADD | ADD_FROM_COMMIT>
File: docs/security-strategy.md (<N> incidents total)
Refresh: <upserted N, swept M, on-default-branch=Y/N>
═══════════════════════════════════════
If on a feature branch and the user added an incident, gently note: "Incidents from feature branches UPSERT but don't trigger sweep — the canonical retired-set updates when the default branch refreshes."
Hard rules
- Never write
docs/security-strategy.mdwithout round-trip parse first. A malformed entry that the parser silently skips is worse than a noisy warning. - Never include real secrets (API keys, passwords, payment data) in the markdown — even though
redactSecrets()runs before egress, the markdown itself goes to PR review and git history. - Never inflate the threat model. Real assets + realistic actors only. A false threat model misleads /plan worse than no threat model.
- One incident per security-relevant fix — not per CVE in a third-party dep, not per Dependabot bump. Genuine post-incident learning material.