1
0
mirror of https://github.com/nvm-sh/nvm.git synced 2026-10-26 16:33:45 +08:00

[Fix] nvm use, nvm-exec: stop option parsing at --

`nvm use` treated `--` as a no-op and kept parsing options after it,
so `nvm use -- -w` enabled `--save`,
fell back to `.nvmrc`,
and rewrote it with the resolved version;
`--delete-prefix` and unknown `--*` arguments after
`--` were likewise acted on or silently dropped.

Now the argument after `--` is taken as the version and option parsing stops there;
a lone `--` still falls back to `.nvmrc`.
`nvm-exec` now passes `NODE_VERSION` after `--`,
so its value is always treated as a version and never as an option

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-4RO8S9N0; the scanner correctly classified it as not a vulnerability.
This commit is contained in:
Jordan Harband
2026-10-18 23:09:34 -07:00
parent 020503b536
commit 39fca6c905
3 changed files with 65 additions and 2 deletions

View File

@@ -8,7 +8,7 @@ unset NVM_CD_FLAGS
\. "$DIR/nvm.sh" --no-use
if [ -n "$NODE_VERSION" ]; then
nvm use "$NODE_VERSION" > /dev/null || exit 127
nvm use -- "$NODE_VERSION" > /dev/null || exit 127
else
{ NVM_RC_VERSION="$(nvm_rc_version 3>&1 1>&4)"; } 4>&1 && nvm_ensure_version_installed "$NVM_RC_VERSION";
if ! nvm use >/dev/null 2>&1; then

7
nvm.sh
View File

@@ -4759,7 +4759,12 @@ nvm() {
NVM_SILENT_ARG='--silent'
;;
--delete-prefix) NVM_DELETE_PREFIX=1 ;;
--) ;;
--)
if [ -n "${2-}" ]; then
PROVIDED_VERSION="$2"
fi
break
;;
--lts) NVM_LTS='*' ;;
--lts=*) NVM_LTS="${1##--lts=}" ;;
--save | -w)

View File

@@ -0,0 +1,58 @@
#!/bin/sh
set -ex
# nvm-exec is a separate bash process: give it the same NVM_DIR as this shell,
# and empty BASH_ENV so a profile there (like the one in this repo's Dockerfile) can't replace it
export NVM_DIR="$(cd ../.. && pwd)"
unset NODE_VERSION
: nvm.sh
\. ../../nvm.sh
\. ../common.sh
ORIG_PWD="$(pwd)"
NVM_EXEC="${NVM_DIR}/nvm-exec"
cleanup() {
cd "${ORIG_PWD}" 2>/dev/null || true
rm -rf "$(nvm_version_path v0.0.1)"
[ -n "${TMP_DIR-}" ] && rm -rf "${TMP_DIR}"
}
die () { echo "$@" ; cleanup ; exit 1; }
make_fake_node v0.0.1
TMP_DIR="$(mktemp -d)"
cd "${TMP_DIR}" || die 'could not cd to temp dir'
HAND_WRITTEN='0.0.1'
printf '%s\n' "${HAND_WRITTEN}" > .nvmrc
try nvm use -- -w
[ "$(cat .nvmrc)" = "${HAND_WRITTEN}" ] || die "\`nvm use -- -w\` rewrote .nvmrc to >$(cat .nvmrc)<"
[ "${CAPTURED_EXIT_CODE}" != 0 ] || die '`nvm use -- -w` should fail, since `-w` is not a version'
try nvm use -- --save
[ "$(cat .nvmrc)" = "${HAND_WRITTEN}" ] || die "\`nvm use -- --save\` rewrote .nvmrc to >$(cat .nvmrc)<"
[ "${CAPTURED_EXIT_CODE}" != 0 ] || die '`nvm use -- --save` should fail, since `--save` is not a version'
try env BASH_ENV= NODE_VERSION=0.0.1 "${NVM_EXEC}" node --version
[ "${CAPTURED_STDOUT}" = 'v0.0.1' ] || die "\`NODE_VERSION=0.0.1 nvm-exec node --version\` should run v0.0.1, got >${CAPTURED_STDOUT}<"
try env BASH_ENV= NODE_VERSION=-w "${NVM_EXEC}" node --version
[ "$(cat .nvmrc)" = "${HAND_WRITTEN}" ] || die "\`NODE_VERSION=-w nvm-exec\` rewrote .nvmrc to >$(cat .nvmrc)<"
[ "${CAPTURED_EXIT_CODE}" = 127 ] || die "\`NODE_VERSION=-w nvm-exec\` should exit 127, got >${CAPTURED_EXIT_CODE}<"
try nvm use --
[ "${CAPTURED_EXIT_CODE}" = 0 ] || die "a lone \`nvm use --\` should fall back to .nvmrc, got exit >${CAPTURED_EXIT_CODE}<"
try nvm use -- 0.0.1
[ "${CAPTURED_EXIT_CODE}" = 0 ] || die "\`nvm use -- 0.0.1\` should succeed, got exit >${CAPTURED_EXIT_CODE}<"
[ "$(cat .nvmrc)" = "${HAND_WRITTEN}" ] || die "\`nvm use -- 0.0.1\` rewrote .nvmrc to >$(cat .nvmrc)<"
try nvm use --save -- 0.0.1
[ "${CAPTURED_EXIT_CODE}" = 0 ] || die "\`nvm use --save -- 0.0.1\` should succeed, got exit >${CAPTURED_EXIT_CODE}<"
[ "$(cat .nvmrc)" = 'v0.0.1' ] || die "\`nvm use --save -- 0.0.1\` should write v0.0.1 to .nvmrc, got >$(cat .nvmrc)<"
cleanup