From c8a2a81f926c268133ba88c2d1818d98c15fc5c7 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 18 Oct 2026 23:11:39 -0700 Subject: [PATCH] [Fix] `nvm_download`, `nvm_get_latest`, `install.sh`: pass `-q` to curl first, so `.curlrc` is ignored again curl only skips reading `.curlrc` when `-q` is its very first argument; anywhere else, the config file has already been read by the time `-q` is seen. a4f89c62 added `-q` to every curl call so that a user's `.curlrc` would not affect nvm, but 6a3b1dd2 (v0.33.2) put `--compressed` in front of it everywhere. bd090ef7 (v0.40.0) moved it back to the front in `nvm.sh`'s `nvm_download`, and then 6d870d18 (v0.40.5) began building those arguments with `set --`, prepending `--compressed` after `-q`, so whenever curl supports compression, `-q` was second again. `nvm_get_latest` and `install.sh`'s `nvm_download` have passed `--compressed` before `-q` since v0.33.2. Pass `-q` first in all three places. Settings in a user's `.curlrc` (such as a proxy, CA bundle, or `write-out`) no longer apply to nvm's own curl calls, as was intended, and as in v0.40.0 through v0.40.4 for `nvm_download`; the corresponding environment variables (eg, `https_proxy`, `CURL_CA_BUNDLE`) still apply. The existing `nvm_get_latest` test pinned the old argument order, so its expectation is updated Discovered by Anthropic's OSS Scanner, as ANT-2026-DESJ2ESZ; the scanner reported it as a vulnerability, but it is not one. --- install.sh | 3 +- nvm.sh | 6 ++- ...d and nvm_get_latest pass -q to curl first | 51 +++++++++++++++++++ test/install_script/nvm_download_curl_q_first | 36 +++++++++++++ test/slow/nvm_get_latest/nvm_get_latest | 2 +- 5 files changed, 94 insertions(+), 4 deletions(-) create mode 100755 test/fast/Unit tests/nvm_download and nvm_get_latest pass -q to curl first create mode 100755 test/install_script/nvm_download_curl_q_first diff --git a/install.sh b/install.sh index 38ce997f..3ae0bd31 100755 --- a/install.sh +++ b/install.sh @@ -118,7 +118,8 @@ nvm_node_version() { nvm_download() { if nvm_has_executable "curl"; then - command curl --fail --compressed -q "$@" + # curl only skips reading `.curlrc` when `-q` is its very first argument + command curl -q --fail --compressed "$@" elif nvm_has_executable "wget"; then # Emulate curl with wget local NVM_DOWNLOAD_WGET_COUNT diff --git a/nvm.sh b/nvm.sh index a95b7884..532df111 100755 --- a/nvm.sh +++ b/nvm.sh @@ -138,7 +138,7 @@ nvm_get_latest() { if nvm_curl_use_compression; then CURL_COMPRESSED_FLAG="--compressed" fi - NVM_LATEST_URL="$(command curl ${CURL_COMPRESSED_FLAG:-} -q -w "%{url_effective}\\n" -L -s -S https://latest.nvm.sh -o /dev/null)" + NVM_LATEST_URL="$(command curl -q ${CURL_COMPRESSED_FLAG:-} -w "%{url_effective}\\n" -L -s -S https://latest.nvm.sh -o /dev/null)" elif nvm_has_executable "wget"; then NVM_LATEST_URL="$(command wget -q https://latest.nvm.sh --server-response -O /dev/null 2>&1 | command awk '/^ Location: /{DEST=$2} END{ print DEST }')" else @@ -166,10 +166,12 @@ nvm_download() { NVM_DOWNLOADER='' if nvm_has_executable "curl"; then NVM_DOWNLOADER='curl' - set -- -q --fail "$@" + set -- --fail "$@" if nvm_curl_use_compression; then set -- --compressed "$@" fi + # curl only skips reading `.curlrc` when `-q` is its very first argument + set -- -q "$@" elif nvm_has_executable "wget"; then NVM_DOWNLOADER='wget' # Emulate curl with wget diff --git a/test/fast/Unit tests/nvm_download and nvm_get_latest pass -q to curl first b/test/fast/Unit tests/nvm_download and nvm_get_latest pass -q to curl first new file mode 100755 index 00000000..bd544714 --- /dev/null +++ b/test/fast/Unit tests/nvm_download and nvm_get_latest pass -q to curl first @@ -0,0 +1,51 @@ +#!/bin/sh + +WORK="$PWD/curl-q-first-work.$$" +TEST_BIN="$WORK/bin" +ARGV_LOG="$WORK/argv.log" + +cleanup () { + unset -f die cleanup + rm -rf "$WORK" + export PATH="$OLDPATH" +} + +die () { echo "$@" ; cleanup ; exit 1; } + +NVM_ENV=testing \. ../../../nvm.sh + +\. ../../common.sh + +OLDPATH="$PATH" + +# curl only skips reading `.curlrc` when `-q` is its very first argument +make_fake_curl "$TEST_BIN" ': > "$ARGV_LOG" +for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done +echo "https://github.com/nvm-sh/nvm/releases/tag/v0.0.0"' + +export ARGV_LOG +export PATH="$TEST_BIN:$OLDPATH" + +# with compression support, --compressed must still come after -q +VERSION_MESSAGE="curl 7.99.0 (fake) +Features: libz" + +nvm_download -s 'http://example.com/' -o /dev/null || die 'nvm_download failed with compression support' +[ "$(head -n 1 "$ARGV_LOG")" = '-q' ] || die "nvm_download (with compression) did not pass -q first; got: $(cat "$ARGV_LOG")" +grep -Fxqe '--compressed' "$ARGV_LOG" || die "nvm_download (with compression) did not pass --compressed; got: $(cat "$ARGV_LOG")" + +nvm_get_latest > /dev/null || die 'nvm_get_latest failed with compression support' +[ "$(head -n 1 "$ARGV_LOG")" = '-q' ] || die "nvm_get_latest (with compression) did not pass -q first; got: $(cat "$ARGV_LOG")" +grep -Fxqe '--compressed' "$ARGV_LOG" || die "nvm_get_latest (with compression) did not pass --compressed; got: $(cat "$ARGV_LOG")" + +# without compression support +VERSION_MESSAGE="curl 7.99.0 (fake) +Features: SSL" + +nvm_download -s 'http://example.com/' -o /dev/null || die 'nvm_download failed without compression support' +[ "$(head -n 1 "$ARGV_LOG")" = '-q' ] || die "nvm_download (without compression) did not pass -q first; got: $(cat "$ARGV_LOG")" + +nvm_get_latest > /dev/null || die 'nvm_get_latest failed without compression support' +[ "$(head -n 1 "$ARGV_LOG")" = '-q' ] || die "nvm_get_latest (without compression) did not pass -q first; got: $(cat "$ARGV_LOG")" + +cleanup diff --git a/test/install_script/nvm_download_curl_q_first b/test/install_script/nvm_download_curl_q_first new file mode 100755 index 00000000..18778387 --- /dev/null +++ b/test/install_script/nvm_download_curl_q_first @@ -0,0 +1,36 @@ +#!/bin/sh + +OLDPATH="$PATH" +WORK="$PWD/nvm_download-q-first-work.$$" +TEST_BIN="$WORK/bin" +ARGV_LOG="$WORK/argv.log" + +cleanup() { + unset -f die cleanup + rm -rf "$WORK" + export PATH="$OLDPATH" +} +die () { echo "$@" ; cleanup ; exit 1; } + +NVM_ENV=testing \. ../../install.sh + +mkdir -p "$TEST_BIN" + +# fake curl: record each received argument, then succeed +{ + echo '#!/bin/sh' + echo ': > "$ARGV_LOG"' + echo 'for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done' + echo 'exit 0' +} > "$TEST_BIN/curl" +chmod +x "$TEST_BIN/curl" + +export ARGV_LOG +export PATH="$TEST_BIN:$OLDPATH" + +nvm_download -s 'http://example.test/nvm.sh' -o - || die 'nvm_download returned nonzero' + +# curl only skips reading `.curlrc` when `-q` is its very first argument +[ "$(head -n 1 "$ARGV_LOG")" = '-q' ] || die "curl did not receive -q as its first argument; got: $(cat "$ARGV_LOG")" + +cleanup diff --git a/test/slow/nvm_get_latest/nvm_get_latest b/test/slow/nvm_get_latest/nvm_get_latest index 0a12b21c..9c2f334c 100755 --- a/test/slow/nvm_get_latest/nvm_get_latest +++ b/test/slow/nvm_get_latest/nvm_get_latest @@ -19,7 +19,7 @@ OLDPATH="$PATH" EXPECTED_VERSION="v12.3.456" URL="https://github.com/nvm-sh/nvm/releases/tag/$EXPECTED_VERSION" -EXPECTED_CURL_ARGS="--compressed -q -w %{url_effective}\n -L -s -S https://latest.nvm.sh -o /dev/null" +EXPECTED_CURL_ARGS="-q --compressed -w %{url_effective}\n -L -s -S https://latest.nvm.sh -o /dev/null" EXPECTED_WGET_ARGS="-q https://latest.nvm.sh --server-response -O /dev/null" export URL EXPECTED_CURL_ARGS EXPECTED_WGET_ARGS