Releases: moonrunnerkc/dumpscan
Release list
v1.0.0 - reproducible, signed vulnerability claims
Two scanners run against the same image disagree about 66 to 80 percent of what they find, and neither can tell you why, because neither wrote down what it was looking at. A scan report pins a wall clock timestamp and nothing else: no version for the advisory database, none for the version comparison, none for the suppressions. "The scan was clean on Tuesday" is unfalsifiable by Friday.
dumpscan is built the other way round. Every result is a claim that pins four digests, and anyone holding the claim can re-derive it.
The guarantee
Same input digest, same feed digest, same comparator ruleset digest, same exclusions digest gives a byte-identical findings file, on any OS, any Node 22 or later, any locale, any timezone. Those four digests ride in a signed in-toto predicate alongside the findings Merkle root.
When the answer changes, dumpscan diff names which of the four moved and shows the evidence, down to the JSON pointer:
input same sha256:725b0669...
feed moved sha256:8fb337fd... -> sha256:93ad3d65...
comparator same sha256:c3baa729...
exclusions same none
2 findings changed
changed feed npm cheeseparser 4.17.20 DUMPSCAN-NPM-0001
changed feed npm cheeseparser 4.17.20 DUMPSCAN-NPM-0003
/affected/0/ranges/0/events/1/fixed "4.17.21" -> "5.0.0"
/withdrawn "2025-02-19T08:00:00Z" -> absent
That second one is an advisory somebody retracted in February coming back. dumpscan keeps withdrawn findings in the set with a status rather than dropping them, which is the only reason there was anything to compare against. A change that nothing accounts for exits 3, because that is a bug in dumpscan rather than a fact about your code.
What is in 1.0.0
Eight commands. snapshot, scan, replay, verify, diff, prove, explain, publish. All take --json.
Eleven lockfile parsers. package-lock.json v2 and v3, pnpm-lock.yaml v6 and v9, yarn.lock classic and Berry, uv.lock, poetry.lock, Pipfile.lock, requirements.txt when fully pinned, Cargo.lock v3 and v4, go.sum with go.mod, Maven dependency:list, and gradle.lockfile. Each has a golden fixture test.
Five version comparators written from each ecosystem's reference implementation: SemVer 2.0.0, PEP 440, Cargo, Go module ordering including pseudo-versions, and Maven ComparableVersion. Each is property tested for antisymmetry and transitivity and checked against a committed corpus. The hash of those corpora is comparatorRulesetDigest, so a semantics change is visible even at an unchanged package version.
OSV ranges evaluated as event lists, not through a semver library's satisfies. Introduced, fixed, last_affected, and limit, per the OSV schema.
Keyless Sigstore signing with certificate identity, Rekor inclusion, and offline verification against the TUF trust root. verify never re-scans.
Merkle inclusion proofs for a finding against the findings root and for an advisory record against the feed root. An advisory that is not in the set comes back with every leaf, so a verifier can recompute the root and look.
Exclusions and OpenVEX, with excluded findings kept in the set with a status so a suppression is a change someone can see.
explain aligns Grype or Trivy output to the same manifest and buckets every disagreement as an identifier mismatch, a feed difference, a range interpretation, or a suppression. It is a diagnostic and is never signed.
A composite GitHub Action with base-branch comparison and SARIF for code scanning.
@dumpscan/canon (RFC 8785 canonicalization) and @dumpscan/merkle (RFC 6962 trees and proofs) have zero dependencies and are useful on their own.
How the determinism is enforced
canon,merkle,versions,match, andpredicateare lint-enforced pure.Date.now,new Date,Math.random,process.env,Intl,localeCompare,toLocaleLowerCase, bare.sort(), and the filesystem and network modules are banned in those packages by rule.- The JCS implementation is fuzz tested against a second implementation written from the RFC with its own escape table and comparator.
pnpm test:determinismruns the fixture corpus twice, underTZ=UTC LANG=CandTZ=Pacific/Kiritimati LANG=tr_TR.UTF-8, and byte compares. CI runs it on ubuntu, macos, and windows and diffs all three.- 752 tests. Coverage 94.87 percent overall, 100 percent on
canonandmerkle. Mutation score 93.81 percent onversionsandmatch, floor 90.
That last one caught a real bug: the published snapshot archive was a zip, and every JavaScript zip writer derives its MS-DOS timestamp from local-time Date methods, so the same snapshot packed in Denver and in Berlin produces different bytes, and packing fails outright west of UTC. It is a gzipped tar with constant headers now.
Install
npm install --global dumpscan
dumpscan snapshot --ecosystems npm,PyPI --out ./snapshot
dumpscan scan package-lock.json --snapshot ./snapshot --sign
dumpscan verify dumpscan.bundle.json --issuer https://token.actions.githubusercontent.com
Node 22.22.2 or later. No native modules.
What it deliberately does not do
No --latest: the caller pins a snapshot digest, because a scanner that picks the newest feed for you cannot make the same claim twice. No reachability, exploitability, or risk scoring. No dropped findings. No guessing at a range it cannot evaluate, which becomes unevaluated with a reason rather than "not affected". No wrapping Grype or Trivy. Lockfiles only in v1: CycloneDX and SPDX bring back the identifier ambiguity that causes cross-tool chaos in the first place.
Known limits
- Consistency proofs are implemented and tested in
merklebut no command emits one. Neither the feed tree nor the findings tree is append-only, so there is no pair of trees a consistency proof would be sound between. ADR 0013. - The daily snapshot publishing workflow is covered end to end against a local store but has never run against real OSV, because this repository is new.
- The launch writeup in
docs/launch.mduses this repository's fixtures rather than a public project's lockfiles, and says so. Grype and Trivy were not available in the environment it was written in.
docs/build-guide.md is the specification, docs/decisions/ holds thirteen ADRs, and docs/progress.md is the build log.
dumpscan feed snapshots
Content addressed OSV feed snapshots. Each asset is named after its feed digest. index.json maps a date to the digest published that day.