Skip to content

Warn when deno.lock keeps installing an npm package (#406) - #1711

Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-npm-deno-lock-warning
Open

Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-npm-deno-lock-warning

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 24, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #406

Summary

In a Deno project that also commits an npm-family lock (package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml, yarn.lock, bun.lock, vlt-lock.json), deno install installs the npm dependencies from deno.lock and never reads the lock socket-patch rewires. Hosted and vendored runs used to report a clean success with no warning, so Deno users thought the package was patched while Deno kept installing the registry copy.

Now:

  • scan --mode hosted / get --mode hosted warn redirect_npm_deno_lock once, naming every redirected npm name@version that the root deno.lock also locks. This covers this run's pins and earlier runs' pins, and repeats on the idempotent re-run. deno.lock is never written.
  • vendor / scan --mode vendored warn vendor_npm_deno_lock for every npm lockfile flavor, including the in-sync re-run.
  • Both warnings share one detail text, which points to the remedy that actually works (deno install, then socket-patch scan --mode agent).
  • The in-memory hosted engine (hosted bundle, Node addon) now fetches deno.lock as an advisory file, so both the warning and the VEX omission work there too.

Root cause

The VEX half of #406 was already on main: vex/discover/deno.rs reads deno.lock's npm section and vex omits such pins (vex_deno_lock_copy). The writers never looked at deno.lock, though. The hosted engine doesn't even pass it to the rewriters, and the npm vendored router didn't check it. So the run's success summary contradicted what vex would say.

Changes

  • crawlers/deno_crawler.rs: lock_npm_packages / lock_text_npm_packages is now the one reader of deno.lock's npm keys (lockfile v2–v5, Deno peer suffix dropped). It is shared by VEX discovery and both warnings, so they agree on which pins count, including a lock with no version key.
  • hosted/engine.rs: npm_deno_lock checks the confirmed npm pins against deno.lock and adds the warning to npm_warnings.
  • vendor/npm_flavor.rs: vendor_npm_any warns after any flavor's successful vendor.
  • patch/redirect/mod.rs: shared npm_deno_lock_detail and the DENO_LOCK constant.
  • hosted/memory/select.rs: deno.lock is added to the advisory text files.
  • CLI_CONTRACT.md (two new warning rows plus the deno row) and docs/ecosystems.md.

Test evidence

Red → green: with the two warning call sites disabled, both new core tests fail (issue_406_a_deno_lock_of_the_redirected_version_is_said panics at engine.rs:4206, a_deno_lock_of_the_vendored_version_is_said at npm_flavor.rs:1893). With the fix they pass.

Issue part Test
#406 hosted: warn when deno.lock locks the redirected version (memory + disk views; peer suffix; other version, unparseable lock or no lock stay silent; deno.lock never written) hosted::engine::tests::issue_406_a_deno_lock_of_the_redirected_version_is_said
#406 hosted, end to end via the CLI: warning on the first run and the re-run, deno.lock untouched, in-run --vex omits (vex_deno_lock_copy, no_applicable_patches); another version attests scan suite hosted_failure_paths::deno_lock_of_the_redirected_version_warns_and_vex_omits_it
#406 vendored (comment 1): warn on the first run and the in-sync re-run, lock still wired vendor::npm_flavor::tests::a_deno_lock_of_the_vendored_version_is_said
deno.lock v2/v3/v4/v5 keys, scoped names, peer suffix, BOM, no-version lock vex::discover::deno::tests::deno_npm_keys_cover_every_lock_version
In-memory engine fetches deno.lock hosted::memory::select::tests::selects_root_candidates_only

Local runs (container is root):

  • cargo fmt --all -- --check and cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features --lib: 6727 passed, 4 failed. The 4 are permission tests that chmod a directory 0555 and expect a write to fail, which can't happen as root (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched, pypi_requirements::wire_failure_rolls_back_already_written_files). All are in files this PR doesn't touch, and CI runs them as non-root.
  • cargo test -p socket-patch-cli --all-features --test scan (246 ok), --test hosted_memory_engine (48 ok), --test in_process_vendor (144 ok).
  • The full cargo test --workspace --all-features ran out of the container's disk while linking every test binary, so the PR CI run is the full-suite record.

Review notes (/code-review high)

Fixed: the in-memory selector didn't fetch deno.lock; the warning reader required version where discovery didn't; the wording was wrong for vlt and shrinkwrap-only projects; npm_deno_lock was simplified. Not changed, with reasons:

  • Derive the hosted warning from the final discovery's DenoLock marks. The warning and discovery already share one reader (lock_npm_packages), so they can't drift on parsing. Wiring the warning through the attribution-gate discovery would couple npm_warnings to an optional, origin-dependent value (final_discovery is None in several paths).
  • deno.lock is re-read on each rewrite pass / each vendored package. This is an advisory read of one small JSON file. It is skipped when no npm pin is confirmed (hosted) or the vendor failed, and matches how allow_file_refusal / allow_scripts_lost re-read .npmrc / package.json per package. Not worth the extra plumbing.

No wrapper changes are needed: the npm/pypi/gem wrappers only dispatch to the binary.

https://claude.ai/code/session_01RLbCDwFEoz96G2kBAq53Fk


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
In a Deno project that also keeps an npm-family lock, `deno install`
installs the npm dependencies from deno.lock and never reads the lock
socket-patch rewires. Hosted and vendored runs reported a clean success
anyway, so Deno users believed the dependency was patched while Deno
kept installing the registry copy.

Hosted scans now warn `redirect_npm_deno_lock` and vendored runs
`vendor_npm_deno_lock` when deno.lock locks the same name@version,
naming the agent-mode remedy. The deno.lock npm-section reader is
shared with VEX discovery, which already omits such pins.

Assisted-by: Claude Code:claude-opus-5-5
Assisted-by: Claude Code:claude-opus-5-5
The in-memory engine (hosted bundle, Node addon) never fetched
deno.lock, so it could not warn about it. Fetch it as an advisory file.
Read a deno.lock without a `version` key the same way VEX discovery
does, so the warning names exactly the pins `vex` leaves out. Reword
the warning so it is also accurate for vlt and shrinkwrap-only
projects.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 24, 2026 08:55
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 c300a1f. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 24, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Labeled Ready for review by the burn-down agent.

  • Head: c300a1f1b889cb11abe151a6daf7ec445295e1e6
  • CI: 88/88 checks green or skipped on this head, no main-wide failures.
  • Bugbot: reviewed c300a1f with no findings; no open review threads.
  • Mergeable, no CHANGELOG.md change.
  • Slack announcement: not sent (the Slack send tool was unavailable in this run).

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief (for the human approver)

What it does. In a project that has both deno.lock and an npm-family lock, deno install reads only deno.lock, so a hosted or vendored npm patch never reaches what Deno installs. Runs used to report a clean success there. Now hosted runs warn redirect_npm_deno_lock and vendored runs warn vendor_npm_deno_lock when deno.lock locks the same name@version. Nothing is rewired differently, and deno.lock is never written.

Risk: low. The change is additive and only produces warnings. The new code is an advisory read: an unreadable or non-JSON deno.lock yields no warning, and a missing one is skipped. The one refactor moves the VEX deno.lock key parser into a shared helper, unchanged except that keys with an empty version are now skipped (it is covered by the existing v2–v5 test).

Look here

  • crawlers/deno_crawler.rs:228-252:`` the shared deno.lock npm-key parser, now used by VEX and both warnings.
  • hosted/engine.rs:2338 and :2982: the hosted warning, limited to confirmed npm pins. Only the final rewrite pass's warnings are kept, so it can't duplicate.
  • vendor/npm_flavor.rs:524 and :541: the vendored warning after any successful flavor, including the in-sync re-run.
  • hosted/memory/select.rs:63: the in-memory engine now fetches deno.lock too.

Verified

  • I read the full diff. Tests cover the same version, a peer suffix, another version, non-JSON and a missing lock, in memory and on disk, plus a CLI end-to-end run (with re-run and the --vex omission).
  • CHANGELOG.md is untouched, and the branch is up to date with main.
  • I ran the new and changed core tests locally: cargo test -p socket-patch-core --lib -- deno_lock deno_npm_keys selects_root_candidates_only, 7/7 passed.
  • CI: 38/38 checks that ran are green (50 skipped by path filters). Bugbot reviewed this head and found no issues. There are no open review threads.

Changes I made. None.

Open questions. None.

Auto-merge is armed, so approving sends it straight to the merge queue.


Generated by Claude Code

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

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hosted npm pin in package-lock.json is attested by VEX in a Deno project, but deno.lock keeps installing the unpatched registry copy

2 participants