[Fix] nvm_download_artifact, nvm debug: mask credentials in mirror URLs when printing them
`nvm_get_mirror` accepts `user:password@` in `$NVM_NODEJS_ORG_MIRROR` and `$NVM_IOJS_ORG_MIRROR`, but `nvm_download_artifact` printed the full tarball URL in its "Downloading", "download from ... failed", and "HTTP 404 at URL" messages, and `nvm debug` printed both mirror variables as-is, so the credentials showed up in terminal output and build logs. Add `nvm_sanitize_url`, which replaces the userinfo part of any URL with `***@`, and use it in those messages. The download itself still uses the full URL, so authenticating to the mirror keeps working. The README now notes that `$NVM_AUTH_HEADER` is the recommended way to authenticate to a mirror Discovered by Anthropic's OSS Scanner, as ANT-2026-X03SZ7C6; the scanner reported it as a vulnerability, but it is not one.
This commit is contained in:
4 files changed
+121
-6
No files matched your search
@@ -0,0 +1,76 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="$PWD/mirror-url-credentials-work.$$"
|
||||
ORIG_NVM_DIR="${NVM_DIR-}"
|
||||
|
||||
cleanup() {
|
||||
unset -f nvm_download nvm_get_checksum nvm_compare_checksum
|
||||
unset NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR
|
||||
NVM_DIR="${ORIG_NVM_DIR}"
|
||||
rm -rf "${WORK}"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
# args: description, output
|
||||
assert_masked() {
|
||||
case "${2}" in
|
||||
*s3cr3t* | *ci-bot*) die "${1} printed the mirror URL credentials; got >${2}<" ;;
|
||||
esac
|
||||
case "${2}" in
|
||||
*'://***@mirror.example.com/dist'*) ;;
|
||||
*) die "${1} did not print the masked mirror URL; got >${2}<" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
mkdir -p "${WORK}"
|
||||
|
||||
NVM_NODEJS_ORG_MIRROR='https://ci-bot:s3cr3t@mirror.example.com/dist'
|
||||
NVM_IOJS_ORG_MIRROR='https://ci-bot:s3cr3t@mirror.example.com/dist'
|
||||
export NVM_NODEJS_ORG_MIRROR NVM_IOJS_ORG_MIRROR
|
||||
|
||||
assert_masked 'nvm debug' "$(nvm debug 2>&1)"
|
||||
|
||||
NVM_DIR="${WORK}"
|
||||
nvm_get_checksum() { nvm_echo 'fake_checksum'; }
|
||||
nvm_compare_checksum() { return 0; }
|
||||
|
||||
# nvm_download still receives the unmasked URL
|
||||
nvm_download() {
|
||||
case "$*" in
|
||||
*"${NVM_NODEJS_ORG_MIRROR}/v20.0.0/"*) ;;
|
||||
*) nvm_err 'nvm_download did not receive the mirror URL credentials' ;;
|
||||
esac
|
||||
return "${DOWNLOAD_EXIT_CODE-0}"
|
||||
}
|
||||
OUTPUT="$(nvm_download_artifact node source std v20.0.0 2>&1)"
|
||||
assert_masked 'nvm_download_artifact' "${OUTPUT}"
|
||||
case "${OUTPUT}" in
|
||||
*'did not receive'*) die "${OUTPUT}" ;;
|
||||
esac
|
||||
|
||||
OUTPUT="$(DOWNLOAD_EXIT_CODE=1 nvm_download_artifact node source std v20.0.0 2>&1)"
|
||||
case "${OUTPUT}" in
|
||||
*'download from '*' failed'*) ;;
|
||||
*) die "expected a download failure message; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
assert_masked 'nvm_download_artifact, on a failed download,' "${OUTPUT#*...}"
|
||||
|
||||
nvm_download() {
|
||||
while [ "$#" -gt 0 ]; do
|
||||
if [ "$1" = '-o' ]; then
|
||||
nvm_echo '404 Not Found' > "$2"
|
||||
return 0
|
||||
fi
|
||||
shift
|
||||
done
|
||||
}
|
||||
OUTPUT="$(nvm_download_artifact node source std v20.0.0 2>&1)"
|
||||
case "${OUTPUT}" in
|
||||
*'HTTP 404 at URL '*) ;;
|
||||
*) die "expected an HTTP 404 message; got >${OUTPUT}<" ;;
|
||||
esac
|
||||
assert_masked 'nvm_download_artifact, on a 404,' "${OUTPUT#*...}"
|
||||
|
||||
cleanup
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
|
||||
die () { echo "$@" ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
check() {
|
||||
ACTUAL="$(nvm_sanitize_url "${1}")"
|
||||
[ "${ACTUAL}" = "${2}" ] || die "nvm_sanitize_url '${1}': expected >${2}<, got >${ACTUAL}<"
|
||||
}
|
||||
|
||||
# URLs without userinfo are unchanged
|
||||
check '' ''
|
||||
check 'https://nodejs.org/dist' 'https://nodejs.org/dist'
|
||||
check 'https://nodejs.org' 'https://nodejs.org'
|
||||
check 'http://127.0.0.1:8080/dist/v20.0.0/node-v20.0.0.tar.xz' 'http://127.0.0.1:8080/dist/v20.0.0/node-v20.0.0.tar.xz'
|
||||
check 'https://[::1]:8080/dist' 'https://[::1]:8080/dist'
|
||||
check 'https://nodejs.org/a@b/dist' 'https://nodejs.org/a@b/dist'
|
||||
|
||||
# userinfo is masked
|
||||
check 'https://user:pw@nexus.example.com/repository/nodejs-dist' 'https://***@nexus.example.com/repository/nodejs-dist'
|
||||
check 'https://user:pw@nexus.example.com' 'https://***@nexus.example.com'
|
||||
check 'https://token@nexus.example.com:8443/dist' 'https://***@nexus.example.com:8443/dist'
|
||||
check 'http://user:p%40w@[::1]:8080/dist' 'http://***@[::1]:8080/dist'
|
||||
# everything up to the last `@` before the path is userinfo; an `@` in the path is kept
|
||||
check 'https://user:p@w@nexus.example.com/a@b' 'https://***@nexus.example.com/a@b'
|
||||
|
||||
# every URL in multiline text is masked
|
||||
check 'URL:http://user:pw@a.example.com/x [1/1]
|
||||
https://user:pw@b.example.com/y' 'URL:http://***@a.example.com/x [1/1]
|
||||
https://***@b.example.com/y'
|
||||
Reference in new issue
Block a user