fix(config): pin unscoped per-registry settings to their source's registry at load time (backport #11953 to v10)#11986
Conversation
Review Summary by QodoPin unscoped per-registry credentials to their source registry at load time
WalkthroughsDescription• Pin unscoped per-registry credentials to source registry at load time • Prevents workspace registry override from redirecting user credentials • Emits deprecation warnings for unscoped auth settings • Removes legacy unscoped credential fallback in auth-header Diagramflowchart LR
A["Config sources<br/>CLI, env, project,<br/>workspace, user, global"] -->|"Load each source"| B["rescopeUnscopedCreds<br/>function"]
B -->|"Pin unscoped keys<br/>to source registry"| C["URL-scoped settings<br/>in merged config"]
C -->|"Later registry override<br/>cannot rebind"| D["Credentials stay<br/>on original host"]
B -->|"Emit deprecation<br/>warning"| E["User migration path"]
File Changes1. config/config/src/rescopeUnscopedCreds.ts
|
…istry at load time Each .npmrc / auth.ini / CLI source's unscoped per-registry settings (_authToken, _auth, username/_password, tokenHelper, inline cert/key) are rewritten to their URL-scoped equivalent during load, using the same source's registry= value (or the npmjs default if none). After this rewrite the merged config contains only URL-scoped settings, so a later layer overriding registry= (workspace .npmrc, pnpm-workspace.yaml, CLI --registry) cannot rebind a credential or client certificate to a different host. Each rescope emits a deprecation warning naming the source and the URL the setting was pinned to. ca/cafile are intentionally not rescoped — they're trust anchors, not credentials, and corporate MITM-proxy setups rely on them applying globally. Ported from #11953. Reported by JUNYI LIU. --- Written by an agent (Claude Code, claude-opus-4-7).
Removing the unscoped credential fallback in getAuthHeadersFromConfig is a breaking change to the package's contract — callers passing unscoped _authToken/_auth/username+_password/tokenHelper alongside a default registry no longer get a header back. Matches the major bump in #11953. --- Written by an agent (Claude Code, claude-opus-4-7).
3beac78 to
fb8dd31
Compare
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Backport of #11953 to the
release/10branch.Each
.npmrc,auth.ini, and CLI source's unscoped per-registry settings (_authToken,_auth,username/_password,tokenHelper, inlinecert/key) are rewritten to their URL-scoped equivalent during load, using the same source'sregistry=value (or the npmjs default if none). After this rewrite the merged config contains only URL-scoped settings, so a later layer overridingregistry=(workspace.npmrc,pnpm-workspace.yaml, CLI--registry) cannot rebind a credential or client certificate to a different host.Each rescope emits a deprecation warning naming the source and the URL the setting was pinned to.
ca/cafileare intentionally not rescoped — they're trust anchors, not credentials, and corporate MITM-proxy setups rely on them applying globally.Reported by JUNYI LIU.
Threat closed
A project-local
.npmrc(orpnpm-workspace.yaml, or a CLI flag) that overridesregistry=would cause pnpm to send user-trusted credentials (Authorization: Bearer <user-secret>) or a client TLS cert/key to the new registry, even though the user had written them into~/.npmrcor~/.config/pnpm/auth.inifor a different destination. After this change those user-level settings are pinned at load time to whichever registry the file named (or to the npmjs default if it named none); the workspace'sregistry=override changes where the merged registry resolves but does not move the setting.Differences from #11953
v10's config-loading layout differs from
main, so the port is not a straight cherry-pick:config/readerpackage — the layered loader runs insideconfig/configvia@pnpm/npm-conf. The rescoper is attached to that existing flow instead of being a property of a new package.createGetAuthHeaderByURInever hadmain'sdefaultRegistryparameter, so that subset of the refactor (and the related call-site updates andpublishPackedPkg.tsfallback removal) doesn't apply here.@pnpm/npm-confis built onconfig-chain+proto-list, which chains each source'sdataobject through prototypes (list[0].__proto__ === list[1], …).key in sourceandsource[key]therefore walk into other sources, so the rescoper usesObject.hasOwneverywhere — otherwise it would pin an inherited token to the wrong registry. This concern doesn't exist inmain.network/auth-header/src/getAuthHeadersFromConfig.ts(which rebound unscoped_auth/_authToken/username+_password/tokenHelperto the merged registry at request time) is removed, matchingmain's intent — credentials always reach the merged config URL-scoped after load-time rescoping. The corresponding "legacy way" test inpkg-manager/core/test/install/auth.tsand the unscoped-fallback tests innetwork/auth-header/test/getAuthHeadersFromConfig.test.tsare dropped.Test plan
config/config/test/index.tscover: workspace registry override (token stays on user's registry), no source-level registry (pins to npmjs default), CLI--registry(pins to npmjs default, doesn't pull token along), URL-scoped passthrough (no warning), deprecation warning shape, explicit URL-scoped key wins, inlinecert/keyrescoping,ca/cafileleft alone.network/auth-headertests pass.pnpm run lintclean on changed packages.config/config/test/index.tsfail identically on the baseline without this PR — they leak the maintainer's real~/.config/pnpm/{rc,auth.ini}into URL-scoped-token assertions; not addressed here.Written by an agent (Claude Code, claude-opus-4-7).