Repository navigation
stasis audit: use @preventive/upstream for advisories and semver; add --repo-advisories - #181
Merged
Merged
Conversation
…sories stasis audit now asks npm's bulk advisories endpoint through @preventive/upstream (pinned to 1.0.0-alpha.0: pre-releases may change the API) instead of its own client, which is removed (src/apis/npm/index.js). Upstream checks every name and version before a request, sends at most 250 names a request, bounds the response size, refuses malformed or unasked-for answers, and matches the covered versions the way `npm audit` does (prereleases and loose semver, and an unreadable range covers every version). audit.js and audit-corrections.js take semver from upstream too; sbom.js still uses src/apis/npm/semver.cjs. The table's `url` column becomes `id`: the GHSA (or npm:<id>), which is what npm's URL pointed to. Against a live lockfile with 86 packages, the 50 rows match main's exactly otherwise. --repo-advisories also asks each package's GitHub repository, found through npm's metadata, for the advisories its maintainers published there before GitHub reviews them into npm's database. The CLI requires a token in GITHUB_TOKEN and passes upstream's GitHub client and the option through unchanged. Also ship src/audit-corrections.js and src/why.js: audit.js imports both, but they were missing from `files`, so a packed @exodus/stasis could not run `stasis audit`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfLZCFXdXdrNeAmHZxEegG
…tream stasis audit's stderr line now counts what it found: `Scanned 86 packages: 51 alerts, 22 high, 22 moderate, 7 low`, each severity present in the table's order (most severe first), a row without one counted as `unrated` so the parts add up. With no hits it reads `...: 0 alerts` (replacing "No advisories found"). sbom.js compares versions with @preventive/upstream's semver too, so src/apis/npm/semver.cjs, the last of src/apis/, is removed. SPDX and CycloneDX output for a live lockfile + bundle are byte-identical to main's apart from timestamps and ids. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfLZCFXdXdrNeAmHZxEegG
--repo-advisories resolves each package's GitHub repository from npm's `latest` document, and without a cache upstream asks for every package on every run. stasis audit now points @preventive/upstream's cache at a per-user directory: $XDG_CACHE_HOME/stasis (when absolute), ~/Library/Caches/stasis on macOS, %LOCALAPPDATA%\stasis\Cache on Windows, else ~/.cache/stasis. Upstream keeps a resolved repo there for a month and answers from it without a request, so it must be a directory only this user writes. A second run over three packages made no npm lookups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LfLZCFXdXdrNeAmHZxEegG
This was referenced Sep 29, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
stasis auditnow gets npm advisories from@preventive/upstreaminstead of stasis's own client, and all of stasis's semver use (audit and sbom) goes through upstream as well. The audit also gains a--repo-advisoriesflag that checks each dependency's GitHub repository, a per-user cache for those repository lookups, and a one-line summary of what it found.Changes
@preventive/upstreamis pinned exactly at1.0.0-alpha.0in@exodus/stasis, because pre-releases may change the API. The lockfile integrity matches the published tarball.stasis/src/audit.js: Now uses upstream'sadvisories(), one{ ecosystem: 'npm', name, versions }per package.flattenAdvisories()takes upstream's rows directly. Upstream already works out which audited versions each advisory covers and drops advisories that cover none, so the local semver matching is gone. Transport and HTTP failures are still wrapped asnpm advisories request failed: …, and the cause is kept. Upstream's own assertions (refused input, malformed answers) pass through unchanged.audit.js,audit-corrections.jsandsbom.jsnow takevalid,satisfiesandcompareVersionsfrom@preventive/upstream/semver.js, which borrows the same npm semver that sits beside node.stasis/src/apis/, stasis's own npm client andsemver.cjs, since nothing uses it any more.urlcolumn is replaced byid, the GHSA ornpm:<id>. npm's URL for these rows was justhttps://github.com/advisories/<GHSA>.Scanned 86 packages: 51 alerts, 22 high, 22 moderate, 7 low. It lists each severity present, most severe first, and counts a row with no severity asunratedso the numbers add up. With no hits it readsScanned N packages: 0 alerts, replacingNo advisories found.--repo-advisories: Also asks each package's GitHub repository, found through npm's metadata, for the advisories its maintainers published there. npm's database only has those once GitHub has reviewed them. The CLI requires a token inGITHUB_TOKEN, and passes upstream's GitHub client and the option through toadvisories()unchanged. Usage text and README are updated. No new tests cover it, since the behavior belongs to upstream.setCacheDir()at a per-user directory, so each package's npm-to-GitHub repository lookup is cached for a month instead of repeated on every run. The directory is$XDG_CACHE_HOME/stasiswhen that path is absolute,~/Library/Caches/stasison macOS,%LOCALAPPDATA%\stasis\Cacheon Windows, and~/.cache/stasisotherwise. Upstream answers from the cache without making a request, so it has to be a directory only that user writes. It's set in the CLI, not inaudit(), because the setting is process-global.src/audit-corrections.jsandsrc/why.jswere missing fromstasis/package.json'sfiles, althoughaudit.jsimports both. That means a packed@exodus/stasiscouldn't runstasis audit;npm pack --dry-runnow includes them.Behavior notes
Upstream checks every name and version before sending any request. It sends at most 250 names per request, limits response size and time, and refuses answers that are malformed or about packages that weren't asked for. It matches versions the way
npm auditdoes, including prereleases and loose semver, and a range semver can't parse covers every version.Testing
tests/audit.test.jsis updated for the new row shape and theidcolumn. Version-matching coverage moved fromflattenAdvisoriesinto anaudit()test against a stubbed registry. New tests cover the summary line: severity breakdown,unrated,0 alerts, and the singular forms.CI=true GITHUB_ACTIONS=trueon Node 24.14.0, 26.0.0 and 26.10.0: 2030 tests, 2027 pass, 3 skipped.node --run lintis clean.tests/fixtures/popular-npm-modules(86 packages): this branch andmainreport the same 51 advisories in the same order, with identical severity, package, installed, vulnerable and title columns. Each newidequals the GHSA at the end ofmain's URL.--whyoutput is also unchanged.stasis sbomSPDX and CycloneDX output for that lockfile plus its bundle is byte-identical tomain's, apart from timestamps and ids.--repo-advisoriessmoke test: withoutGITHUB_TOKENit's a usage error. With a bad token it reachesGET https://api.github.com/repos/lodash/lodash/security-advisoriesand fails with GitHub's 401.XDG_CACHE_HOME: the first run over 3 packages made 3 npm lookups and wrotenpm/repos/{lodash,axios,@babel+core}.json; the second run made none.🤖 Generated with Claude Code
https://claude.ai/code/session_01LfLZCFXdXdrNeAmHZxEegG