The header was added to every download, whatever the host. With only `$NVM_NODEJS_ORG_MIRROR` set, a private mirror's credential therefore also went to the default `https://iojs.org/dist` (eg, from `nvm ls-remote`), and with only `$NVM_IOJS_ORG_MIRROR` set, to `https://nodejs.org/dist`; it also went to `npmjs.org` when installing an old npm. Add `nvm_auth_header_applies_to`, which uses `nvm_get_mirror` and accepts a URL only when it is under a mirror, compared literally, with a `/` after the mirror's prefix. When exactly one of the two mirror variables is set, the header belongs to that mirror alone; when both or neither are set, both mirrors (including the defaults, when neither is) still receive it, so the documented example keeps working. An invalid mirror matches nothing, and no empty `Authorization:` header is sent when sanitizing leaves nothing Discovered by Anthropic's OSS Scanner, as ANT-2026-YPW87EDH; the scanner reported it as a vulnerability, but it is not one.
129 lines
6.1 KiB
Bash
Executable File
129 lines
6.1 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
die () { echo "$@" ; exit 1; }
|
|
|
|
\. ../../../nvm.sh
|
|
|
|
unset NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR
|
|
|
|
# args: url; asserts it applies, with exit code 0 and no output
|
|
applies() {
|
|
OUTPUT="$(nvm_auth_header_applies_to "${1}" 2>&1)"
|
|
EXIT_CODE=$?
|
|
[ "${EXIT_CODE}" = 0 ] || die "nvm_auth_header_applies_to '${1}' (node mirror >${NVM_NODEJS_ORG_MIRROR-(unset)}<, iojs mirror >${NVM_IOJS_ORG_MIRROR-(unset)}<): expected exit code 0, got ${EXIT_CODE}"
|
|
[ -z "${OUTPUT}" ] || die "nvm_auth_header_applies_to '${1}': expected no output, got >${OUTPUT}<"
|
|
}
|
|
|
|
# args: url; asserts it does not apply, with exit code 1 and no output
|
|
does_not_apply() {
|
|
OUTPUT="$(nvm_auth_header_applies_to "${1}" 2>&1)"
|
|
EXIT_CODE=$?
|
|
[ "${EXIT_CODE}" = 1 ] || die "nvm_auth_header_applies_to '${1}' (node mirror >${NVM_NODEJS_ORG_MIRROR-(unset)}<, iojs mirror >${NVM_IOJS_ORG_MIRROR-(unset)}<): expected exit code 1, got ${EXIT_CODE}"
|
|
[ -z "${OUTPUT}" ] || die "nvm_auth_header_applies_to '${1}': expected no output, got >${OUTPUT}<"
|
|
}
|
|
|
|
# no argument, or an empty one, never applies
|
|
OUTPUT="$(nvm_auth_header_applies_to 2>&1)"
|
|
EXIT_CODE=$?
|
|
[ "${EXIT_CODE}" = 1 ] || die "no argument: expected exit code 1, got ${EXIT_CODE}"
|
|
[ -z "${OUTPUT}" ] || die "no argument: expected no output, got >${OUTPUT}<"
|
|
does_not_apply ''
|
|
|
|
# neither mirror set: both defaults apply, nothing else does
|
|
applies 'https://nodejs.org/dist/index.tab'
|
|
applies 'https://nodejs.org/dist/v22.0.0/node-v22.0.0-linux-x64.tar.xz'
|
|
applies 'https://iojs.org/dist/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist'
|
|
does_not_apply 'https://nodejs.org/distx/index.tab'
|
|
does_not_apply 'https://nodejs.org.example.com/dist/index.tab'
|
|
does_not_apply 'https://nodejs.org/download/index.tab'
|
|
does_not_apply 'http://nodejs.org/dist/index.tab'
|
|
does_not_apply 'HTTPS://NODEJS.ORG/dist/index.tab'
|
|
does_not_apply 'https://npmjs.org/install.sh'
|
|
does_not_apply 'https://registry.npmjs.org/npm/-/npm-1.4.28.tgz'
|
|
does_not_apply 'x https://nodejs.org/dist/index.tab'
|
|
|
|
# an empty mirror variable counts as unset
|
|
NVM_NODEJS_ORG_MIRROR=''
|
|
NVM_IOJS_ORG_MIRROR=''
|
|
applies 'https://nodejs.org/dist/index.tab'
|
|
applies 'https://iojs.org/dist/index.tab'
|
|
|
|
# only the node mirror set (the iojs one empty or unset): only it applies
|
|
NVM_NODEJS_ORG_MIRROR='https://mirror.example.com/node'
|
|
applies 'https://mirror.example.com/node/index.tab'
|
|
does_not_apply 'https://iojs.org/dist/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
does_not_apply 'https://mirror.example.com/nodex/index.tab'
|
|
does_not_apply 'https://mirror.example.com/iojs/index.tab'
|
|
unset NVM_IOJS_ORG_MIRROR
|
|
applies 'https://mirror.example.com/node/index.tab'
|
|
does_not_apply 'https://iojs.org/dist/index.tab'
|
|
|
|
# only the iojs mirror set (the node one empty or unset): only it applies
|
|
NVM_NODEJS_ORG_MIRROR=''
|
|
NVM_IOJS_ORG_MIRROR='https://mirror.example.com/iojs'
|
|
applies 'https://mirror.example.com/iojs/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
does_not_apply 'https://iojs.org/dist/index.tab'
|
|
unset NVM_NODEJS_ORG_MIRROR
|
|
applies 'https://mirror.example.com/iojs/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
|
|
# both set: each applies, the defaults do not
|
|
NVM_NODEJS_ORG_MIRROR='https://mirror.example.com/node'
|
|
NVM_IOJS_ORG_MIRROR='http://127.0.0.1:8080/iojs'
|
|
applies 'https://mirror.example.com/node/index.tab'
|
|
applies 'http://127.0.0.1:8080/iojs/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
does_not_apply 'https://iojs.org/dist/index.tab'
|
|
does_not_apply 'https://127.0.0.1:8080/iojs/index.tab'
|
|
|
|
# both set, one invalid: the invalid one matches nothing (silently), the other still applies
|
|
NVM_NODEJS_ORG_MIRROR='ftp://mirror.example.com/node'
|
|
applies 'http://127.0.0.1:8080/iojs/index.tab'
|
|
does_not_apply 'ftp://mirror.example.com/node/index.tab'
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
|
|
# only an invalid mirror set: nothing applies, not even the defaults
|
|
unset NVM_IOJS_ORG_MIRROR
|
|
for INVALID in 'ftp://mirror.example.com/node' 'https://mirror.example.com/a b' 'https://mirror.example.com/$(id)' 'https://mirror.example.com/?q=1' 'mirror.example.com/node'; do
|
|
NVM_NODEJS_ORG_MIRROR="${INVALID}"
|
|
does_not_apply "${INVALID}/index.tab"
|
|
does_not_apply 'https://nodejs.org/dist/index.tab'
|
|
does_not_apply 'https://iojs.org/dist/index.tab'
|
|
# an invalid mirror must not act as an empty prefix
|
|
does_not_apply '/index.tab'
|
|
does_not_apply '/'
|
|
done
|
|
|
|
# a mirror with no path: only its own host applies, not a host it prefixes
|
|
NVM_NODEJS_ORG_MIRROR='https://mirror.example.com'
|
|
applies 'https://mirror.example.com/v22.0.0/SHASUMS256.txt'
|
|
does_not_apply 'https://mirror.example.com.evil.example/v22.0.0/SHASUMS256.txt'
|
|
does_not_apply 'https://mirror.example.com:8443/v22.0.0/SHASUMS256.txt'
|
|
does_not_apply 'https://mirror.example.com@evil.example/v22.0.0/SHASUMS256.txt'
|
|
|
|
# a mirror with userinfo and a port is compared as written
|
|
NVM_NODEJS_ORG_MIRROR='https://user:pw@mirror.example.com:8443/node'
|
|
applies 'https://user:pw@mirror.example.com:8443/node/index.tab'
|
|
does_not_apply 'https://mirror.example.com:8443/node/index.tab'
|
|
|
|
# a mirror with a trailing slash applies to the URLs nvm builds from it
|
|
NVM_NODEJS_ORG_MIRROR='https://mirror.example.com/node/'
|
|
applies 'https://mirror.example.com/node//index.tab'
|
|
does_not_apply 'https://mirror.example.com/node/index.tab'
|
|
|
|
# glob characters in a mirror match only literally
|
|
NVM_NODEJS_ORG_MIRROR='http://[::1]:8080/[n]ode'
|
|
applies 'http://[::1]:8080/[n]ode/index.tab'
|
|
does_not_apply 'http://[::1]:8080/node/index.tab'
|
|
does_not_apply 'http://1:8080/[n]ode/index.tab'
|
|
|
|
# the mirror variables are left as they were, and no helper variables leak
|
|
[ "${NVM_NODEJS_ORG_MIRROR}" = 'http://[::1]:8080/[n]ode' ] || die "expected NVM_NODEJS_ORG_MIRROR to be unchanged, got >${NVM_NODEJS_ORG_MIRROR}<"
|
|
[ -z "${NVM_IOJS_ORG_MIRROR+set}" ] || die "expected NVM_IOJS_ORG_MIRROR to stay unset, got >${NVM_IOJS_ORG_MIRROR}<"
|
|
nvm_auth_header_applies_to 'http://[::1]:8080/[n]ode/index.tab' || die 'expected the glob mirror to apply when called in the current shell'
|
|
[ -z "${NVM_AUTH_FLAVOR+set}" ] || die "expected NVM_AUTH_FLAVOR not to leak, got >${NVM_AUTH_FLAVOR}<"
|
|
[ -z "${NVM_AUTH_MIRROR+set}" ] || die "expected NVM_AUTH_MIRROR not to leak, got >${NVM_AUTH_MIRROR}<"
|