Hooks
What find-cve-agent runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
$ npx -y skills add ByamB4/find-cve-agent --agent claude-codeShips with find-cve-agent. Installing the plugin gets these hooks.
What fires, and when
SessionStart
Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.
- Matches
startup|resume|clear|compactnode "hooks/session-start-context.mjs"
PreToolUse
- Matches
Bashnode "hooks/pretooluse-clone-dedup.mjs" - Matches
Write|Editnode "hooks/pretooluse-finding-selfcheck.mjs"
PostToolUse
- Matches
Readnode "hooks/posttooluse-version-check.mjs"
Where it lives
- hooks/posttooluse-version-check.mjsRunsGitHub
Read the script
#!/usr/bin/env node /** * Post-Tool-Use: Version Check Hook * * After reading package.json, requirements.txt, go.mod, or similar: * - Reminds to check the exact version against NVD/OSV * - Reminds to verify this is the latest version */ import { readFileSync } from "fs"; // Read the tool input/output from stdin let input = ""; try { input = readFileSync("/dev/stdin", "utf-8"); } catch { process.exit(0); } // Check if we're reading a dependency/version file const versionFiles = [ "package.json", "package-lock.json", "requirements.txt", "setup.py", "setup.cfg", "pyproject.toml", "go.mod", "go.sum", "Gemfile", "Gemfile.lock", "composer.json", "Cargo.toml", "pom.xml", ]; const isVersionFile = versionFiles.some((f) => input.includes(f)); if (!isVersionFile) { process.exit(0); } const reminders = []; reminders.push("VERSION CHECK REMINDER:"); reminders.push(" - Note the EXACT version number from this file"); reminders.push(" - Check NVD/OSV for existing CVEs on this exact version:"); reminders.push( ' curl -s "https://api.osv.dev/v1/query" -d \'{"package":{"name":"<pkg>","ecosystem":"<eco>"}}\'' ); reminders.push( " - Verify this is the LATEST release (not an old, already-patched version):" ); reminders.push(" npm view <package> version"); reminders.push(" pip3 index versions <package>"); reminders.push( " - If the version is outdated, check the changelog for security fixes" ); reminders.push( " - A patched version = DUPLICATE, not a new finding. Check before proceeding." ); console.log(reminders.join("\n")); process.exit(0); - hooks/pretooluse-clone-dedup.mjsRunsGitHub
Read the script
#!/usr/bin/env node /** * Pre-Tool-Use: Clone Deduplication Hook * * Before a Bash tool call that contains "git clone": * - Checks if the target repo is already in REGISTRY.md * - Warns if the target has already been investigated * - Checks if the directory already exists locally */ import { readFileSync, existsSync } from "fs"; import { join } from "path"; const projectRoot = process.env.PROJECT_DIR || process.cwd(); const registryPath = join(projectRoot, "REGISTRY.md"); // Read the tool input from stdin let input = ""; try { input = readFileSync("/dev/stdin", "utf-8"); } catch { process.exit(0); } // Only act on git clone commands if (!input.includes("git clone")) { process.exit(0); } // Extract the repo URL or name from the clone command const cloneMatch = input.match(/git clone\s+(?:--[^\s]+\s+)*(?:["']?)([^\s"']+)/); if (!cloneMatch) { process.exit(0); } const repoUrl = cloneMatch[1]; // Extract repo name from URL const repoName = repoUrl .replace(/\.git$/, "") .split("/") .pop(); if (!repoName) { process.exit(0); } const warnings = []; // Check REGISTRY.md for duplicates if (existsSync(registryPath)) { try { const registry = readFileSync(registryPath, "utf-8"); const lowerRegistry = registry.toLowerCase(); const lowerName = repoName.toLowerCase(); if (lowerRegistry.includes(lowerName)) { // Determine which section it's in const sections = [ { name: "IN PROGRESS", pattern: /## IN PROGRESS([\s\S]*?)(?=##|$)/ }, { name: "SUBMITTED", pattern: /## SUBMITTED([\s\S]*?)(?=##|$)/ }, { name: "FALSE POSITIVES", pattern: /## FALSE POSITIVES([\s\S]*?)(?=##|$)/ }, { name: "SKIP", pattern: /## SKIP([\s\S]*?)(?=##|$)/ }, { name: "DUPLICATE", pattern: /## DUPLICATE([\s\S]*?)(?=##|$)/ }, ]; for (const section of sections) { const match = registry.match(section.pattern); if (match && match[1].toLowerCase().includes(lowerName)) { warnings.push( `REGISTRY WARNING: "${repoName}" found in ${section.name} section of REGISTRY.md. Check before proceeding.` ); break; } } } } catch { // Registry couldn't be read - not critical } } // Check if target directory already exists const targetsDir = join(projectRoot, "targets", repoName); if (existsSync(targetsDir)) { warnings.push( `DIRECTORY EXISTS: targets/${repoName}/ already exists locally. The repo may already be cloned.` ); } if (warnings.length > 0) { console.log(warnings.join("\n")); } process.exit(0); - hooks/pretooluse-finding-selfcheck.mjsRunsGitHub
Read the script
#!/usr/bin/env node /** * Pre-Tool-Use: Finding Self-Check Hook * * When writing to findings.md or verdict.md: * - Injects the self-criticism checklist as a reminder * - Reminds about common false positive patterns */ import { readFileSync } from "fs"; // Read the tool input from stdin let input = ""; try { input = readFileSync("/dev/stdin", "utf-8"); } catch { process.exit(0); } // Check if we're writing to a findings or verdict file const isFindings = input.includes("findings.md"); const isVerdict = input.includes("verdict.md"); const isPoc = input.includes("poc_"); if (!isFindings && !isVerdict && !isPoc) { process.exit(0); } const reminders = []; if (isFindings) { reminders.push("SELF-CHECK REMINDER (findings.md):"); reminders.push(" 1. Did you trace the FULL data flow from source to sink?"); reminders.push(" 2. Did you verify the source is attacker-controlled (not internal)?"); reminders.push(" 3. Did you check for validation/sanitization between source and sink?"); reminders.push(" 4. Did you read the README for 'untrusted input' warnings?"); reminders.push(" 5. Is this a real security bug, or intended behavior?"); } if (isVerdict) { reminders.push("SELF-CHECK REMINDER (verdict.md):"); reminders.push(" 1. Did the PoC succeed 3/3 times?"); reminders.push(" 2. Is this the LATEST version of the package?"); reminders.push(" 3. Does exploitation require permissions that already grant equivalent access?"); reminders.push(" 4. Are there runtime/framework protections you haven't checked?"); reminders.push(" 5. Am I hallucinating? LLMs are biased toward seeing bugs."); reminders.push(" 6. Did I actually READ the validation code, or assume it works?"); reminders.push(" 7. For DoS: OOM crash or just a caught RangeError?"); } if (isPoc) { reminders.push("POC REMINDER:"); reminders.push(" - Did the Director approve this PoC plan?"); reminders.push(" - Does the PoC run locally only (no remote targets)?"); reminders.push(" - Does it use the exact version from the target's lockfile?"); reminders.push(" - Does it produce concrete evidence (not 'it might crash')?"); } if (reminders.length > 0) { console.log(reminders.join("\n")); } process.exit(0); - hooks/session-start-context.mjsRunsGitHub
Read the script
#!/usr/bin/env node /** * Session Start Context Hook * * On session start/resume/clear/compact: * - Reads REGISTRY.md if it exists and injects current research state * - Reads any active target brief for context continuity * - Provides a summary of what's in progress */ import { readFileSync, existsSync, readdirSync, statSync } from "fs"; import { join, resolve } from "path"; const projectRoot = process.env.PROJECT_DIR || process.cwd(); const registryPath = join(projectRoot, "REGISTRY.md"); const targetsDir = join(projectRoot, "targets"); const lines = []; // Read REGISTRY.md summary if (existsSync(registryPath)) { try { const content = readFileSync(registryPath, "utf-8"); const inProgressMatches = content.match(/^\|[^|]+\|[^|]+\|[^|]+\|[^|]+\|$/gm); const submittedMatches = content.match( /## SUBMITTED[\s\S]*?(?=##|$)/ ); let inProgressCount = 0; let submittedCount = 0; if (inProgressMatches) { // Subtract header row inProgressCount = Math.max(0, inProgressMatches.length - 5); } // Count submitted entries if (submittedMatches) { const submittedLines = submittedMatches[0] .split("\n") .filter((l) => l.startsWith("|") && !l.includes("---") && !l.includes("Repo")); submittedCount = submittedLines.length; } // Count sections const sections = { in_progress: (content.match(/## IN PROGRESS([\s\S]*?)(?=##|$)/)?.[1] || "") .split("\n") .filter((l) => l.startsWith("|") && !l.includes("---") && !l.includes("Repo")).length, submitted: (content.match(/## SUBMITTED([\s\S]*?)(?=##|$)/)?.[1] || "") .split("\n") .filter((l) => l.startsWith("|") && !l.includes("---") && !l.includes("Repo")).length, false_positives: (content.match(/## FALSE POSITIVES([\s\S]*?)(?=##|$)/)?.[1] || "") .split("\n") .filter((l) => l.startsWith("|") && !l.includes("---") && !l.includes("Repo")).length, skip: (content.match(/## SKIP([\s\S]*?)(?=##|$)/)?.[1] || "") .split("\n") .filter((l) => l.startsWith("|") && !l.includes("---") && !l.includes("Repo")).length, }; lines.push("REGISTRY STATUS:"); lines.push(` In Progress: ${sections.in_progress}`); lines.push(` Submitted: ${sections.submitted}`); lines.push(` False Positives: ${sections.false_positives}`); lines.push(` Skipped: ${sections.skip}`); } catch { // Registry exists but couldn't be parsed - not critical } } else { lines.push( "No REGISTRY.md found. Run /registry or /hunt to initialize." ); } // Check for active targets with briefs if (existsSync(targetsDir)) { try { const targets = readdirSync(targetsDir).filter((d) => { const fullPath = join(targetsDir, d); return statSync(fullPath).isDirectory(); }); if (targets.length > 0) { lines.push(""); lines.push("ACTIVE TARGETS:"); for (const target of targets) { const briefPath = join(targetsDir, target, "brief.md"); const findingsPath = join(targetsDir, target, "findings.md"); const verdictPath = join(targetsDir, target, "verdict.md"); const pocFiles = existsSync(join(targetsDir, target)) ? readdirSync(join(targetsDir, target)).filter((f) => f.startsWith("poc_") ) : []; let status = "recon"; if (existsSync(verdictPath)) { const verdict = readFileSync(verdictPath, "utf-8"); if (verdict.includes("CONFIRMED")) status = "CONFIRMED"; else if (verdict.includes("FALSE_POSITIVE")) status = "false_positive"; else status = "validating"; } else if (pocFiles.length > 0) { status = "poc_ready"; } else if (existsSync(findingsPath)) { status = "findings_ready"; } else if (existsSync(briefPath)) { status = "brief_ready"; } lines.push(` ${target}: ${status}`); } } } catch { // Targets dir exists but couldn't be read - not critical } } if (lines.length > 0) { console.log(lines.join("\n")); }
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
Open Source CVE Hunting Harness for Claude Code A Claude Code plugin that systematically finds real CVEs in open source packages through coordinated multi-agent security research.
Repo: ByamB4/find-cve-agent

