/verify
[stable] Verify findings with Joern plus code review and update the shared findings artifact
$ npx -y skills add allsmog/vuln-scout --agent claude-codeHow it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/verify
Context preview
What this command does when you run it.
[stable] Verify findings with Joern plus code review and update the shared findings artifact
Command definition
verify.mdname: verify
description: "[stable] Verify findings with Joern plus code review and update the shared findings artifact"
argument-hint: "<file:line> [--type sql-injection|command-injection|xss|path-traversal|ssrf|reentrancy|integer-overflow] [--all-critical] [--from file] [--no-cache] [--json]"
allowed-tools:
- Bash
- Read
- Write
- Grep
- Glob
- TodoWrite
- Task
Verification Workflow
Verify whether a reported item is a real vulnerability, a false positive, or still unresolved.
Philosophy
Pattern matches and sink heuristics produce leads. Verification decides whether those leads stay `hotspot`, become `finding`, or close as `false_positive`.
Modes
Single target
/vuln-scout:verify routes/login.ts:34 --type sql-injection
Batch verify critical findings
/vuln-scout:verify --all-critical
Verify from prior results
/vuln-scout:verify --from .claude/findings.json
Language support
First-party Joern verification is implemented for:
- JavaScript / TypeScript
- Python
- Go
- Java
- PHP
- Ruby
- C# when the local Joern frontend is available
Solidity verification is Slither-owned. Joern should return `verdict: na_cpg` for Solidity findings instead of trying to build a Solidity CPG.
If the target language is not supported for the requested verifier:
- return `verdict: na_cpg`
- do **not** convert that into `false_positive`
Step 1: Load or create the CPG cache
Cache CPGs by target hash plus language so repeated verification does not restart from scratch.
TARGET_HASH=$(git ls-files -z . | xargs -0 shasum | shasum | awk '{print $1}')
LANGUAGE=<detected-language>
CPG_FILE=".joern/${TARGET_HASH}-${LANGUAGE}.cpg"
if [ ! -f "$CPG_FILE" ] || [ "$NO_CACHE" = "true" ]; then
mkdir -p .joern
joern-parse . --output "$CPG_FILE"
fiStep 2: Choose verifier
| Type | Script | |------|--------| | `sql-injection` | `verify-sqli.sc` | | `command-injection` | `verify-cmdi.sc` | | `xss` | `verify-xss.sc` | | `path-traversal` | `verify-path.sc` | | `ssrf` | `verify-ssrf.sc` | | other / mixed | `verify-generic.sc` |
Step 3: Run Joern
Single verification:
joern --script "${CLAUDE_PLUGIN_ROOT}/scripts/joern/verify-${VULN_TYPE}.sc" \
--params cpgFile="$CPG_FILE",file="$FILE",line="$LINE"Batch verification:
joern --script "${CLAUDE_PLUGIN_ROOT}/scripts/joern/batch-verify.sc" \
--params cpgFile="$CPG_FILE",findingsFile=".claude/findings.json"Joern output should resolve to one of:
- `VERIFIED`
- `FALSE_POSITIVE`
- `NEEDS_REVIEW`
- `NA_CPG`
Step 4: Run the cold verification loop
Use the `false-positive-verifier` as a cold reviewer. Do NOT pass prior chain-of-thought; pass only:
- finding type and location
- Joern result
- code context
- required evidence:
- source evidence
- hop chain
- control evidence
- exploitability evidence
Review contract for the orchestration loop:
- `APPROVED`
- `CHANGES_REQUESTED`
Workflow: 1. Run Joern and collect the initial verification result. 2. Invoke `false-positive-verifier` with only the evidence bundle. 3. If the reviewer returns `CHANGES_REQUESTED`, revise the candidate, downgrade it to `hotspot`, or leave it as `needs_review`, then invoke a NEW cold reviewer. 4. Stop after 3 rounds maximum. 5. If the finding is still not settled after round 3, keep the final artifact verdict as `needs_review` and add `[REVIEWER NOTE: unresolved]` to `.claude/review-ledger.json`.
Persist each verification review round in `.claude/review-ledger.json`:
- `subject_type: "finding-verification"`
- `subject_id`
- `round`
- `reviewers`
- `status`: `APPROVED`, `CHANGES_REQUESTED`, or `UNRESOLVED`
- `notes`
Step 5: Update `.claude/findings.json`
Use `vuln-scout/references/findings.schema.json` as the contract.
Verification updates must preserve:
- `schema_version`
- `stable_key`
- `kind`
- `source_tool`
- `evidence`
Verification updates may change:
- `verdict`
- `confidence`
- `message`
- `evidence`
Recommended rules:
- keep `kind: hotspot` if attacker control is still unproven
- promote `hotspot -> finding` only when verification shows a real exploit path
- unsupported language -> `verdict: na_cpg`
- unresolved review loops stay `verdict: needs_review`
Step 6: Update `.claude/review-ledger.json`
Alongside `.claude/findings.json`, update `.claude/review-ledger.json` with the cold-review history for each verified item. The ledger is the machine-readable contract for:
- which rounds happened
- which reviewer angle requested changes
- whether the outcome ended in `APPROVED`, `CHANGES_REQUESTED`, or `UNRESOLVED`
When a finding remains unresolved, include `[REVIEWER NOTE: unresolved]` in `notes`.
Example verified entry:
{
"id": "VSCOUT-001",
"stable_key": "semgrep:sql-injection:routes/login.ts:34",
"kind": "finding",
"severity": "critical",
"type": "sql-injection",
"title": "SQL injection in login handler",
"file": "routes/login.ts",
"line": 34,
"verdict": "verified",
"confidence": "verified",
"source_tool": "joern",
"message": "User-controlled input reaches db.query without parameterization.",
"evidence": [
{
"type": "data-flow",
"label": "req.body.email to db.query",
"path": "routes/login.ts",
"line": 34,
"excerpt": "db.query(`SELECT ... ${email}`)"
}
]
}JSON mode
When `--json` is passed:
- emit the updated `.claude/findings.json`
Notes
- Verification is allowed to dismiss a finding, but unsupported-language verification must never do that by default.
- Batch mode is preferred when verifying many findings because it keeps one Joern JVM alive.
- The shared findings schema is the source of truth, not the inline example.
- `.claude/review-ledger.json` is the source of truth for verification review rounds.
Read more
name: verify description: "[stable] Verify findings with Joern plus code review and update the shared findings artifact" argument-hint: "<file:line> [--type sql-injection|command-injection|xss|path-traversal|ssrf|reentrancy|integer-overflow] [--all-critical] [--from file] [--no-cache] [--json]" allowed-tools: - Bash - Read - Write - Grep - Glob - TodoWrite - Task
Verification Workflow
Verify whether a reported item is a real vulnerability, a false positive, or still unresolved.
Philosophy
Pattern matches and sink heuristics produce leads. Verification decides whether those leads stay `hotspot`, become `finding`, or close as `false_positive`.
Modes
Single target
/vuln-scout:verify routes/login.ts:34 --type sql-injection
Batch verify critical findings
/vuln-scout:verify --all-critical
Verify from prior results
/vuln-scout:verify --from .claude/findings.json
Language support
First-party Joern verification is implemented for:
- JavaScript / TypeScript
- Python
- Go
- Java
- PHP
- Ruby
- C# when the local Joern frontend is available
Solidity verification is Slither-owned. Joern should return `verdict: na_cpg` for Solidity findings instead of trying to build a Solidity CPG.
If the target language is not supported for the requested verifier:
- return `verdict: na_cpg`
- do **not** convert that into `false_positive`
Step 1: Load or create the CPG cache
Cache CPGs by target hash plus language so repeated verification does not restart from scratch.
TARGET_HASH=$(git ls-files -z . | xargs -0 shasum | shasum | awk '{print $1}')
LANGUAGE=<detected-language>
CPG_FILE=".joern/${TARGET_HASH}-${LANGUAGE}.cpg"
if [ ! -f "$CPG_FILE" ] || [ "$NO_CACHE" = "true" ]; then
mkdir -p .joern
joern-parse . --output "$CPG_FILE"
fiStep 2: Choose verifier
| Type | Script | |------|--------| | `sql-injection` | `verify-sqli.sc` | | `command-injection` | `verify-cmdi.sc` | | `xss` | `verify-xss.sc` | | `path-traversal` | `verify-path.sc` | | `ssrf` | `verify-ssrf.sc` | | other / mixed | `verify-generic.sc` |
Step 3: Run Joern
Single verification:
joern --script "${CLAUDE_PLUGIN_ROOT}/scripts/joern/verify-${VULN_TYPE}.sc" \
--params cpgFile="$CPG_FILE",file="$FILE",line="$LINE"Batch verification:
joern --script "${CLAUDE_PLUGIN_ROOT}/scripts/joern/batch-verify.sc" \
--params cpgFile="$CPG_FILE",findingsFile=".claude/findings.json"Joern output should resolve to one of:
- `VERIFIED`
- `FALSE_POSITIVE`
- `NEEDS_REVIEW`
- `NA_CPG`
Step 4: Run the cold verification loop
Use the `false-positive-verifier` as a cold reviewer. Do NOT pass prior chain-of-thought; pass only:
- finding type and location
- Joern result
- code context
- required evidence:
- source evidence
- hop chain
- control evidence
- exploitability evidence
Review contract for the orchestration loop:
- `APPROVED`
- `CHANGES_REQUESTED`
Workflow: 1. Run Joern and collect the initial verification result. 2. Invoke `false-positive-verifier` with only the evidence bundle. 3. If the reviewer returns `CHANGES_REQUESTED`, revise the candidate, downgrade it to `hotspot`, or leave it as `needs_review`, then invoke a NEW cold reviewer. 4. Stop after 3 rounds maximum. 5. If the finding is still not settled after round 3, keep the final artifact verdict as `needs_review` and add `[REVIEWER NOTE: unresolved]` to `.claude/review-ledger.json`.
Persist each verification review round in `.claude/review-ledger.json`:
- `subject_type: "finding-verification"`
- `subject_id`
- `round`
- `reviewers`
- `status`: `APPROVED`, `CHANGES_REQUESTED`, or `UNRESOLVED`
- `notes`
Step 5: Update `.claude/findings.json`
Use `vuln-scout/references/findings.schema.json` as the contract.
Verification updates must preserve:
- `schema_version`
- `stable_key`
- `kind`
- `source_tool`
- `evidence`
Verification updates may change:
- `verdict`
- `confidence`
- `message`
- `evidence`
Recommended rules:
- keep `kind: hotspot` if attacker control is still unproven
- promote `hotspot -> finding` only when verification shows a real exploit path
- unsupported language -> `verdict: na_cpg`
- unresolved review loops stay `verdict: needs_review`
Step 6: Update `.claude/review-ledger.json`
Alongside `.claude/findings.json`, update `.claude/review-ledger.json` with the cold-review history for each verified item. The ledger is the machine-readable contract for:
- which rounds happened
- which reviewer angle requested changes
- whether the outcome ended in `APPROVED`, `CHANGES_REQUESTED`, or `UNRESOLVED`
When a finding remains unresolved, include `[REVIEWER NOTE: unresolved]` in `notes`.
Example verified entry:
{
"id": "VSCOUT-001",
"stable_key": "semgrep:sql-injection:routes/login.ts:34",
"kind": "finding",
"severity": "critical",
"type": "sql-injection",
"title": "SQL injection in login handler",
"file": "routes/login.ts",
"line": 34,
"verdict": "verified",
"confidence": "verified",
"source_tool": "joern",
"message": "User-controlled input reaches db.query without parameterization.",
"evidence": [
{
"type": "data-flow",
"label": "req.body.email to db.query",
"path": "routes/login.ts",
"line": 34,
"excerpt": "db.query(`SELECT ... ${email}`)"
}
]
}JSON mode
When `--json` is passed:
- emit the updated `.claude/findings.json`
Notes
- Verification is allowed to dismiss a finding, but unsupported-language verification must never do that by default.
- Batch mode is preferred when verifying many findings because it keeps one Joern JVM alive.
- The shared findings schema is the source of truth, not the inline example.
- `.claude/review-ledger.json` is the source of truth for verification review rounds.
AI-powered whitebox penetration testing plugin for Claude Code. 9 languages, 22 skills, 7 autonomous agents. STRIDE threat modeling, OWASP 2025 coverage, polyglot monorepo support.
Repo: allsmog/vuln-scout
Other commands on vuln-scout.
- /auto-fix
[experimental] Auto-remediate verified findings by generating patches and optionally creating a PR
Open command - /create-rule
[experimental] Create a custom Semgrep detection rule from a confirmed vulnerability pattern
Open command - /diff
[stable] Compare security posture between two git refs to find new/fixed vulnerabilities and track regression
Open command - /full-audit
[stable] End-to-end security audit with hotspot-aware framework pivots, shared findings.json schema, and CI-friendly workflow flags
Open command - /mobile-audit
[beta] Audit a decompiled Android target — scans jadx_out/sources + apktool_out together and merges findings
Open command - /mutate
[experimental] Security mutation testing -- weaken security controls and check if the scanner detects the resulting vulnerability
Open command

