Skip to content

feat: resolve exact installed versions from the lockfile#3

Merged
msoffredi merged 8 commits into
mainfrom
lockfile-version-resolution
Jul 4, 2026
Merged

feat: resolve exact installed versions from the lockfile#3
msoffredi merged 8 commits into
mainfrom
lockfile-version-resolution

Conversation

@msoffredi

Copy link
Copy Markdown
Contributor

Summary

This action previously only discovered package names from package.json, never the actual installed version — score.ts called GET /packages/:name with no version parameter, so packagerating.com scored whatever it considered "latest" for each dependency. Two versions of the same package can score very differently (a CVE fixed in a patch release, a maintenance cliff at a specific major version), so an audit that ignores the installed version can both miss real risk and flag risk that doesn't apply to what's actually running.

This PR resolves each discovered package's exact installed version from the project's lockfile and threads it into the packagerating API, which already supports version-specific scoring end-to-end (GET /packages/:name?version=X — shipped separately, earlier today, in the package-rating API repo).

Supported: npm (package-lock.json, lockfileVersion 1 and 2/3), yarn Classic (yarn.lock v1), pnpm (pnpm-lock.yaml, both the importers['.'] and legacy root-level structures). Auto-detected next to package.json, in that priority order.

Not supported (falls back to unversioned, never a hard failure): Yarn Berry (v2+) and PnP mode, npm/pnpm workspaces, transitive dependency versions beyond what's explicitly named via the packages input.

New use-lockfile input (default true) fully reverts to today's behavior when set to false.

A necessary refactor along the way

score.ts's old crawl-trigger path called POST /packages/crawl — a batch endpoint that has no version parameter and never will. That call is now removed entirely: the single-package GET /packages/:name?version=X endpoint already auto-triggers a crawl on a miss and returns a job_id in its 202 response, so fetchScoreOnce and crawlAndWait collapsed into one fetchOrCrawl that triggers, polls, and re-fetches without any redundant round trip.

Implementation notes

  • Built via subagent-driven development: 7 tasks, each with independent implementer + reviewer passes, plus a final whole-branch review.
  • The score.ts refactor (Task 6) was the highest-risk task — reviewer confirmed zero POST /packages/crawl calls survive, job_id is reused from the initial response rather than re-fetched, and ?version= is correctly omitted (not the literal string "null") when unresolved. One Important finding (duplicated response-parsing logic between two functions) was fixed and independently re-verified via a traced before/after comparison.
  • The final whole-branch review traced the full discoverPackages → index.ts → scorePackages → ?version= data-flow chain in the merged code (not just per-task diffs) and confirmed no field/argument drift across the three files it spans.

Test plan

  • Full suite: 72/72 passing
  • Typecheck clean (zero errors project-wide)
  • npm run build succeeds
  • Manual smoke test against a real npm/yarn/pnpm project — recommend running this action against a project with each lockfile type before the next release tag

🤖 Generated with Claude Code

msoffredi and others added 8 commits July 4, 2026 10:43
Implement NamedRange type and resolveNpmVersions function to parse npm
lockfiles (v1 nested dependencies and v2/v3 flat packages map formats),
with proper fallback behavior when packages map lacks an entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removes ~13 lines of near-duplicate logic that converted an
ApiPackageResponse into a scored PackageScore or 'not-found', which had
been copy-pasted between fetchScore and the tail of fetchOrCrawl.
Pure refactor; no observable behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@msoffredi
msoffredi merged commit ba7961c into main Jul 4, 2026
1 check passed
@msoffredi
msoffredi deleted the lockfile-version-resolution branch July 4, 2026 16:24
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