Skip to content

Global mode can't see any Deno global install: the npm packages under $DENO_DIR/npm/registry.npmjs.org are never crawled, so scan -g misses them and Deno 2.9's per-tool node_modules gets patched and VEX-attested while the tool runs the unpatched copy #444

Description

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

Summary

deno install -g (every Deno version tested, 1.46.3 to 2.9.6) resolves a global tool's npm dependencies from Deno's shared npm cache, $DENO_DIR/npm/registry.npmjs.org/<name>/<version>/. socket-patch's global mode never looks there:

False "patched" on Deno 2.9. Deno 2.9 also writes a per-tool <install-root>/bin/.<tool>/node_modules/.deno/... tree. For a tool installed from a local or JSR entrypoint, that tree is a decoy: the tool's deno.json has no nodeModulesDir, and Deno still loads the $DENO_DIR copy (DENO_LOG=debug: Resolved package folder of is-odd@3.0.1 to …/dd/npm/registry.npmjs.org/is-odd/3.0.1). The decoy is the one tree --global-prefix can crawl. Pointing --global-prefix at it reports applied, and vex attests verified / not_affected, while the global tool keeps executing the unpatched bytes. (For an npm: entrypoint tool, Deno 2.9 writes "nodeModulesDir": "manual" and does load the per-tool tree; transitive deps there hit #373.)

Impact

  • A user or CI job running socket-patch scan -g to cover their globally installed Deno tools gets a clean success report with zero findings, so vulnerable global tools stay unpatched with no signal.
  • On Deno 2.9, the only workaround that looks like it works gives a false VEX not_affected attestation for code that isn't patched.

Repro (Linux, Deno 2.9.6; same shape on every version)

export DENO_DIR=$PWD/dd DENO_INSTALL_ROOT=$PWD/root
mkdir tool && printf 'import isOdd from "npm:is-odd@3.0.1";\nconsole.log("isOdd(3)=" + isOdd(3));\n' > tool/t.ts
deno install -g -A -n sptool tool/t.ts
find . -path '*is-odd*' -name index.js
#   ./dd/npm/registry.npmjs.org/is-odd/3.0.1/index.js                      <- what Deno runs
#   ./root/bin/.sptool/node_modules/.deno/is-odd@3.0.1/node_modules/is-odd/index.js   (2.9 only)

# Manifest + blobs for pkg:npm/is-odd@3.0.1; after-bytes = before + "console.log('SP-PATCHED')"
mkdir cwd && cd cwd && git init -q && python3 mkman.py . ../dd/npm/registry.npmjs.org/is-odd/3.0.1/index.js
socket-patch scan -g --json                                   # success, is-odd never queried (verified with a stub of the batch API)
socket-patch apply -g --offline --json                        # partialFailure: skipped package_not_installed
for P in ../dd ../dd/npm ../dd/npm/registry.npmjs.org; do
  socket-patch apply --global-prefix $P --offline --json      # partialFailure: package_not_installed (all three)
done
socket-patch apply --global-prefix ../root/bin/.sptool/node_modules --offline --json   # success: applied
../root/bin/sptool                                            # isOdd(3)=true  -- no SP-PATCHED, the unpatched DENO_DIR copy ran
socket-patch vex --global-prefix ../root/bin/.sptool/node_modules --offline --product pkg:generic/t --output v.json
#   statements: GHSA-… not_affected (inline_mitigations_already_exist)

mkman.py is the 15-line helper in the probe workflow linked below (git-sha256 blobs plus .socket/manifest.json). Reproduced twice locally on Linux (1.46.3, 2.2.15 and 2.9.6), and in the 15-cell probe.

Expected vs actual

  • Expected: the maintainers' global-mode contract. scan -g finds every globally installed package that has a patch, and apply -g / rollback -g / vex -g act on the copy that actually runs. CLI_CONTRACT.md documents --global as "Operate on globally-installed packages", and docs/ecosystems.md lists Deno agent mode as "✅ in place". The deno crawler's own module doc says scan --global --ecosystems deno --global-prefix <path> is how users target Deno globals. At minimum, socket-patch should not report applied / VEX not_affected for a copy the runtime doesn't load.
  • Actual: Deno globals are invisible to -g and to every --global-prefix, and scan -g exits 0 / success. On Deno 2.9, patching the per-tool tree gives a false applied and a false VEX attestation.

OS × version (probe run https://github.com/SocketDev/socket-patch/actions/runs/36833777679)

OS Deno Deno loads $DENO_DIR/npm/registry.npmjs.org copy apply -g --global-prefix $DENO_DIR / npm / registry.npmjs.org per-tool node_modules decoy: apply → tool runs patched? / vex
ubuntu / macos / windows 1.46.3 yes (only copy on disk) not found not found ×3 n/a (no per-tool dir)
ubuntu / macos / windows 2.0.6 yes not found not found ×3 n/a
ubuntu / macos / windows 2.2.15 yes not found not found ×3 n/a
ubuntu / macos / windows 2.4.5 yes not found not found ×3 n/a
ubuntu / macos / windows 2.9.6 yes not found not found ×3 applied, tool UNPATCHED, VEX not_affected

All 15 cells reproduce. (On Windows scan -g -e npm also scans 0 packages; that's #434.)

First bad version

Not a regression. Releases 4.0.0 and 3.3.0 behave identically (apply -g → package_not_installed), and no version of the npm or Deno crawler has known the $DENO_DIR/npm/<registry>/<name>/<version> layout.

Suspect code

Related, but distinct: #373 (node_modules/.deno transitive deps in projects), #374 (JSR cache path), #436 (get -g --mode hosted|vendored doesn't refuse).

Activity

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions