[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.
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
#1008 fixed #812. Hosted npm scans now warn
redirect_npm_replace_registry_hostwhen npm'sreplace-registry-hostsetting would rewrite the hosted pin's origin to the registry. But socket-patch reads the setting's value raw from the project and user.npmrcfiles. npm env-replaces every config value (@npmcli/configparseField→envReplace). Soreplace-registry-host=${RRH}withRRH=alwaysbehaves exactly likereplace-registry-host=alwaysfor npm: everynpm cifails 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). Thereplace-registry-hostvalue 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-onlyreplace-registry-host=${NPM_REPLACE_HOST}). The hosted scan exits 0 and says the project is patched. Every fresh install then fails E404, fetchinghttps://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.2at127.0.0.1:18080; every socket-patch command gets--api-url <mock> --org o --api-token fake --patch-server-url <mock>)Results on main
f3c6313, npm 10.9.4. I ran each${RRH}row twice and each control row once:replace-registry-hostis setredirect_npm_replace_registry_hostnpm ci.npmrc=always.npmrc=127.0.0.1(the hosted host)npm_config_replace_registry_host=always.npmrc(NPM_CONFIG_USERCONFIG)=always.npmrc=${RRH},RRH=always.npmrc=${RRH},RRH=always.npmrc=ALWAYS(control)The human output of the
${RRH}case says "1 package is already on hosted patches; nothing to rewrite." with no warning.Expected vs actual
replace-registry-hostsetting would rewrite the hosted pin's host, the hosted scan warnsredirect_npm_replace_registry_host. "Effective" has to mean the value npm uses, after${VAR}/${VAR?}expansion, as the module already does for path-typed settings.Related, not a bug: the vendored
allow-filecheck (#969) treats a raw${AF}as "notall", so it fails safe. It warns andvendor --checkfails even ifAF=all. A shared env-replaced value reader would fix both.${RRH}in the project and user layers, ×2 each)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) andouter_file_valuetakenpmrc_top_level_value(...)(:175) verbatim. Noenv_replaceis applied, unlikeNpmConfigEnv::config_path(:430).replace_registry_host_rewrites(:666) then compares the literal${RRH}against the host.