Skip to content

A non-base64 _password is handled three different ways by pnpm 11, pnpm 12, and npm #16273

Description

@dibas-np

Which area(s) of pnpm are affected?

Configuration, Dependencies resolver

pnpm version

  • pnpm 12.7.0 and a build of main: accepts the value, sends it as the raw password
  • pnpm 11.28.0: refuses to run

Found while fixing the credential selection in #10237. The two CLIs disagree on a _password that is not valid base64, and npm agrees with neither.

Reproduction steps

mkdir repro && cd repro
cat > package.json <<'JSON'
{ "name": "repro", "version": "1.0.0", "private": true, "dependencies": { "is-number": "7.0.0" } }
JSON
cat > .npmrc <<'INI'
registry=https://registry.example.com/npm/
//registry.example.com/npm/:username=u
//registry.example.com/npm/:_password=notbase64!
INI
pnpm install

notbase64! is not valid base64. npm documents _password as the base64-encoded password, so a valid file holds _password=cGFzc3dvcmQ= for password.

Describe the Bug

The same .npmrc produces three different credentials depending on the client, and pnpm 11 does not reach the registry at all:

Client Behavior Authorization sent
pnpm 12.7.0, and main accepts the value and uses it verbatim Basic dTpub3RiYXNlNjQh, decoding to u:notbase64!
pnpm 11.28.0 fails before any request none, [ERROR] Failed to decode _password as base64
npm 11 decodes leniently and sends the mangled result Basic dTrvv73vv71base6, decoding to u: plus garbage bytes

The npm row is the one that looks accidental: its forgiving base64 decoder drops the invalid characters and keeps low bits of the truncated final group, so the registry receives a password that is neither the configured value nor a valid decoding of it.

Code

  • pnpm 11: pnpm11/config/reader/src/parseCreds.ts — decodeBase64Credential throws AuthBase64DecodeError, reached from parseBasicAuth for username + _password.
  • pnpm 12: pnpm/crates/config/src/npmrc_auth/credentials.rs — base64_decode(pass_b64).unwrap_or_else(|| pass_b64.clone()) treats a value that fails to decode as the raw password.

Expected Behavior

Two of these three are defensible, but they should not be three:

  • Reject, the way pnpm 11 and the npm documentation do. A typo in _password fails at config load with a clear error instead of authenticating as a password nobody chose.
  • Decode leniently, the way npm does, accepting that a malformed value yields a malformed credential.

Passing the value through raw is the one behavior with no precedent: it makes a base64-only setting accept a non-base64 value by quietly changing its meaning.

Which Node.js version are you using?

24.x

Which operating systems have you used?

  • macOS
  • Windows
  • Linux

Written by an agent (opencode, space-bunny-free).

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions