Skip to content

Advisory cross-check + version resolution + recent-scan harness - #20

Closed
acuciureanu wants to merge 3 commits into
mainfrom
feat/advisory-cross-check
Closed

acuciureanu wants to merge 3 commits into
mainfrom
feat/advisory-cross-check

Conversation

@acuciureanu

Copy link
Copy Markdown
Owner

What & why

Building a "scan the latest libraries for 0days" harness surfaced two real correctness bugs in the disclosure classifier — both of which made known CVEs masquerade as undocumented 0-day candidates. This PR fixes them and adds the harness.

Fixes (the important part)

  1. fix(target): resolve dist-tag/range specs to the installed version. --target foo@latest kept the literal "latest" as config.version, so OSV/GitHub-Advisory-DB lookups queried "latest" (matching nothing) and any reproduced pollution was labelled undocumented. Now the concrete installed version (from node_modules/<pkg>/package.json) is used for discovery, the PoC, the reported version, and advisory lookups.
  2. fix(disclosure): cross-check the GitHub Advisory DB, not OSV alone. Adds the npm bulk advisory endpoint as a second live source (adapted to the OSV vuln shape and merged, so the classifier is unchanged). A finding is undocumented only when the static DB, OSV, and the GitHub Advisory DB all have nothing. Provenance (ghsa vs osv) is reported honestly; --no-osv governs both.

Validation: deep-defaults@latest and deep-set@latest — previously false "undocumented" candidates — now correctly classify as known-cve (CVE-2021-25944, CVE-2020-28276), reproduced 2× in the hardened container.

Feature

feat(discovery): scripts/discovery/ — scan-recent.mjs (discover PP-prone packages published since a cutoff) → sweep.sh (fuzz each in the sandbox, reusing run-sandboxed.sh) → triage.mjs (surface only reproduced undocumented findings). Plus .github/workflows/recent-scan.yml (daily + on-demand, informational, opens an issue only on a surviving candidate). run-sandboxed.sh is now TTY-aware so it works in loops/CI.

The tool is the finder, never the disclosure vehicle — see scripts/discovery/README.md.

Tests

319 tests, 317 pass / 0 fail (2 pre-existing skips); lint clean. New coverage for the npm-advisory source/adapter and the installed-version resolver.

🤖 Generated with Claude Code

acuciureanu and others added 3 commits July 31, 2026 18:38
The disclosure classifier decided "known-cve vs undocumented" from OSV.dev
plus the built-in static DB only. OSV is not a complete mirror of the GitHub
Advisory Database, so real npm prototype-pollution advisories are missing from
it — e.g. deep-defaults (CVE-2021-25944 / GHSA-h6xg-rg33-9mf4) and deep-set
(GHSA-wgxm-rg53-h2c6), both critical and both affecting their latest published
version. With OSV as the only live source, the classifier labelled these
"undocumented-vulnerability", i.e. manufactured false 0-day candidates.

Add a second live source: the GitHub Advisory Database via the npm registry
bulk endpoint (the source `npm audit` uses; no auth). Each advisory is adapted
into the OSV vuln shape and merged into the same array the classifier already
consumes, so classification logic is unchanged — a finding is "undocumented"
only when the static DB, OSV, AND the GitHub Advisory DB all say unknown.
Provenance is reported honestly (ghsa vs osv). --no-osv now governs both live
sources. Fail-safe identical to osv.js: never throws, degrades gracefully.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CfAYfHFiDZmDVPuD3cy8W7
`--target foo@latest` (or a semver range / dist-tag) kept the literal spec as
config.version all the way through the run. Every downstream consumer that keys
on an exact version then misbehaved — most damagingly the OSV.dev and GitHub
Advisory DB lookups, which query "latest" as if it were a version, match
nothing, and let a KNOWN CVE be reported as an undocumented 0-day (observed on
deep-defaults@latest → CVE-2021-25944 and deep-set@latest → CVE-2020-28276).

After install, read the concrete version from node_modules/<pkg>/package.json
and use it for discovery, the reported version, the PoC, and advisory lookups.
With this, foo@latest resolves to e.g. 1.0.5 and both advisories match, so the
findings correctly classify as known-cve.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CfAYfHFiDZmDVPuD3cy8W7
Add scripts/discovery/, a three-step pipeline for hunting undocumented
prototype-pollution gadgets in current npm libraries:

  scan-recent.mjs  discover PP-prone packages published since a cutoff (default
                   today, UTC) via the npm search endpoint + a package-name
                   relevance filter, ranked by weekly downloads.
  sweep.sh         fuzz each target in the hardened container, reusing
                   run-sandboxed.sh (cap-drop=ALL, seccomp, non-root, read-only
                   rootfs), advisory classification ON.
  triage.mjs       classify results; surface only reproduced *undocumented*
                   findings as disclosure candidates, exit 2 when any survive.

run-sandboxed.sh now allocates a TTY only when attached to one, so it works in
these batch loops and in CI.

.github/workflows/recent-scan.yml runs the pipeline daily (and on demand),
uploads the sweep artifact, and opens a tracking issue only when a candidate
survives. Informational; never gates other CI.

The tool is the finder, never the disclosure vehicle — see the README's
responsible-disclosure note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CfAYfHFiDZmDVPuD3cy8W7
@acuciureanu
acuciureanu deleted the feat/advisory-cross-check branch August 10, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant