Batch lookup CVE IDs and GHSA IDs offline, without GitHub API rate limits
TL;DR — To batch lookup CVE and GHSA IDs offline without GitHub API rate limits, load the signed rust-vulns advisory pack into xerj and resolve every ID in one terms query. Each answer names the source file; unknown IDs abstain. On 2026-10-19, 6 IDs took 15 ms: 4 resolved, 2 abstained. Coverage is Rust crates only.
xerj --insecure --data-dir ./xerj-data
Start a single-user node on localhost:9200 in its own terminal.
xerj corpus add rust-vulns --from rust-vulns-pack.zip --verify-sig rust-vulns.pub
Install the advisory pack after checking its signature.
xerj corpus index rust-vulns
Index the RustSec and OSV records into xc-rust-vulns-* indices.
bash batch.sh
Resolve every ID in ids.txt with one _search; print the record and source, or abstain.
Run it
Download the zip, the public key and the signature from the GitHub release pack-rust-vulns-2026-10-19. Then install the pack with a signature check.
xerj --insecure --data-dir ./xerj-data &
xerj corpus add rust-vulns --from rust-vulns-pack.zip --verify-sig rust-vulns.pub
xerj corpus index rust-vulns
The add step printed signature verified in about 1.3 s. It loaded 1,288 RustSec and 745 OSV advisories. Indexing the 2,033 advisories took 9,769 ms.
Put the IDs in a file:
CVE-2021-25900
GHSA-9q5j-jm53-v7vr
CVE-2026-35355
RUSTSEC-2023-0071
CVE-2024-24576
CVE-2099-00001
batch.sh sends all of them in one request and matches each ID against id or aliases:
ids=$(jq -R . ids.txt | jq -sc .)
curl -s 'localhost:9200/xc-rust-vulns-*/_search' -H 'content-type: application/json' -d "{
\"size\": 100,
\"_source\": [\"id\", \"aliases\", \"packages\", \"fixed_versions\", \"origin\", \"source_url\"],
\"query\": {\"bool\": {\"should\": [{\"terms\": {\"id\": $ids}}, {\"terms\": {\"aliases\": $ids}}]}}
}" | jq -r --argjson ids "$ids" '
[.hits.hits[]._source] as $h
| $ids[] as $q
| ($h | map(select(.id == $q or ((.aliases // []) | index($q)))) | first) as $r
| if $r then "\($q) \($r.id) \(($r.packages // []) | join(",")) fixed \(($r.fixed_versions // []) | join(",")) src \($r.source_url) :: \($r.origin)"
else "\($q) NOT FOUND in this pack: abstain" end'
Real output, 15 ms wall time:
CVE-2021-25900 RUSTSEC-2021-0003 smallvec fixed 0.6.14,1.6.1 src https://github.com/rustsec/advisory-db :: crates/smallvec/RUSTSEC-2021-0003.md
GHSA-9q5j-jm53-v7vr RUSTSEC-2022-0051 lz4-sys fixed 1.9.4 src https://github.com/rustsec/advisory-db :: crates/lz4-sys/RUSTSEC-2022-0051.md
CVE-2026-35355 GHSA-239g-2685-54x3 uu_install fixed 0.6.0 src https://osv-vulnerabilities.storage.googleapis.com/crates.io/all.zip :: GHSA-239g-2685-54x3.json
RUSTSEC-2023-0071 RUSTSEC-2023-0071 rsa fixed src https://github.com/rustsec/advisory-db :: crates/rsa/RUSTSEC-2023-0071.md
CVE-2024-24576 NOT FOUND in this pack: abstain
CVE-2099-00001 NOT FOUND in this pack: abstain
RUSTSEC-2023-0071 shows no fixed version because the advisory lists none. A trailing wildcard (xc-rust-vulns-*) is the pattern that worked; see the last section for the two that did not.
Measured on 2026-10-19
| Measure | Value |
|---|---|
| Pack | rust-vulns, 1,598,960 bytes, 2,033 advisories, built 2026-10-19 |
| Signature check and load | about 1.3 s |
| Index | 9,769 ms |
| Batch of 6 IDs, one request | 15 ms wall time |
| Resolved / abstained | 4 / 2 |
| GitHub API calls after download | 0 |
How it compares
| Approach | Offline | Rate limit | Many IDs per call | Source per answer | Coverage |
|---|---|---|---|---|---|
| xerj + rust-vulns pack | yes | none, local | yes, one terms query | yes, file in advisory-db or OSV | Rust crates |
| GitHub advisory REST API | no | yes, per token | no, one cve_id per request | yes | GitHub ecosystems |
| OSV.dev API | no | service limits | yes, querybatch | yes | many ecosystems |
cargo audit | after DB fetch | none | per lockfile, not per ID | yes | Rust crates |
| Chat model | depends | depends | yes | no, may invent an ID | training data |
When not to use xerj
- Your IDs are not Rust crates. The pack holds RustSec and OSV crates.io advisories only; npm, PyPI and Go are not in it.
- You need advisories published since the build date. The pack was built 2026-10-19 and does not update itself.
- You want to audit a
Cargo.lock.cargo auditdoes that directly. - You need
_msearchor a mid-name wildcard. On rc.97,_msearchwithxc-rust-vulns-*returned 404 index_not_found, and_searchonxc-rust-vulns-*-rustsecreturned 0 hits without an error. Use the trailing wildcard as above.
FAQ
How do I batch lookup CVE IDs without the GitHub API?
Download the signed rust-vulns pack once, load it with xerj corpus add and xerj corpus index, then send all IDs in one terms query on id and aliases. Nothing calls the GitHub API after the download.
How do I avoid GitHub API rate limits when looking up advisories?
Query a local copy. The pack is a 1,598,960-byte release asset; after that every lookup is a request to your own node on localhost.
Can I search vulnerability advisories offline?
Yes, for the advisories in the pack: 2,033 records from RustSec (1,288) and OSV crates.io (745), built 2026-10-19. It covers Rust crates only.
How do I cite the source for a CVE's affected versions?
Each record carries source_url and origin, for example crates/smallvec/RUSTSEC-2021-0003.md in rustsec/advisory-db. Print them next to the fixed versions, as the script on this page does.
What happens to an ID that is not in the pack?
The script prints NOT FOUND in this pack: abstain. CVE-2024-24576, a Rust standard library CVE, was not in this crate-advisory pack and abstained.
Is there a built-in batch command?
Not yet. Many IDs per call with a per-item answer or abstain is issue #1801, open. Today the batch is one _search plus jq.
Can I use xerj code with a CVE ID instead?
Not reliably. xerj code rust-vulns "CVE-2021-25900" ranked an lz4 advisory first in our run. Exact ID matching in xerj code is issue #1788; use the terms query.
Evidence
- Six IDs: four resolved, two abstained —
benchmarks/agent-intents-2026-10-19/05-batch-output.txt - The batch script —
benchmarks/agent-intents-2026-10-19/05-batch.sh - xerj code ranked the lz4 advisory first for CVE-2021-25900 —
benchmarks/agent-intents-2026-10-19/05-xerj-code-exact-id.txt - Pack load about 1.3 s, index 9,769 ms, batch 15 ms —
benchmarks/agent-intents-2026-10-19/README.md - Batch queries are open as issue #1801 — https://github.com/xerj-org/xerj/issues/1801