Keep up with security advisories, and keep a shared record of the ones that touched your projects.
-
/cve recent [ecosystem] [days]lists advisories published lately, newest first:/cve recent rust 14. They come from GitHub's advisory database, across ecosystems or one of them (rust, pip, go, npm and others). -
/cve scan [project]scans every project in the Projects setting, or the one named: it reads each repo's lockfiles from git at its ref and asks OSV.dev which of the pinned versions have advisories, in one batch. Each new hit opens an impact as investigating. The answer lists what's new, what's reopened (a fixed record hit again) and what's gone (an open record whose versions have left the ref), with each project's open count. Each hit says whether it's known to be exploited, its EPSS score and its CVSS score, and the lists come fix-first (see below). Itsfixedis the fix for the pinned version's own release line: undici 7.30.0 is fixed in 7.30.1, even though the same advisory also names 6.29.1 for the 6.x line. -
What it reads: Cargo.lock, uv.lock, poetry.lock,
requirements*.txt(and.txtfiles in arequirementsfolder: their==pins), go.sum, package-lock.json, pnpm-lock.yaml, yarn.lock (Yarn 1 and 2+), Gemfile.lock and composer.lock, up to 4 folders down, skipping dependency and build folders. It also skips the project's test data and other projects' code: lockfiles under atests,test,fixtures,testdata,examplesorvendorfolder at any depth. Each answer says how many it skipped. It also reads the actions the repo's own workflows use (.github/workflows/*.yml). An action counts when pinned to a version tag, or to a commit with its tag in a comment (@3d3c42e… # v7.0.1). That comment is taken at its word: if it names the wrong tag, the answer is wrong too. A branch, such as@main, doesn't count. OSV doesn't compare Actions versions, so the extension checks each pinned tag against the advisory's ranges itself. A major tag such asv4counts as the newest release of v4. -
/cve plan <project>gives the upgrade plan at the project's refs. Each pinned package gets one line: its version now, the lowest version that fixes all its advisories (checked against each one's ranges), and how many that fixes, most first. It also names the advisories no upgrade fixes yet. It only reads and records nothing. Every scan's answer carries the same plan, and the pane's view of a project shows each repo's plan from its last scan. -
/cve vex <project>exports the project's records as an OpenVEX 0.2.0 document, posted as a file. Each record is one statement:- the advisory, with its other ids as aliases;
- each repo it was found in as a product (owner/repo as a GitHub purl), with the package versions it hits as purl subcomponents;
- its status in VEX's words (
investigatingisunder_investigation); - for a
not_affectedrecord, its justification and note; for anaffectedone, the upgrade to take; for afixedone, its evidence.author=names who the document says made it.
-
/cve report [projects]builds a page for whoever reviews and merges the fixes, posted as a file: for each project, the PRs that fix its open advisories (known exploited first, then the most severe), what each fixes, and the open advisories with no PR yet. It reads the records'prs, so record a PR on its advisories with/cve set … pr=when you open it. It changes nothing. -
/cve check <folder> [project]does the same for one folder, read as it is on disk. Name a project and it opens an impact there for each one not yet recorded. -
/cve add <advisory> <project>records an advisory that touched a project./cve setupdates the record: its status, where it hit, and the PRs, threads and tasks that dealt with it. -
/cve show <advisory>shows an advisory and every project's record of it./cve impacts [project]lists the records, the open ones first, fix-first. -
The pane opens on your projects: each one's open records, how many are known exploited, and when it was last scanned. Pick one to see:
- its counts by status;
- its open records fix-first, with known-exploited ones flagged;
- each repo's upgrade plan, and when each repo was last scanned, at which ref;
- its closed records.
Tap a record to see where it hit, its evidence (PRs, threads, tasks) and its history, newest first. Everything is in rows, which read on a phone. Records change with
/cve set, not in the pane.
Agents run the same commands with create_job (cve.scan, cve.recent, cve.check, cve.add…). The extension also gives every agent on its nodes a short guide, its behaviour (SKILL.md, details in references/triage.md):
- triage fix-first, after checking the finding is really ours;
- open one task per upgrade from
/cve plan; - fix and release;
- confirm with
/cve scan, then mark records with evidence; - only mark a record not affected with VEX's justification and a note.
evals/ measures it against a fake finding. pointman cve … on the command line acts as the person, so it asks them to approve it, and agents shouldn't use it.
Each impact is a small file this extension keeps in your workspace, impacts/<PROJECT>/<ADVISORY>.toml. Every machine with the extension reads the same files, and every change is a new version, so the history is the audit trail. Nothing leaves your workspace, and there's no repo or account to set up.
advisory = ["emudhra-g3-distrust"]
title = "eMudhra G3 intermediates computable"
project = "MB"
status = "fixed" # investigating | affected | not_affected | fixed
justification = "" # VEX's, for not_affected: vulnerable_code_not_in_execute_path, …
severity = "medium" # yours, beside the published one
fixed_in = "release 32"
prs = ["https://github.com/pointman-ai/pointman/commit/7f5769fa"]
threads = ["pointman://thread/17856"]
tasks = ["MB-588"]
[confirmed."pointman-ai/pointman"] # a scan of that repo found the version gone after it was marked fixed
by = "cve scan on mac-m4 at origin/main (7f5769fa)"
ref = "origin/main"
commit = "7f5769fa…"
at = "2026-10-23T09:00:00Z"
[[where]]
repo = "pointman-ai/pointman"
file = "node/Cargo.lock"
package = "rustls-webpki"
version = "0.103.15"
fixed_version = "0.103.16"
[[history]] # one for each change, oldest first
at = "2026-10-22T10:02:11Z"
by_said = "@patchbay" # who, as the caller said it (by = "…" once the node says who asked)
status = "fixed"
was = "affected"
why = "rustls-webpki 0.103.16 denylists them"
evidence = ["https://github.com/pointman-ai/pointman/commit/7f5769fa", "fixed in release 32"]The statuses are VEX's (Vulnerability Exploitability eXchange), so the records can be turned into a standard report later.
Not affected says why, as VEX does. not_affected takes one of VEX's justifications (component_not_present, vulnerable_code_not_present, vulnerable_code_not_in_execute_path, vulnerable_code_cannot_be_controlled_by_adversary, inline_mitigations_already_exist) and a note in your words, and is refused without both. Records written before 0.8.0 with the older short names are read as VEX's.
Every change is kept. Each /cve add and /cve set, and each record a scan opens, confirms or reopens, adds a [[history]] entry: when, who, the status it left and took, why, and its evidence. /cve show lists it. A command isn't told who asked yet, so for now "who" is the by its caller gives, kept as by_said and shown as "(said)". Once the node passes the asker, that's kept as by and by params are ignored. A scan's own changes are kept as by, naming the scan, its machine and its ref.
Agents mark records; scans check them. Marking a record fixed needs its evidence: pr, commit or fixed_in (/cve set CVE-2026-1234 MB fixed pr=https://github.com/…); without one, it's refused. Each scan then checks the fixed records it has a repo for:
- Still there: the version is still pinned at the ref, so the fix hasn't reached it yet. It's listed as still there and left as it is.
- Confirmed: the version has gone from that repo. The record is marked confirmed for it, once, with the scan, the ref, its commit and the time. A record found in several repos is confirmed once each of them is. One advisory found in another repo of the same project is the same record, which gains that repo's places.
-
Reopened: the version is back in a repo that confirmed it gone. The record turns
affectedagain, with a note. Its PRs stay, as its history.
An open record whose version has gone is listed as gone, and stays open until it's marked fixed with its evidence. Scans, /cve check, /cve add and /cve set run one at a time on a machine, so a scan never overwrites a change made while it runs.
Projects is a list, one entry a project:
[{"key": "MB", "repo": "~/Documents/message-board", "ref": "origin/main"}]-
keyis the Pointman project the records go under. -
name(optional) is what people call it (Spark), in a scan's digest. Repos that share a key share a line there. -
repois its git folder under~/Documents. A scan fetches it first, lists its lockfiles at the ref and reads each one withgit show <ref>:<path>. The working tree is never read, so an old or dirty checkout can't change the answer. A folder that isn't a git repo is read as files, and the answer says so. -
refis a branch, tag or commit:origin/mainif left out. -
skip(optional) names more paths that aren't the project's: a folder (legacy) or a glob (docs/*/yarn.lock). -
include(optional) names paths to read anyway, even under those folders (examples/app, for an example that ships). It wins over both.
A project that can't be read (its folder is missing, its ref unknown, or git fails) gets an error in the answer, and the others are still scanned. The answer and the event count these in failed. A scan is refused only when no project could be read.
Nothing runs on its own. A scan runs when someone asks for one, or when a workflow calls cve.scan. When it's done it sends the event cve.scanned, with the counts in all and for each project. A scan with nothing new still answers, so a workflow can branch on new.
A digest for a person. Beside its full text, a scan answers a few plain lines a workflow can post as they are (a workflow's post escapes Markdown in the values it's given, so they carry none):
-
headline: what's new and reopened (how many known exploited, high or critical), and how many fix PRs wait to be merged. -
waitingandwaiting_text: the fix PRs that wait to be merged, a line each with its link, what it fixes and its upgrades; known exploited first, then the most severe. A PR waits when an open record names it and GitHub says it's open. Where its state can't be had (noghsigned in on the machine, or offline), it waits while the scan still finds its advisories at the ref.waiting_prshas the same as data. -
projects_text: a line a project, its repos together under itsname: new, reopened, open, PRs to merge, gone (to mark fixed) and with no fix yet.
The PRs' states are read with the machine's own gh sign-in (gh pr view <link> --json state). It only reads.
Open records, and each scan's new and reopened hits, are sorted in this order:
- Known exploited: in CISA's Known Exploited Vulnerabilities catalog.
- Likely to be exploited: a higher EPSS score, FIRST's estimate of the chance it's exploited in the next 30 days, compared in whole percentage points (0.05% against 0.03% is noise, so severity decides).
- Severity: critical, high, medium, low, unknown.
- CVSS: the higher base score.
Both lists are by CVE id, so an advisory with no CVE (many RUSTSEC ones) isn't in KEV and has no EPSS. Each is fetched at most twice a day. If one can't be reached, the answer says so and the order falls back to severity. In /cve recent, the CVSS score is GitHub's: its CVSS 3 score, or its 4.0 score when that's all there is. In scans, it's computed from OSV's CVSS 3.x vector, and a RUSTSEC or PYSEC advisory that names no severity gets one from that score. A CVSS 4.0 vector from OSV isn't scored, so its advisory's own severity stands.
The KEV catalog is CC0 and used as published; this extension isn't endorsed by CISA. EPSS scores are free to use, and FIRST asks for credit: answers that show them end with "EPSS scores: FIRST, https://www.first.org/epss".
- Network: GitHub's advisory database (60 requests an hour without signing in), OSV.dev, CISA's KEV feed and FIRST's EPSS API.
- It reads only lockfiles, and only under
~/Documents(itsfolderspermission, shown when it's added): a folder anywhere else is refused. It runsgit fetch,git ls-treeandgit showin the repos you list, and never changes their working trees or local branches.
PYTHONPATH=.:tests python3 -m unittest discover -s testsSet POINTMAN_EXTENSIONS_SDK to the node's Python SDK folder (one that speaks the typed protocol, with pointman_extension_types.py) to run the end-to-end tests too. They run the extension under the SDK's stand-in node, with a stand-in for the node's files, and never touch the network (CVE_OFFLINE=1, or CVE_OSV_URL pointing the scan tests at a stand-in OSV).
MIT licensed.