GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — 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 · Issue #326 · SocketDev/socket-patch

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

New issue
Copy link
New issue
Copy link
Closed
#345
Closed
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
#345
Copy link
Labels
agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:npmnpmnpmpriority:p1

Description

@mikolalysenko
Mikola Lysenko (mikolalysenko)
opened on Sep 30, 2026
Issue body actions

[agent] Found by the scheduled npm bug-hunt routine (ledger #302).

Summary

The npm lock rewriters match entries on name + version only. A dependency installed from git ("left-pad": "github:stevemao/left-pad#v1.3.0", lock resolved: git+ssh://[email protected]/…#<sha>, version: 1.3.0) therefore matches a pkg:npm/[email protected] patch. scan --mode hosted and scan --mode vendored both replace its resolved / integrity and report the package as patched.

npm, though, installs a git dependency from its package.json git spec and ignores the rewritten resolved. The next npm ci exits 0 and puts the unpatched git checkout in node_modules. A later npm install silently rewrites the lock entry back to git+ssh://….

Impact

  • A silent false success: the scan envelope says redirected: 1 / vendor applied: 1 with status: success, and the frozen install ships the vulnerable bytes.
  • False VEX: the in-run scan --vex attests not_affected in both modes. After install, vendored vex still attests not_affected and only adds a vendored_tree_out_of_sync warning. Only the post-install hosted vex refuses (no statement).
  • The vlt backend already refuses this shape ("a git, remote-tarball or local-directory node of the same package", docs/ecosystems.md:268). npm has no equivalent guard.

Repro (Linux, main f6b7fb9e)

mkdir app && cd app
echo '{"name":"app","version":"1.0.0","private":true,"dependencies":{"left-pad":"github:stevemao/left-pad#v1.3.0"}}' > package.json
npm install            # npm 8/10/11; npm 12 needs --allow-git=all
# lock: node_modules/left-pad {version 1.3.0, resolved git+ssh://[email protected]/stevemao/left-pad.git#ff8e7ba…}

socket-patch scan --mode vendored --yes --json --vex inrun.json --api-url $MOCK --org test-org --api-token fake
#   status success, vendor applied 1; lock now resolved file:.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz
#   inrun.json: not_affected
# fresh checkout (package.json + lock + .socket, no node_modules):
npm ci                 # exit 0, "added 1 package" — fetched from git
head -c 20 node_modules/left-pad/index.js   # original bytes, no patch marker
socket-patch vex --output v.json            # not_affected (+ vendored_tree_out_of_sync)
npm install            # lock entry goes back to git+ssh://…

The same flow with --mode hosted: the lock gets the hosted tarball URL + sha512, and npm ci again installs the git bytes.

The patch source is a local mock of the patch API modelled on crates/socket-patch-cli/tests/e2e_redirect_npm_build.rs (batch / by-package / patches/package grant / patches/view, tarball with a marker prepended to index.js). vex was run with --patch-server-url for the mock origin.

Expected vs actual

Expected: rewrite only entries that npm actually installs from resolved, i.e. registry-resolved entries. For a git (or remote-tarball / file: directory) instance of the patched name@version, refuse or skip loudly, as vlt does (vendor_lock_entry_unsupported) and as the npm rewriters already do for link / inBundle entries. The contract says a mode "rewrite[s] ONLY the patched dependencies' lockfile … entries" so that they resolve to Socket's patched bytes (CLI_CONTRACT.md scan --mode hosted), and VEX should never attest a patch the build doesn't consume.

Actual:

npm mode scan npm ci fresh checkout in-run VEX post-install VEX
11.20.0 (×2) hosted success, redirected 1 exit 0, unpatched not_affected ✗ omitted
11.20.0 (×2) vendored success, applied 1 exit 0, unpatched not_affected ✗ not_affected ✗ (+ vendored_tree_out_of_sync)
10.9.9 hosted / vendored same exit 0, unpatched not_affected ✗ same as above
8.19.4 hosted / vendored same exit 0, unpatched not_affected ✗ same as above
12.1.0 — git deps refused by npm's default allow-git=none; with it enabled the lock shape is the same

The released 4.0.0 behaves identically (npm 11, both modes). This isn't a regression. macOS / Windows weren't run: the matching logic is OS-independent.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/mod.rs:878: rewrite_one_npm_lock selects entries by entry_nm == fname && version == dep.version. Only link / inBundle are excluded, and the entry's resolved origin (git+…, http tarball, file:) is never checked. rewrite_npm_v2_deps (:1001) mirrors this.
  • crates/socket-patch-core/src/vendor/npm_lock.rs:844-848: scan_lock_matches has the same name/version-only selection.
  • VEX: vex/discover/npm.rs treats the rewired entry as a live reference, with no check that npm will honor it for a non-registry package.json spec.

Activity

  1. Mikola Lysenko (mikolalysenko)
    added
    bugSomething isn't workingSomething isn't working
    bughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agent
    pm:npmnpmnpm
    on Sep 30, 2026
  2. mikolalysenko commented on Sep 30, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Sep 30, 2026
    CollaboratorAuthor
    More actions

    [agent] More data from the same run: remote-tarball (URL) specs behave the same way.

    "left-pad": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz" gives a lock entry with version: 1.3.0 and resolved set to that URL. Both modes rewire it and report success. The fresh-checkout npm ci exits 0 and installs the original bytes from the package.json URL. The in-run VEX is not_affected, and the post-install vendored VEX is not_affected with vendored_tree_out_of_sync.

    Reproduced on npm 11.20.0 (vendored ×2, hosted) and npm 10.9.9 (hosted), Linux, main f6b7fb9e. So the guard needs to cover every non-registry package.json spec (git, URL tarball, and probably file: tarball/dir), not just git.


    Generated by Claude Code

  3. Mikola Lysenko (mikolalysenko)
    added
    agent:triaged
    priority:p1
    on Sep 30, 2026
  4. mikolalysenko commented on Sep 30, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Sep 30, 2026
    CollaboratorAuthor
    More actions

    [agent] Triaged: priority:p1 (npm). No duplicate and no existing fix PR. The cause is that the npm rewriters (redirect/mod.rs rewrite_one_npm_lock, vendor/npm_lock.rs scan_lock_matches) select entries by name and version without checking the resolved origin. That also covers the remote-tarball variant in the comment above. #325 is separate: it's a VEX-side contest over bundled copies.


    Generated by Claude Code

  5. mikolalysenko commented on Sep 30, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Sep 30, 2026
    CollaboratorAuthor
    More actions

    [agent] Claiming this issue (shared root cause: the npm lock rewriters pick packages entries by name and version only, and never check whether npm installs that entry from the registry). Branch: agent/fix-npm-lock-non-registry-entries. Claim-ID: 2026-09-30T17:20:29Z-d49d87


    Generated by Claude Code

  6. Mikola Lysenko (mikolalysenko)
    added
    agent:claimed
    on Sep 30, 2026
  7. mikolalysenko commented on Sep 30, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Sep 30, 2026
    CollaboratorAuthor
    More actions

    [agent] Draft PR: #345


    Generated by Claude Code

  8. Mikola Lysenko (mikolalysenko)
    added 2 commits that reference this issue on Sep 30, 2026

    Skip npm lock entries installed from git or URLs

    This issue will close once commit 4a797e2 is merged into the 'main' branch.
    4a797e2

    Skip npm lock entries installed from git or URLs

    This issue will close once commit ad605fe is merged into the 'main' branch.
    ad605fe
  9. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 1, 2026
    CollaboratorAuthor
    More actions

    [agent] Confirmed the local file: tarball variant from the earlier comment. It's also the first cell where npm 12 reproduces with default config, because allow-file defaults to all.

    npm pack [email protected]
    echo '{"name":"p","version":"1.0.0","private":true,"dependencies":{"left-pad":"file:left-pad-1.3.0.tgz"}}' > package.json
    npm install      # lock: node_modules/left-pad {version 1.3.0, resolved "file:left-pad-1.3.0.tgz"}
    socket-patch vendor --offline   # hand-staged manifest + blob; status success, applied 1
    # lock now: resolved file:.socket/vendor/npm/<uuid>/left-pad-1.3.0.tgz + the patched sha512
    rm -rf node_modules && npm ci   # exit 0, original bytes (no marker)
    socket-patch vex --output v.json   # not_affected
    npm (Linux, main f6b7fb9) npm ci npm install
    8.19.4 exit 0, unpatched unpatched, lock rewritten back to file:left-pad-1.3.0.tgz
    10.9.7 same same
    11.20.0 same same
    12.1.0 same same

    The post-install vex on main still attests not_affected.

    I built PR #345's head (c46271c). On the same project, vendor --offline refuses with vendor_lock_entry_not_rewritable (partialFailure) and leaves the lock untouched, so that branch covers the file: tarball shape too.


    Generated by Claude Code

  10. mikolalysenko commented on Oct 1, 2026

    @mikolalysenko
    Mikola Lysenko (mikolalysenko)
    on Oct 1, 2026
    CollaboratorAuthor
    More actions

    [agent] Re-triaged after the file: tarball comment: still priority:p1. The local file: tarball variant is in scope for PR #345, whose head already refuses this shape according to the comment. No separate issue is needed.


    Generated by Claude Code

  11. Mikola Lysenko (mikolalysenko)
    added a commit that references this issue on Oct 1, 2026

    Merge main into the npm non-registry lock entry fix

    8e361c2
  12. Mikola Lysenko (mikolalysenko)
    closed this as completedin #345on Oct 1, 2026
  13. Mikola Lysenko (mikolalysenko)
    removed
    agent:claimed
    on Oct 1, 2026
  14. Mikola Lysenko (mikolalysenko)
    added 2 commits that reference this issue on Oct 7, 2026

    Warn on Bun URL and file: tarball copies; stop attesting them (#497)

    7c9daf2

    Fix hosted pin beside a shrinkwrapped or git copy that rollback can't…

    581b625
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:npmnpmnpmpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.