Skip to content
Security
Command

/verify

[stable] Verify findings with Joern plus code review and update the shared findings artifact

From plugin
vuln-scout
2215 skills9 agents15 commands
Install
$ npx -y skills add allsmog/vuln-scout --agent claude-code

How 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.md
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"
fi

Step 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
Ships withvuln-scout

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.

Get the whole plugin, auto-invoked
Stats
22
Stars
1
Views
3
Forks
Maintained
Maintenance
Python
Language
MIT
License
1mo ago
Last commit
5mo ago
Created

Repo: allsmog/vuln-scout