Skip to content

The #812 replace-registry-host warning misses a ${VAR} value in .npmrc: npm expands it to always, so every npm ci fails E404, while the hosted scan reports success with no warning #1233

Description

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

Summary

#1008 fixed #812. Hosted npm scans now warn redirect_npm_replace_registry_host when npm's replace-registry-host setting would rewrite the hosted pin's origin to the registry. But socket-patch reads the setting's value raw from the project and user .npmrc files. npm env-replaces every config value (@npmcli/config parseField → envReplace). So replace-registry-host=${RRH} with RRH=always behaves exactly like replace-registry-host=always for npm: every npm ci fails E404. socket-patch sees the literal ${RRH}, which matches no host, so it gives no warning and the scan reports success. That is the #812 failure again, for this spelling.

The path-typed lookups in the same module already env-replace (NpmConfigEnv::config_path → env_replace). The replace-registry-host value path doesn't.

Impact

Same as #812, for teams that template npm config through environment variables (the usual ${NPM_TOKEN} pattern, applied to this key, for example a CI-only replace-registry-host=${NPM_REPLACE_HOST}). The hosted scan exits 0 and says the project is patched. Every fresh install then fails E404, fetching https://registry.npmjs.org/patch/npm/.... Low frequency, but the failure is silent.

Repro (Linux; a local mock of the org patch API serves one free patch for ms@2.1.2 at 127.0.0.1:18080; every socket-patch command gets --api-url <mock> --org o --api-token fake --patch-server-url <mock>)

mkdir p && cd p
echo '{"name":"p","version":"1.0.0","dependencies":{"ms":"2.1.2"}}' > package.json && npm i
printf 'replace-registry-host=${RRH}\n' > .npmrc
export RRH=always
npm config get replace-registry-host          # always
socket-patch scan --yes --json | grep -c redirect_npm_replace_registry_host   # 0
rm -rf node_modules && npm ci --cache "$(mktemp -d)"
# npm error code E404
# npm error 404 Not Found - GET https://registry.npmjs.org/patch/npm/ms/2.1.2/tok/***/ms-2.1.2.tgz

Results on main f3c6313, npm 10.9.4. I ran each ${RRH} row twice and each control row once:

Where replace-registry-host is set npm reads redirect_npm_replace_registry_host npm ci
project .npmrc =always always yes E404 (warned)
project .npmrc =127.0.0.1 (the hosted host) 127.0.0.1 yes E404 (warned)
env npm_config_replace_registry_host=always always yes E404 (warned)
user .npmrc (NPM_CONFIG_USERCONFIG) =always always yes E404 (warned)
project .npmrc =${RRH}, RRH=always always no E404, silent
user .npmrc =${RRH}, RRH=always always no E404, silent
project .npmrc =ALWAYS (control) ALWAYS no installs (correct)

The human output of the ${RRH} case says "1 package is already on hosted patches; nothing to rewrite." with no warning.

Expected vs actual

Related, not a bug: the vendored allow-file check (#969) treats a raw ${AF} as "not all", so it fails safe. It warns and vendor --check fails even if AF=all. A shared env-replaced value reader would fix both.

OS npm Reproduces
Linux 10.9.4 / Node 22 yes (${RRH} in the project and user layers, ×2 each)
macOS / Windows — untested; the parsing is OS-independent

First bad: none. Before #1008 (16106b1) there was no warning for any spelling (#812). This is a gap in that fix.

Suspect code

  • crates/socket-patch-core/src/patch/redirect/npmrc.rs:636 (effective_replace_registry_host) and outer_file_value take npmrc_top_level_value(...) (:175) verbatim. No env_replace is applied, unlike NpmConfigEnv::config_path (:430).
  • replace_registry_host_rewrites (:666) then compares the literal ${RRH} against the host.

Activity

  1. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (npm). Not a duplicate: a residual of #812 for the ${VAR} spelling. No open PR covers it.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 9, 2026

    @mikolalysenko
    CollaboratorAuthor

    v5 triage: P2, not a release blocker. Retain npm replace-registry-host through environment interpolation at P2; the literal configuration conflict is already diagnosed.

    This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.

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