Claude Code mod that spots CVE IDs in Claude's replies and, on hover, shows a card enriched with data from vuln.mlab.sh.
I looked at the xz backdoor, see CVE-2024-3094, and Log4Shell CVE-2021-44228.
⚠ CVE-2024-3094 10 ⚠ CVE-2021-44228 10 KEV
╰─ hover → card floats above
- Detects
CVE-YYYY-NNNN(case-insensitive) in every block of Claude's replies. - Adds a row of chips under the text, one per CVE, colored by CVSS severity:
magenta = CRITICAL, red = HIGH, yellow = MEDIUM, green = LOW, gray = unknown or still loading.
The badge shows the CVSS score, plus
KEVwhen the CVE is in the CISA KEV catalog. - Hovering a chip floats a card above the text, without shifting anything:
- severity and CVSS score, KEV with its due date;
- publication date, EPSS and its percentile, CWE;
- description (200 characters max), affected products;
- link to
https://vuln.mlab.sh/cve/<id>.
- Data comes from the public API
https://vuln.mlab.sh/api/v1/cve/<id>, no token needed. Each CVE is fetched once per session. - The stored message is untouched: only the display changes, and Claude still reads the original text.
- Claude Code with mod support (function hooks / hooks modules), in the terminal or the desktop app's Code tab.
- Network access to
vuln.mlab.sh.
git clone git@github.com:mlab-sh/mlab-claude-mod.git ~/mlab-claude-modclaude --plugin-dir ~/mlab-claude-modThe folder is watched: any code change hot-reloads the mod.
The desktop app cannot take --plugin-dir. Declare the folder in ~/.claude/settings.json instead (project settings are not read for this):
{
"env": {
"CLAUDE_CODE_PLUGIN_DIRS": "~/mlab-claude-mod"
}
}To load several folders, separate the paths with : (macOS/Linux) or ; (Windows). Then restart the app or the session.
Ask Claude to mention a CVE, e.g. "tell me about CVE-2021-44228". A ⚠ CVE-2021-44228 10 KEV chip appears under the reply; hover it.
- Mouse hover only: needs a terminal that reports the mouse (fullscreen mode) or the desktop app.
- Card clipped on short replies: a card cannot extend past its message. On a reply shorter than about 7 lines, its top gets cut off.
- Chips under the text: the text is rendered as one block, so the CVE isn't highlighted inside the sentence; a chip is added below instead.
- No retry: if the API fails or doesn't know the CVE, the chip shows
?until the session ends. - Black background: the card has an opaque black background, which stands out in light theme. Change
backgroundColorinhooks/register.tsxto adjust it.
.claude-plugin/plugin.json plugin manifest (name, version, types contract)
hooks/hooks.json declares the hooks module
hooks/register.tsx all of the mod's code
types/index.d.ts shape of a CVE card + shared state (cve-hover.cves)
How hooks/register.tsx works:
- A
ui.renderhook onAssistantMessagereceives each reply block's text. With no CVE in it, it falls back to the default rendering. - Otherwise it redraws the block: the original text (
Markdown), then the chip row. - For each unknown CVE,
ensure()calls$.http.fetchand stores the result in thecve-hover.cvesstate. That write redraws the chips and cards. - Each chip is a
Boxwith akey, which makes it its own hover scope. Its card is aBoxwithposition="absolute"anddisplay="none", switched todisplay: 'flex'on hover, so it overlays the text without moving it.
Validate the manifest and the module:
claude plugin validate .The report lists the hooks, the $ calls, the state keys read and written, and anything the engine would refuse.
Run with hot reload:
claude --plugin-dir .If a hook fails, a dim cve-hover: … line appears in the conversation. claude --debug has the details.
The .claude-plugin/types/ folder is generated by Claude Code for editor typing; git ignores it.