Skip to content
Security
Hook

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.

From plugin
find-cve-agent
6021 skills5 agents7 commands3 hooks

Claude Code

$ npx -y skills add ByamB4/find-cve-agent --agent claude-code

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.

  • Matchesstartup|resume|clear|compactnode "hooks/session-start-context.mjs"

PreToolUse

  • MatchesBashnode "hooks/pretooluse-clone-dedup.mjs"
  • MatchesWrite|Editnode "hooks/pretooluse-finding-selfcheck.mjs"

PostToolUse

  • MatchesReadnode "hooks/posttooluse-version-check.mjs"
Read hooks/hooks.json

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.

@byamb4
Ships withfind-cve-agent

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.

6021 skills5 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
60
Star history of ByamB4/find-cve-agent on GitHub, a chart by star-history.com

More plugins that ship hooks.

watermarks-remover

watermarks-remover

Agent skill + stdlib Python service to strip multi-vendor AI provenance marks from text and files, for privacy and hygiene on content you own. The skill is a thin client: it drives the machinery over HTTP, so the agent host needs no Python.

2 skills1 hook27k
@guillaumemeyer@guillaumemeyer
Install Plugin
agentic-bug-hunter

agentic-bug-hunter

Runs recon on a target, tests it for real, reportable vulnerabilities, checks each finding against a strict validation gate, and writes a report ready to submit to bug bounty platforms. It also remembers patterns across targets and resumes unfinished sessions.

25 skills+35.9k
@awarexone@awarexone
Install Plugin
agentic-bug-hunter

agentic-bug-hunter

A Claude Code plugin for bug bounty hunters, bundling skills for reconnaissance, vulnerability hunting such as IDOR, SSRF and XSS, black-box audits of deployed AI agents, and CI/CD pipeline security.

25 skills+35.9k
@shuvonsec@shuvonsec
Install Plugin
cc-safety-net

cc-safety-net

A pre-execution guard that blocks destructive Git and file system commands, and common attempts to read sensitive files, before a tool call runs. Works with several AI coding agents, including Claude Code.

2 skills1 hook1.9k
@kenryu42@kenryu42
Install Plugin
claude-code-safety-net

claude-code-safety-net

A pre-execution guard that blocks destructive Git and file system commands, plus common attempts to read sensitive files, before an AI coding agent runs a tool call. Works with Claude Code and several other agent CLIs.

2 skills1 hook1.9k
@kenryu42@kenryu42
Install Plugin
pentest-agents

pentest-agents

A bug bounty agent framework for Claude Code that automates reconnaissance, vulnerability hunting and exploit chaining, with skills for analyzing recon output, running autonomous hunt loops and correlating findings into attack chains.

39 skills+31.1k
@h-mmer@h-mmer
Install Plugin
vestige

vestige

Vestige is a fail-closed runtime firewall for AI agents. The model proposes, a deterministic gate decides, and every action leaves a receipt. When something breaks, causal_walk finds the real cause.

3 agents+1660
@samvallad33@samvallad33
Install Plugin
cti-expert

cti-expert

A cyber threat intelligence and OSINT analysis toolkit that runs structured investigations and produces analyst-style reports with sourced, trust-scored findings. Built for security analysts working OSINT and CTI cases in Claude Code.

1 skill+2624
@7onez@7onez
Install Plugin

Questions about the hooks in find-cve-agent.

Which coding agents can use the hooks in find-cve-agent?
The hooks in find-cve-agent install in Claude Code and Cursor: each runs Claude Code hooks from .claude/settings.json, so copy the hooks there.