Fix npm overrides of git deps being refused (#490) - #491
Open
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Mikola Lysenko (mikolalysenko) wants to merge 6 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
When a dependency declares another package from git, a URL or file:, and the project's package.json "overrides" pins it back to a registry version, npm installs the registry release. socket-patch still treated that copy as installed from git: hosted scan skipped it and exited 0 with the package unpatched, vendored scan failed, and vex refused to attest a correctly patched install. The check that decides which lock entries come from the registry now reads the root package.json overrides (top-level rules, rules nested under the dependent or its ancestors, "." values and $name refs). An override that clearly sends the edge to a registry spec makes the copy patchable again in hosted mode, vendored mode and vex. Anything less clear keeps the old, cautious behavior. Fixes #490 Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
force-pushed
the
agent/fix-npm-origin-overrides
branch
from
October 1, 2026 18:38
871c293 to
fbaea5b
Compare
The hosted capstone suite gains the #490 project shape: a local package depends on left-pad from git and the project's overrides pin it to the registry release. The test checks that scan redirects it, that a fresh npm ci installs the patched bytes, that vex verifies them, and that rollback restores the lock. It skips on npm older than 8.3, which has no overrides. Refs #490 Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 1, 2026 19:03
Collaborator
Author
|
BugBot review Generated by Claude Code |
npm 9.0.0 can't install the #490 project at all: it dies with "Invalid comparator: github:..." when an override covers a git spec. The case doesn't exist on that npm, so the e2e returns instead of failing CI's pinned 9.0.0 leg. npm 9.9 and later run it fully. Refs #490 Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
Two override rules scoped to different ancestors at the same nesting depth tied, and the one later in key order won. npm uses the rule of the closest ancestor, so a farther registry rule could clear an edge that a closer git or file: rule keeps on that source. socket-patch could then patch or attest a copy npm still installs from the spec. Rules are now ranked by how close their ancestor is to the dependent, then by depth. Equally ranked rules that disagree count as unclear, so the copy stays reported as unpatched. Refs #490 Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c657128. Configure here.
Collaborator
Author
|
Burn-down agent: ready for review at
Generated by Claude Code |
This branch has not been deployed
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #490
Summary
A dependency can declare a package from git, a URL or
file:. When the project'soverridespin that package back to a registry version, npm installs the registry release. Since #345, socket-patch still treated that copy as coming from git:scanskipped it withredirect_npm_non_registry_entry_skipped, exited 0 and left the package unpatched;scanfailed withvendor_lock_entry_not_rewritable;vexrefused to attest an install that was correctly patched.All three now treat the overridden copy as the registry install it is. They patch it and attest it.
Root cause
vendor::npm_origin::npm_non_registry_entriesdecides which lock entries come from the registry. The hosted rewriter, the vendored backend and VEX discovery all share it. It flagged an entry whenever some dependent's raw spec for that name wasn't a registry spec, and it never looked atoverrides. npm doesn't recordoverridesin the lock (checked with npm 10: the root""entry has nooverrides, whileresolvedis the registry tarball andpkga's entry keepsgithub:…), so the lock alone can't tell this case apart.Changes
core
vendor/npm_origin.rs: newNpmOverrides, parsed from the rootpackage.json(overridesplus the root dependency specs, so$namereferences resolve).npm_non_registry_entries(lock, &overrides)drops a non-registry edge when an override that clearly applies to it gives a registry spec. That covers:"."values;$namereferences;As in npm, the rule scoped to the closest ancestor wins, then the most deeply nested one. Two equally close rules that disagree count as unclear. Anything else unclear (range selectors, rules scoped to unrelated packages, overrides to another git/
file:source, missing$references) keeps the npm hosted and vendored modes rewire git-sourced lock entries, so npm ci silently installs the unpatched git bytes while scan and VEX report success #326 behavior, so a doubtful copy stays reported as UNPATCHED and is never attested.Hosted: the engine reads
package.jsonas advisory input when an npm candidate meets an npm lock. A symlinked or unreadable in-memory entry is left out, never refused. The in-memory selector fetches it (EXTRA_TEXT_FILES). The rewriter never writes it.Vendored (
vendor/npm_lock.rs):vendor_npmand the download-plan preflight read it once and pass it to the scan, the sibling-lock scan and the rewire.VEX (
vex/discover/npm.rs): lockfile discovery reads it quietly (no diagnostic, no recognition) next to the lock.CHANGELOG.md: added to the existing npm hosted and vendored modes rewire git-sourced lock entries, so npm ci silently installs the unpatched git bytes while scan and VEX report success #326 entry.The npm/pypi/gem wrappers only dispatch to the binary, so they need no change.
Tests
vendor::npm_origin::tests::issue_490_*(5 tests: registry overrides of every supported shape, fresh and already-redirected locks; innermost wins; nested/scoped ancestors)issue_490_overrides_that_do_not_clearly_apply_keep_the_edgeissue_490_the_closest_ancestor_rule_wins_whatever_the_key_orderissue_490_equally_close_rules_that_disagree_keep_the_edgepatch::redirect::tests::issue_490_a_git_edge_overridden_to_the_registry_is_redirectedhosted::engine::tests::issue_490_the_root_manifest_overrides_reach_the_npm_rewritervendor::npm_lock::tests::issue_490_a_git_edge_overridden_to_the_registry_is_vendoredvendor_lock_entry_not_rewritable)vex::discover::npm::tests::issue_490_a_git_edge_overridden_to_the_registry_is_attested(hosted + vendored wiring)e2e_redirect_npm_build::npm_redirect_overridden_git_dependency_installs_patched_bytes"Without fix" was produced by short-circuiting
NpmOverrides::replacementtoNone(and, for the closest-ancestor test, by restoring the old depth-only ranking). The existing #326 tests (npm_non_registry_entries_are_skipped_with_loud_warning,non_registry_only_instances_refuse_and_write_nothing,entries_npm_installs_from_a_non_registry_spec_are_not_attested, the goldenindexed_npm_lock_rewrite_matches_golden) still pass unchanged.Real-npm e2e. The new test reuses the hosted capstone fixture with a real
npm installof the #490 project shape. Each run checks four things:scan --mode hosted --vexreportsredirected: 1, writes the lock pin and emits one in-run VEX statement;npm ciinstalls the patched bytes byte for byte;vexattests them;rollbackrestores the lock.CI's npm compatibility matrix runs it end to end on npm 9.9.4, 10.9.9, 11.20.0, 12.0.0 and 12.1.0. It is N/A, not a skip (
skip()fails the pinned REQUIRED legs), in two cases:overrides;Invalid comparator: github:…when an override covers a git spec.Locally the
rollbackstep can't reach registry.npmjs.org through this sandbox's TLS proxy. The unchanged main capstone hits the same limit, so CI is the proof for that step.CI on
c657128. All 11 workflows pass: CI, npm, pnpm, Bun, vlt, Poetry, PDM, Pipenv, Go, Composer and the GHA audit. Bugbot's latest review of this commit found no new issues, and its one earlier finding is fixed and resolved.Local runs:
cargo clippy --workspace --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast: everything passes except:golang_get_uuid_hosted_day2_machine_builds, which hit a full disk mid-run (link: no space left on device). It passes on re-run.cargo fmt: the files this PR touches are rustfmt-clean apart from lines that were already unformatted onmain. CI doesn't gate on fmt.🤖 Generated with Claude Code
https://claude.ai/code/session_01NLJYcEVmFmVgtyWFgtLjez
Generated by Claude Code