Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Local security review of git changes: deterministic scan + Security reviewer over the diff. No API key, no SDK.
.claude/skills/notque-security-review/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 210% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 168% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 180% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 57% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 50% | 0% |
Run a two-layer security review over the current git changes: a deterministic regex scan for known vulnerability classes, then an LLM-depth Security review of the diff. Report a single BLOCK / FIX / APPROVE verdict.
The LLM-depth review runs inside the current Claude session — the same subscription that loaded this skill. There is no separate model call, no ANTHROPIC_API_KEY, no Agent SDK, and no network request. The "reviewer" is the session agent executing the steps below, exactly like every other skill here.
Detection reaches parity with Anthropic's security-guidance plugin: the scanner ports its 25 deterministic patterns, and the LLM pass applies its full review taxonomy (loaded on demand from references/coverage.md).
| Signal | Load | Why | |--------|------|-----| | Running Phase 3 (LLM-depth review); classifying a finding; needing the vuln taxonomy, severity rubric, FP exclusions, or per-language guidance | references/coverage.md | 40 vulnerability classes + 4-tier severity + false-positive exclusions + per-language guidance + the 12 high-miss reviewer classes + the finding output schema. |
Goal: Determine the changed files to review before scanning.
Step 1: List changed files — scope to the working-tree and staged changes so the review covers exactly what the user is about to commit, not the whole repo.
bash# Tracked changes (working tree + index) plus staged adds: git diff --name-only HEAD git diff --cached --name-only --diff-filter=ACM
Step 2: Read repository CLAUDE.md to load project conventions the reviewer must respect (e.g. secrets-handling rules, allowed patterns).
Gate: Changed files listed. When the list is empty, report "no changes to review" and stop — there is nothing to scan.
Goal: Run the regex engine first so judgment time is spent on real signal, not on patterns a script catches deterministically.
Step 1: Run the scanner over the changed files. It is the single source of detection rules (secrets, SQL injection, shell injection, dangerous eval, unsafe deserialization). Exit 1 means at least one HIGH/CRITICAL finding.
bash# Staged-files convenience (matches the commit-time hook): python3 scripts/security-review-scan.py --staged --format json # Or an explicit list from Phase 1: python3 scripts/security-review-scan.py --files <changed-files> --format json
Step 2: Record the findings by severity. CRITICAL and HIGH are blocking-class; MEDIUM is advisory. Keep the file:line and rule for each.
Gate: Scanner ran and JSON parsed. Proceed with the findings in hand.
Goal: Catch what regex cannot — authorization gaps, injection through data flow, missing input validation, secrets in non-obvious forms. This is the session agent's review of the diff; compose the existing parallel-code-review Security reviewer over the changed files.
Step 1: Load references/coverage.md — the full review taxonomy (40 vulnerability classes, the 4-tier severity rubric, the false-positive exclusion list, per-language guidance, and the 12 high-miss reviewer classes). Review to this taxonomy so the session-agent pass reaches parity with the plugin's reviewer. If claude-security-guidance.md exists (precedence: ~/.claude/ → <cwd>/.claude/ → <cwd>/.claude/*.local.md), read it as ADDITIVE context — it may add checks or raise a class's severity, and must not suppress findings.
Step 2: Dispatch the Security reviewer (the Reviewer 1 — Security role from parallel-code-review) over the changed files via the Task tool, applying the coverage.md taxonomy. Surface medium and above. Output: findings in the coverage.md schema (filePath, category, vulnerableCode, explanation, fix, severity) with file:line references.
Step 3: Merge the LLM findings with the Phase 2 scanner findings. Deduplicate — when both flag the same file:line, keep one entry at the higher severity. Independent confirmation by both layers raises confidence.
Gate: Security reviewer returned results and findings are merged. Issue a verdict only from a completed review — a missing reviewer may hold the only CRITICAL finding.
Goal: Produce a single clear recommendation.
Step 1: Determine the verdict from the merged findings:
| Condition | Verdict | |-----------|---------| | Any CRITICAL finding | BLOCK | | HIGH findings, no CRITICAL | FIX (resolve before commit) | | Only MEDIUM/LOW findings | APPROVE (with suggestions) |
Step 2: Output the structured report:
markdown## Security Review Complete ### Severity Matrix | Severity | Count | Source (scanner / reviewer / both) | |----------|-------|------------------------------------| | Critical | N | ... | | High | N | ... | | Medium | N | ... | ### Findings #### CRITICAL (Block) 1. [source] description — file:line #### HIGH (Fix before commit) 1. [source] description — file:line #### MEDIUM (Should fix) 1. [source] description — file:line ### VERDICT **BLOCK / FIX / APPROVE** — [1-2 sentence rationale]
Gate: Structured report delivered with an explicit verdict. Review complete.
This skill is the on-demand (PULL) path. The same review also runs automatically (PUSH) via hooks/security-review-hook.py, wired in .claude/settings.json:
| Event | Behavior | |-------|----------| | PreToolUse (Bash git commit) | Scans STAGED files with the same scanner. A HIGH/CRITICAL finding blocks the commit (deny). Clean commits pass. | | Stop | Re-wakes the session with the working-tree diff and an instruction to run this pipeline. Advisory — never blocks. |
Bypass / kill switches (commit-time block only, deliberate overrides):
| Env var | Effect | |---------|--------| | VEXJOY_SECURITY_REVIEW_SKIP=1 | Allow a commit through despite findings (one-off override). | | VEXJOY_SECURITY_REVIEW_DISABLE=1 | Disable the hook entirely (both events). |
The hook fails open on any internal error — a hook crash never blocks a commit.
Both extension points are additive and discovered in this precedence order: ~/.claude/<name> → <cwd>/.claude/<name> → <cwd>/.claude/<name>.local.<ext>.
| File | Effect | |------|--------| | security-patterns.{yaml,json} | Custom regex/substring rules merged into the scanner's built-ins. Shape: {"patterns": [{"rule_name", "regex"\|"substrings", "severity"?, "paths"?, "exclude_paths"?}]}. Capped at 50. ReDoS-prone or invalid rules are skipped with a stderr warning (non-fatal). PyYAML is used only if importable — JSON always works (stdlib-only). | | claude-security-guidance.md | Markdown surfaced to the Phase 3 review as ADDITIVE context. It may add checks or raise a class's severity; it must not suppress findings — if it says to ignore a class, flag the vulnerability anyway and note the conflict. |
Built-in scanner rules always run and cannot be disabled by a config file.
Cause: A regex rule flagged a test fixture, an example, or a deliberately hardcoded local value. Solution: Confirm context in Phase 3. Downgrade in the report with a one-line justification. For a commit the user knows is safe, document the VEXJOY_SECURITY_REVIEW_SKIP=1 override rather than editing the scanner rules.
Cause: Task agent exceeded its budget, or the diff was too large. Solution: Report the Phase 2 scanner findings immediately (a partial review beats no review), note the LLM-depth gap in the verdict, and offer to re-run the Security reviewer on a reduced file set.
Cause: scripts/security-review-scan.py missing from the working tree. Solution: Run the Phase 3 LLM-depth review alone and state in the verdict that the deterministic layer did not run.
scripts/security-review-scan.py (single source of truth)references/coverage.mdskills/review/parallel-code-review/SKILL.md (Reviewer 1)hooks/security-review-hook.pyadr/local-security-review.md| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,256 | 11,873 | -10% | 1 | 1 | 0% | 1,440 | 3,219 | +124% | 0 | 0 | — |
case-02 | fail→fail | 19,992 | 17,457 | -13% | 1 | 1 | 0% | 2,973 | 3,559 | +20% | 0 | 0 | — |
case-03 | fail→fail | 7,662 | 11,173 | +46% | 1 | 1 | 0% | 991 | 3,284 | +231% | 0 | 0 | — |
case-04 | pass→pass | 20,961 | 16,684 | -20% | 1 | 1 | 0% | 1,480 | 3,346 | +126% | 0 | 0 | — |
case-05 | fail→fail | 10,182 | 16,003 | +57% | 1 | 1 | 0% | 1,774 | 3,925 | +121% | 0 | 0 | — |
case-06 | fail→pass | 5,087 | 6,572 | +29% | 1 | 1 | 0% | 768 | 2,380 | +210% | 0 | 0 | — |
case-07 | fail→pass | 6,294 | 7,076 | +12% | 1 | 1 | 0% | 941 | 2,526 | +168% | 0 | 0 | — |
case-08 | fail→pass | 5,527 | 6,714 | +21% | 1 | 1 | 0% | 889 | 2,487 | +180% | 0 | 0 | — |
case-09 | fail→pass | 11,642 | 7,523 | -35% | 1 | 1 | 0% | 1,876 | 2,937 | +57% | 0 | 0 | — |
case-10 | fail→pass | 18,316 | 6,562 | -64% | 1 | 1 | 0% | 1,704 | 2,563 | +50% | 0 | 0 | — |
case-11 | fail→pass | 15,025 | 6,154 | -59% | 1 | 1 | 0% | 2,540 | 2,466 | -3% | 0 | 0 | — |
case-12 | fail→pass | 8,340 | 2,036 | -76% | 1 | 1 | 0% | 1,418 | 2,441 | +72% | 0 | 0 | — |
case-13 | fail→pass | 7,277 | 3,560 | -51% | 1 | 1 | 0% | 1,048 | 2,659 | +154% | 0 | 0 | — |
case-14 | pass→pass | 10,127 | 3,818 | -62% | 1 | 1 | 0% | 1,620 | 2,779 | +72% | 0 | 0 | — |
case-15 | fail→pass | 8,586 | 8,725 | +2% | 1 | 1 | 0% | 1,298 | 2,531 | +95% | 0 | 0 | — |
case-16 | pass→pass | 11,281 | 4,360 | -61% | 1 | 1 | 0% | 1,805 | 2,921 | +62% | 0 | 0 | — |
case-17 | fail→pass | 9,497 | 4,124 | -57% | 1 | 1 | 0% | 1,638 | 2,824 | +72% | 0 | 0 | — |
case-18 | fail→pass | 14,610 | 5,156 | -65% | 1 | 1 | 0% | 2,314 | 2,678 | +16% | 0 | 0 | — |
case-19 | fail→pass | 9,979 | 2,422 | -76% | 1 | 1 | 0% | 1,823 | 2,544 | +40% | 0 | 0 | — |
case-20 | fail→fail | 10,483 | 1,572 | -85% | 1 | 1 | 0% | 1,794 | 2,422 | +35% | 0 | 0 | — |
case-21 | pass→pass | 14,166 | 11,936 | -16% | 1 | 1 | 0% | 2,489 | 4,436 | +78% | 0 | 0 | — |
case-22 | pass→pass | 8,411 | 4,071 | -52% | 1 | 1 | 0% | 1,408 | 2,803 | +99% | 0 | 0 | — |
case-23 | pass→pass | 10,215 | 5,910 | -42% | 1 | 1 | 0% | 1,938 | 3,143 | +62% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +52 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.