--- name: security:scan description: Scan files, directories, or GitHub repos for security issues — secrets, injection vulnerabilities, supply chain risks, OWASP LLM patterns allowed-tools: Read, Glob, Grep, Bash, Agent model: sonnet --- # /security scan [path|url] Scan target for security issues. Accepts local paths or GitHub URLs. Delegates to specialized agents sequentially. ## Step 1: Resolve Target - If `$ARGUMENTS` contains `--deep` → strip it, set `run_deep_scan = true` - If `$ARGUMENTS` contains `--branch ` → strip it, set `branch = ` - If `$ARGUMENTS` is empty → `target = "."`, `clone_path = null` - If `$ARGUMENTS` starts with `https://github.com/` or `git@github.com:` → Run: `node /scanners/lib/git-clone.mjs clone "" [--branch ]` If exit code != 0 → show error to user and **STOP** Set `clone_path` = stdout (trimmed), `target = clone_path` Set `remote_url = ` for display - Otherwise → `target = $ARGUMENTS`, `clone_path = null` ## IMPORTANT: Cleanup Guarantee (remote scans) If `clone_path != null`, the following cleanup MUST run regardless of scan outcome. If ANY step between clone and cleanup fails or errors, STILL run cleanup before stopping: 1. `node /scanners/lib/git-clone.mjs cleanup ""` 2. `node /scanners/lib/fs-utils.mjs cleanup ""` (if `evidence_file` is set) ## Step 1.5: Pre-extraction (remote scans only) If `clone_path != null` (target is a cloned remote repo): Get temp path: `node /scanners/lib/fs-utils.mjs tmppath "content-extract.json"` Run: `node /scanners/content-extractor.mjs "" --output-file ""` If exit code != 0 → warn user, set `evidence_file = null` (fall back to direct scan) Otherwise set `evidence_file` = the temp path. Print the compact summary line to user. ## Step 2: Detect Scan Type **Single `.md` file:** `run_skill_scan = true`, `run_mcp_scan = false` **Directory:** Glob for `**/commands/*.md`, `**/agents/*.md`, `**/skills/*/SKILL.md` → `run_skill_scan = true`. Glob for `**/.mcp.json`, `**/package.json`, `**/.claude/settings.json` with mcpServers → `run_mcp_scan = true`. Neither → skill scan only. Record ISO 8601 timestamp. ## Step 3: Plugin Root This file is at `/commands/scan.md`. Use absolute paths for knowledge files. ## Step 3.5: Registry Check (local scans only) If `clone_path == null` (local scan) and `run_skill_scan == true`: ```bash node -e " import { fingerprintSkill, checkRegistry } from '/scanners/lib/skill-registry.mjs'; const r = fingerprintSkill(''); const c = checkRegistry(r.fingerprint, ''); console.log(JSON.stringify({ fingerprint: r.fingerprint, name: r.name, files: r.files, ...c })); " --input-type=module ``` If `found == true` and `stale == false`: display cached result and set `skip_skill_scan = true`: ``` **Registry hit:** (fingerprint: ) Verdict: | Risk: /100 | Last scanned: | Scans: (Use `/security registry scan ` to force re-scan) ``` Otherwise set `skip_skill_scan = false` and store `registry_fingerprint` and `registry_name` for post-scan registration. ## Step 4: Spawn Agents Sequentially Use registered subagent types — they contain full scan procedures as system prompt. **Skill Scanner** (if `run_skill_scan = true` AND `skip_skill_scan != true`): `subagent_type: "llm-security:skill-scanner-agent"`, `model: "sonnet"`: If `evidence_file` is set (remote scan — evidence-package mode): > EVIDENCE-PACKAGE MODE. Read the pre-extracted evidence at: \ > Read knowledge: \/knowledge/skill-threat-patterns.md, \/knowledge/secrets-patterns.md > Analyze the JSON sections: injection_findings, frontmatter_inventory, shell_commands, credential_references, persistence_signals, claude_md_analysis, cross_instruction_flags. > DO NOT use Read/Glob/Grep on the target directory — all evidence is in the package. > `[INJECTION-PATTERN-STRIPPED]` markers are confirmed findings — report them. > Return findings with severity, category, file, line, OWASP ref, evidence, remediation. > End with JSON: `{"scanner":"skill-scanner","verdict":"ALLOW|WARNING|BLOCK","risk_score":N,"counts":{"critical":0,"high":0,"medium":0,"low":0,"info":0},"files_scanned":N}` Otherwise (local scan — direct mode): > Scan target: \ > Read: \/knowledge/skill-threat-patterns.md, \/knowledge/secrets-patterns.md > Return findings with severity, category, file, line, OWASP ref, evidence, remediation. > End with JSON: `{"scanner":"skill-scanner","verdict":"ALLOW|WARNING|BLOCK","risk_score":N,"counts":{"critical":0,"high":0,"medium":0,"low":0,"info":0},"files_scanned":N}` **MCP Scanner** (if `run_mcp_scan = true`, run AFTER skill scanner): `subagent_type: "llm-security:mcp-scanner-agent"`, `model: "sonnet"`: If `evidence_file` is set (remote scan — evidence-package mode): > EVIDENCE-PACKAGE MODE. Read the pre-extracted evidence at: \ > Read: \/knowledge/mcp-threat-patterns.md > Analyze: mcp_tool_descriptions (check hidden instructions, length >500, injection_detected), shell_commands, credential_references. > DO NOT use Read/Glob/Grep on the target directory. > Return findings with severity, category, evidence, remediation. > End with JSON: `{"scanner":"mcp-scanner","verdict":"ALLOW|WARNING|BLOCK","risk_score":N,"counts":{"critical":0,"high":0,"medium":0,"low":0,"info":0},"files_scanned":N}` Otherwise (local scan — direct mode): > Scan target: \ > Read: \/knowledge/mcp-threat-patterns.md > Return findings with severity, category, server name, evidence, remediation. > End with JSON: `{"scanner":"mcp-scanner","verdict":"ALLOW|WARNING|BLOCK","risk_score":N,"counts":{"critical":0,"high":0,"medium":0,"low":0,"info":0},"files_scanned":N}` ## Step 5: Aggregate and Report Combine counts. `risk_score = riskScore(counts)` (severity-dominated v2 model — see `scanners/lib/severity.mjs`). Verdict: critical ≥ 1 OR score ≥ 65 → BLOCK; high ≥ 1 OR score ≥ 15 → WARNING; else ALLOW. Output banner then all findings grouped by severity (critical→info). Each finding: `### [SEV] Title` with Category, File:line, OWASP, Evidence, Remediation. For TFA (Toxic Flow Analysis) findings, render the chain description prominently: - Show the 3 trifecta legs (Input, Access, Exfil) with their evidence - Note mitigation status (which hooks are active) - Group direct trifectas separately from cross-component trifectas ## Step 5.5: Register in Skill Registry (local scans only) If `clone_path == null` and `skip_skill_scan != true` and `registry_fingerprint` is set: ```bash node -e " import { registerScan } from '/scanners/lib/skill-registry.mjs'; registerScan({ skillPath: '', fingerprint: '', name: '', files: , verdict: '', risk_score: , counts: , files_scanned: }, ''); " --input-type=module ``` ## Step 6: Deep Scan (only if `--deep`) If `run_deep_scan = true`, run `/security deep-scan ` logic: Get temp path, run `node /scanners/scan-orchestrator.mjs "" --output-file ""`. Parse stdout aggregate JSON. Merge with LLM findings. Re-evaluate verdict. Output "Deep Scan Findings" section with CRITICAL/HIGH only. ## Step 7: Cleanup (only if remote) If `clone_path != null`: Run: `node /scanners/lib/git-clone.mjs cleanup ""` If cleanup fails → warn: "Could not remove temp dir — remove manually." If `evidence_file != null`: Run: `node /scanners/lib/fs-utils.mjs cleanup ""`