skills/security-threat-model/SKILL.md
Security threat model: scan toolkit for attack surface, supply-chain risks.
npx skillsauth add notque/claude-code-toolkit security-threat-modelInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill executes a structured, phase-gated security threat model workflow that scans the toolkit installation for attack surface exposure, supply-chain injection patterns, and learning DB contamination. It follows the toolkit's four-layer architecture: deterministic Python scripts perform all checks and produce JSON artifacts; Phase 5 (synthesis only) is the LLM step. Each phase gates on artifact validation before proceeding.
Outputs are saved to security/ with a shared run_id for correlation across phases.
Phase 5 produces an actionable threat model document.
Goal: Enumerate the active attack surface of the current installation.
Create the security/ output directory and run the surface scan script:
mkdir -p security
python3 scripts/scan-threat-surface.py --output security/surface-report.json
This script enumerates:
~/.claude/settings.json) with file paths and event types~/.claude/mcp.json and .mcp.json)skills/) with allowed-tools entrieshooks/, skills/, or agents/ containing ANTHROPIC_BASE_URLValidate output:
python3 -c "import json; d=json.load(open('security/surface-report.json')); print('hooks:', len(d.get('hooks',[])), '| skills:', len(d.get('skills',[])), '| mcp_servers:', len(d.get('mcp_servers',[])))"
Gate (ARTIFACT VALIDATION): security/surface-report.json must exist, parse as valid JSON, and contain hooks, skills, and mcp_servers keys. A missing directory is handled gracefully with empty arrays. All artifacts are written to security/ before gating. Do not proceed to Phase 2 until this gate passes.
Goal: Produce a concrete deny-list config derived from Phase 1 findings.
Generate the deny-list from the surface report:
python3 scripts/generate-deny-list.py \
--surface security/surface-report.json \
--output security/deny-list.json
The script applies these mappings from surface findings to deny rules:
curl or wget → append "Bash(curl *)" and "Bash(wget *)"ssh or scp → append "Bash(ssh *)" and "Bash(scp *)"allowed-tools contains unscoped Read(*) or Write(*) → add path-scoped deny entriesANTHROPIC_BASE_URL override → append "Bash(* ANTHROPIC_BASE_URL=*)"Always includes static baseline deny rules for credentials and privileged operations:
["Read(~/.ssh/**)", "Read(~/.aws/**)", "Read(**/.env*)",
"Write(~/.ssh/**)", "Write(~/.aws/**)",
"Bash(curl * | bash)", "Bash(ssh *)", "Bash(scp *)", "Bash(nc *)",
"Bash(* ANTHROPIC_BASE_URL=*)"]
Display deny-list for human review:
python3 -c "
import json
d = json.load(open('security/deny-list.json'))
print('Deny-list entries to add to settings.json:')
for rule in d['permissions']['deny']:
print(' ', rule)
print()
print('Review security/deny-list.json before merging.')
"
Gate (HUMAN APPROVAL REQUIRED): The deny-list is produced for human review only — it is never merged automatically. Display the diff and block until the operator confirms review. This gate is the highest-ROI control in the workflow. In --ci-mode, skip this gate and proceed to Phase 3. Do not proceed without explicit acknowledgment.
Goal: Scan all installed hooks, skills, and agents for injection patterns and hidden characters.
Run the supply-chain audit:
python3 scripts/scan-supply-chain.py \
--scan-dirs hooks/ skills/ agents/ \
--output security/supply-chain-findings.json
Detection patterns (full regex details in scripts/scan-supply-chain.py source):
| Pattern | Severity |
|---------|----------|
| Zero-width + bidi Unicode characters | CRITICAL |
| HTML comments and hidden payload blocks | CRITICAL |
| ANTHROPIC_BASE_URL override in any file | CRITICAL |
| Instruction-override and role-hijacking phrases | CRITICAL |
| Outbound network commands in hooks/skills | WARNING |
| enableAllProjectMcpServers setting | WARNING |
| Broad permission grants without path scoping | WARNING |
Check for CRITICAL findings:
python3 -c "
import json, sys
d = json.load(open('security/supply-chain-findings.json'))
crits = [f for f in d.get('findings', []) if f.get('severity') == 'CRITICAL']
warns = [f for f in d.get('findings', []) if f.get('severity') == 'WARNING']
print(f'CRITICAL: {len(crits)}, WARNING: {len(warns)}')
if crits:
for c in crits:
print(f' CRITICAL: {c[\"file\"]}:{c.get(\"line\",\"?\")} -- {c[\"pattern\"]}')
sys.exit(1)
"
Gate (BLOCKING CRITICAL FINDINGS): Any CRITICAL finding halts forward progress. All CRITICAL findings must be remediated or explicitly acknowledged before Phase 4 can start. This includes zero-width Unicode, ANTHROPIC_BASE_URL overrides, hidden payloads, and instruction-override phrases. WARNING findings are logged but do not block. Log warnings in the threat model under "Gaps and Recommended Next Controls" with acceptance rationale.
Goal: Inspect the learning DB for entries that may contain injected content from external sources.
Run the sanitization check in dry-run mode (never mutates without explicit --purge):
python3 scripts/sanitize-learning-db.py \
--output security/learning-db-report.json
Flags entries where:
key or value contain instruction-override or role-hijacking phrasessource is pr_review, url, or external (high-risk origins)value contains zero-width Unicode or base64 blobsfirst_seen is older than 90 days and source indicates external originReview flagged entries:
python3 -c "
import json
d = json.load(open('security/learning-db-report.json'))
flagged = d.get('flagged_entries', [])
print(f'Total flagged: {len(flagged)}')
for e in flagged[:10]:
print(f' [{e[\"severity\"]}] id={e[\"id\"]} source={e.get(\"source\",\"?\")} action={e[\"action\"]}')
if len(flagged) > 10:
print(f' ... and {len(flagged)-10} more. See security/learning-db-report.json')
"
Gate (DRY-RUN BY DEFAULT): The script operates in dry-run mode by default. No rows are deleted without explicit operator request and --purge flag. Present the report to the operator. If purge is desired after review, re-run with --purge. Learning DB is not found gracefully — script produces an empty report (total_entries: 0, flagged_entries: []). Proceed to Phase 5 when operator acknowledges the report or when no entries are flagged.
Goal: Synthesize Phases 1-4 findings into an actionable threat model document. This is the only LLM-driven phase.
Load all phase artifacts:
security/surface-report.jsonsecurity/deny-list.jsonsecurity/supply-chain-findings.jsonsecurity/learning-db-report.jsonWrite security/threat-model.md with these required sections (validator checks for exact headings):
# Threat Model
## Run Metadata
## Attack Surface Inventory
## Active Threats
## Mitigations In Place
## Gaps and Recommended Next Controls
## Deny-List Status
## Supply-Chain Audit Summary
## Learning DB Sanitization Summary
Write security/audit-badge.json:
{
"status": "pass",
"timestamp": "2026-01-01T00:00:00Z",
"run_id": "from-surface-report",
"critical_count": 0,
"warning_count": 0,
"phases_completed": 5
}
Status is fail if any CRITICAL finding was not remediated or if any phase gate did not pass.
Validate outputs:
python3 scripts/validate-threat-model.py \
--threat-model security/threat-model.md \
--badge security/audit-badge.json
Gate (ARTIFACT VALIDATION WITH RETRY LIMIT): validate-threat-model.py must exit 0. If validation fails, add the missing sections and re-run. Maximum 3 fix iterations before escalating to operator for review.
Cause: A hook, skill, or agent contains zero-width Unicode, ANTHROPIC_BASE_URL override, or known injection phrase.
Resolution:
--exclude list and re-run scan-supply-chain.pyCause: Phase 5 synthesis omitted a required section heading.
Resolution: Read the validator output for the exact missing section name. Add the section to security/threat-model.md with content synthesized from the phase artifacts and re-run validate-threat-model.py. Maximum 3 fix iterations before escalating to operator.
Cause: ~/.claude/settings.json or learning DB doesn't exist.
Resolution: These are handled gracefully by the scripts:
settings.json → surface-report produces empty arrays for hookstotal_entries: 0 and flagged_entries: []
These are not error conditions. Re-run with --verbose for detail on missing paths.documentation
Document translation: quick/normal/refined modes with chunked parallel subagents and glossary support.
development
AI image generation: Gemini and Nano Banana backends; single/series/batch workflows with prompt-to-disk.
testing
Unified voice content generation pipeline with mandatory validation and joy-check. 13-phase pipeline: LOAD, GROUND, STATS-CHECKPOINT, GENERATE, HOOK-GATE, VALIDATE, REFINE, VARIETY-GATE, JOY-CHECK, ANTI-AI, CLOSE-GATE, OUTPUT, CLEANUP. Use when writing articles, blog posts, or any content that uses a voice profile. Use for "write article", "blog post", "write in voice", "generate content", "draft article", "write about".
documentation
Critique-and-rewrite loop for voice fidelity validation.