I write code with an AI agent every day. It is fast, it is careful, and on security it is quietly
out of date — not because it is careless, but because a model's training data has a cutoff, while
advisories are published every day. Ask your assistant whether a dependency is safe and you get an
answer shaped by whatever was public months ago.
So I built the missing part: a tool the assistant can call that reads today's public advisory
data, ranks it, and can prove a fix worked.
opencraysec — MIT, Rust, one static binary. A CLI and an
MCP server. No account, no API key, no telemetry. Your source code never leaves the machine.
curl -fsSL https://raw.githubusercontent.com/amgio38/opencraysec/main/scripts/install.sh | sh
opencraysec update --for . # advisories for the ecosystems this project uses
opencraysec scan .
claude mcp add opencraysec -- opencraysec mcp
What it actually does
It reads the lockfiles you already have — Cargo, npm/yarn/pnpm, composer, go.mod, Python — plus the
GitHub Actions your workflows pin, and matches exact versions against OSV, the CISA Known Exploited
Vulnerabilities list, FIRST EPSS, the MITRE CWE catalog and NVD. Findings are ranked: malicious
package first, then exploited in the wild, then likely to be exploited, then CVSS.
Two things make it more useful than a list of CVEs:
- Usage evidence. For Rust and Go it looks in your own source and tells you whether you import the affected package (down to the function). A finding your code never touches sinks in the ranking — it is never hidden, just ordered honestly.
-
Fix → verify → record.
plan_fixgives the exact upgrade commands and modifies nothing.verify_fixre-scans and reports which advisories are gone — and refuses to call something "resolved" unless an earlier scan of the same path actually reported it.record_fixlogs what changed and why, like a commit message, so the dashboard shows the assistant's reasoning, the measured lockfile change and the commands it ran.
Three real runs
All of these are from opencraysec 0.20261022.4 with advisory data downloaded on 2026-10-22
(crates.io 2,834 records; npm 229,905; Packagist 7,182; Go 9,582; PyPI 25,783).
1. A private Go + TypeScript service repository (anonymised: ten lockfiles, four services and a
front-end):
opencraysec: 258 packages in 10 lockfile(s), 115 finding(s)
data npm local 2026-10-22T12:38:09Z (100 packages)
data Go local 2026-10-20T17:07:58Z (158 packages)
[HIGH 7.5] GHSA-fx2h-pf6j-xcff vite@5.4.21
vite: `server.fs.deny` bypass on Windows alternate paths
weakness: CWE-200, CWE-22
usage: your code imports it [admin-ui/vite.config.ts:4]
fix: 6.4.3 (admin-ui/package-lock.json)
115 findings, 22 of them critical, in 2.4 seconds — and the ones that mattered were the ones my own
code imports. Nothing here was a surprise to the advisory data; all of it was a surprise to the agent
that had been happily editing those files.
2. A 250,000-line Rust workspace (the agent harness I run on):
opencraysec: 856 packages in 4 lockfile(s), 1 finding(s)
[UNKNOWN] RUSTSEC-2026-0173 proc-macro-error2@2.0.1
proc-macro-error2 is unmaintained
usage: not imported by your code (may be used by another dependency)
fix: none published (Cargo.lock)
One finding, and it is "this crate is unmaintained", not a vulnerability. A tool that reported
"856 packages, 0 problems, you're secure" would be lying; this one says what it checked, what it could
not, and how old the data is.
3. The files that steer the assistant. This is the part I did not expect to need. Two files — a
CLAUDE.md and an .mcp.json:
opencraysec: 2 assistant configuration file(s) read, 3 finding(s)
[HIGH] agent-exfiltrate CWE-200 CLAUDE.md:4 (suspected)
A line sends credentials, keys or environment variables to a URL
[MEDIUM] agent-mcp-unpinned CWE-1357 .mcp.json:3 (suspected)
An MCP server runs a package with no exact version, so it cannot be checked
[MEDIUM] agent-override-instruction CWE-1427 CLAUDE.md:4 (suspected)
A line tells the assistant to ignore its earlier instructions
Rule files, MCP server settings, hooks, auto-run tasks and skills are executable configuration: they
are instructions your agent will follow. scan_agent_config reads them for injected instructions,
invisible characters, commands that download and run code, unpinned packages and secrets — every rule
naming its CWE and the OWASP entry behind it. The MCP packages a server or skill starts are checked
against the same advisory and malicious-package data.
What it is not
The tool is deliberately loud about its own limits, because a false "clean" is worse than no scan:
- Matching uses lockfile versions. Usage evidence is source text, not a call graph — it can miss
dynamic use and cannot see use by other dependencies.
not_foundmeans "your code does not import it", not "safe". -
scan-codeandscan-agent-configare pattern matching with small rule packs. Every hit is suspected; read the code path before believing it. - A
requirements.txtline without an exact==pin cannot be matched at all. - The activity log is not rotated yet.
- If a result is incomplete — no lockfile, unreadable lockfile, no advisory data for an ecosystem — it
says so, and
--fail-onexits 3 rather than passing your CI on nothing.
Why give this to an agent rather than run it yourself
You can, both work. But the interesting case is the agent: it can scan before choosing a version, plan
the minimal upgrades, apply them, re-scan, and record why — inside one conversation, with the report as
evidence. Before this, "is this dependency fine?" was a question the assistant had to answer from memory.
Now it is a question it can look up.
Repo, docs, prebuilt binaries for Linux and macOS (Windows via WSL2):
https://github.com/amgio38/opencraysec — MIT licensed, issues and dissent welcome.
Top comments (0)