Skip to content

Commit 0982203

Browse files
committed
[Fix] nvm exec/nvm run: do not treat filenames with digits as versions
`nvm_is_valid_version` accepted strings like `123.js`, `v1.js`, and `1.2.3.4.5.6.js`, because `nvm_version_greater_than_or_equal_to` exits as soon as the first number is greater than zero, without looking at the rest. So `nvm run 123.js` failed with `N/A: version "v123.js" is not yet installed` instead of running the file. It now checks the shape first: one to three dot-separated numbers, optionally with a trailing dot, and a prerelease suffix (rc, nightly, v8-canary, test builds) only after a full `x.y.z`. A suffix with a dot followed by a non-digit, like a file extension, is rejected, so names like `1.2.3-foo.js` are treated as files too. Every version listed in the nodejs.org and iojs.org `index.tab` files is still accepted, so shell startup keeps using a `default` alias that resolves to an installed prerelease. Names that are also valid versions, like `123` or `v1`, are still treated as versions; `nvm run current 123` runs such a file. Refs #1166. See #3931.
1 parent a885b88 commit 0982203

4 files changed

Lines changed: 155 additions & 0 deletions

File tree

‎nvm.sh‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,27 @@ nvm_is_valid_version() {
10151015
*)
10161016
local VERSION
10171017
VERSION="$(nvm_strip_iojs_prefix "${1-}")"
1018+
local NVM_VERSION_CORE
1019+
NVM_VERSION_CORE="${VERSION#v}"
1020+
case "${NVM_VERSION_CORE}" in
1021+
*-*)
1022+
# prereleases (rc, nightly, v8-canary, ...) need a full x.y.z, and never have a dot followed by a non-digit, like a file extension
1023+
case "${NVM_VERSION_CORE#*-}" in
1024+
'' | .* | *. | *..* | *.*[!0-9.]* | *[!0-9A-Za-z.-]*) return 1 ;;
1025+
esac
1026+
NVM_VERSION_CORE="${NVM_VERSION_CORE%%-*}"
1027+
case "${NVM_VERSION_CORE}" in
1028+
*.*.*) ;;
1029+
*) return 1 ;;
1030+
esac
1031+
;;
1032+
*.)
1033+
NVM_VERSION_CORE="${NVM_VERSION_CORE%.}"
1034+
;;
1035+
esac
1036+
case "${NVM_VERSION_CORE}" in
1037+
'' | .* | *. | *..* | *.*.*.* | *[!0-9.]*) return 1 ;;
1038+
esac
10181039
nvm_version_greater_than_or_equal_to "${VERSION}" 0
10191040
;;
10201041
esac
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/bin/sh
2+
3+
set -ex
4+
5+
die () { echo "$@" ; cleanup ; exit 1; }
6+
7+
cleanup() {
8+
cd "${ORIG_PWD}" 2>/dev/null || true
9+
[ -n "${TMP_DIR-}" ] && rm -rf "${TMP_DIR}"
10+
}
11+
12+
export NVM_DIR="$(cd ../.. && pwd)"
13+
14+
: nvm.sh
15+
\. ../../nvm.sh
16+
17+
\. ../common.sh
18+
19+
ORIG_PWD="$(pwd)"
20+
21+
# Run from a fresh, empty directory so no ambient .nvmrc above the test dir
22+
# can satisfy the lookup and mask the warning.
23+
TMP_DIR="$(mktemp -d)"
24+
cd "${TMP_DIR}" || die "could not cd to temp dir"
25+
26+
EXEC_WARNING='WARNING: `nvm exec` was invoked without a version argument and without an .nvmrc file.'
27+
RUN_WARNING='WARNING: `nvm run` was invoked without a version argument and without an .nvmrc file.'
28+
29+
for FILE in 123.js v1.23.js 1.2.3.js; do
30+
set +ex # needed for stderr
31+
RUN_STDERR="$(nvm run "${FILE}" </dev/null 2>&1 1>/dev/null)"
32+
set -ex
33+
case "${RUN_STDERR}" in
34+
*"N/A: version \"v${FILE#v}\""*) die "'nvm run ${FILE}' treated the file as a version; got >${RUN_STDERR}<" ;;
35+
*"${RUN_WARNING}"*) ;;
36+
*) die "'nvm run ${FILE}' did not fall back with a warning; got >${RUN_STDERR}<" ;;
37+
esac
38+
done
39+
40+
set +ex # needed for stderr
41+
EXEC_STDERR="$(nvm exec 1.2.3.js </dev/null 2>&1 1>/dev/null)"
42+
set -ex
43+
case "${EXEC_STDERR}" in
44+
*'N/A: version "v1.2.3.js"'*) die "'nvm exec 1.2.3.js' treated the command as a version; got >${EXEC_STDERR}<" ;;
45+
*"${EXEC_WARNING}"*) ;;
46+
*) die "'nvm exec 1.2.3.js' did not fall back with a warning; got >${EXEC_STDERR}<" ;;
47+
esac
48+
49+
# an uninstalled prerelease is still a version, not a file
50+
set +ex # needed for stderr
51+
RUN_RC_STDERR="$(nvm run v999.0.0-rc.1 app.js </dev/null 2>&1 1>/dev/null)"
52+
set -ex
53+
case "${RUN_RC_STDERR}" in
54+
*'N/A: version "v999.0.0-rc.1" is not yet installed.'*) ;;
55+
*) die "'nvm run v999.0.0-rc.1' did not report the prerelease as not installed; got >${RUN_RC_STDERR}<" ;;
56+
esac
57+
58+
set +ex # needed for stderr
59+
EXEC_RC_STDERR="$(nvm exec v999.0.0-rc.1 node </dev/null 2>&1 1>/dev/null)"
60+
set -ex
61+
case "${EXEC_RC_STDERR}" in
62+
*'N/A: version "v999.0.0-rc.1" is not yet installed.'*) ;;
63+
*) die "'nvm exec v999.0.0-rc.1' did not report the prerelease as not installed; got >${EXEC_RC_STDERR}<" ;;
64+
esac
65+
66+
cleanup
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/bin/sh
2+
3+
ORIG_NVM_DIR="${NVM_DIR}"
4+
TMP_DIR=''
5+
6+
cleanup () {
7+
case "${TMP_DIR}" in
8+
/*) rm -rf "${TMP_DIR}" ;;
9+
esac
10+
NVM_DIR="${ORIG_NVM_DIR}"
11+
export NVM_DIR
12+
}
13+
14+
die () { echo "$@" ; cleanup ; exit 1; }
15+
16+
: nvm.sh
17+
\. ../../../nvm.sh
18+
19+
\. ../../common.sh
20+
21+
TMP_DIR="$(mktemp -d)"
22+
NVM_DIR="${TMP_DIR}/nvm"
23+
export NVM_DIR
24+
mkdir -p "${NVM_DIR}/alias" || die 'could not create alias dir'
25+
26+
RELEASE='v998.0.0'
27+
NIGHTLY='v999.0.0-nightly20241031996708042b'
28+
make_fake_node "${RELEASE}" || die "fake node ${RELEASE} could not be made"
29+
make_fake_node "${NIGHTLY}" || die "fake node ${NIGHTLY} could not be made"
30+
31+
for TARGET in "${NIGHTLY}" node; do
32+
nvm_make_alias default "${TARGET}" || die "could not alias default to ${TARGET}"
33+
nvm deactivate >/dev/null 2>&1
34+
nvm_auto use || die "nvm_auto use failed with default -> ${TARGET}"
35+
CURRENT="$(nvm current)"
36+
[ "${CURRENT}" = "${NIGHTLY}" ] || die "with default -> ${TARGET}, nvm_auto use left 'nvm current' as >${CURRENT}<, expected >${NIGHTLY}<"
37+
done
38+
39+
cleanup

‎test/fast/Unit tests/nvm_is_valid_version‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,32 @@ nvm_is_valid_version iojs || die "nvm_is_valid_version iojs did not return 0"
1212
nvm_is_valid_version node || die "nvm_is_valid_version node did not return 0"
1313
nvm_is_valid_version stable || die "nvm_is_valid_version stable did not return 0"
1414
nvm_is_valid_version unstable || die "nvm_is_valid_version unstable did not return 0"
15+
16+
nvm_is_valid_version 123.js && die "nvm_is_valid_version 123.js did not return 1"
17+
nvm_is_valid_version v123.js && die "nvm_is_valid_version v123.js did not return 1"
18+
nvm_is_valid_version v1.js && die "nvm_is_valid_version v1.js did not return 1"
19+
nvm_is_valid_version v1.23.js && die "nvm_is_valid_version v1.23.js did not return 1"
20+
nvm_is_valid_version 1.2.3.js && die "nvm_is_valid_version 1.2.3.js did not return 1"
21+
nvm_is_valid_version 1.2.3.4.5.6.js && die "nvm_is_valid_version 1.2.3.4.5.6.js did not return 1"
22+
nvm_is_valid_version 1.2.3.4 && die "nvm_is_valid_version 1.2.3.4 did not return 1"
23+
nvm_is_valid_version 1..2 && die "nvm_is_valid_version 1..2 did not return 1"
24+
nvm_is_valid_version 1.2.3-foo.js && die "nvm_is_valid_version 1.2.3-foo.js did not return 1"
25+
nvm_is_valid_version 2.0.0-migration.js && die "nvm_is_valid_version 2.0.0-migration.js did not return 1"
26+
nvm_is_valid_version 1.2-3.4-5 && die "nvm_is_valid_version 1.2-3.4-5 did not return 1"
27+
nvm_is_valid_version 1.2.3- && die "nvm_is_valid_version 1.2.3- did not return 1"
28+
nvm_is_valid_version 1.2.3-rc..1 && die "nvm_is_valid_version 1.2.3-rc..1 did not return 1"
29+
nvm_is_valid_version '1.js
30+
2' && die "nvm_is_valid_version with an embedded newline did not return 1"
31+
32+
nvm_is_valid_version 1 || die "nvm_is_valid_version 1 did not return 0"
33+
nvm_is_valid_version v1 || die "nvm_is_valid_version v1 did not return 0"
34+
nvm_is_valid_version 1.2 || die "nvm_is_valid_version 1.2 did not return 0"
35+
nvm_is_valid_version v18.20.0 || die "nvm_is_valid_version v18.20.0 did not return 0"
36+
nvm_is_valid_version 20. || die "nvm_is_valid_version 20. did not return 0"
37+
nvm_is_valid_version v18.20. || die "nvm_is_valid_version v18.20. did not return 0"
38+
nvm_is_valid_version iojs-v1.0.0 || die "nvm_is_valid_version iojs-v1.0.0 did not return 0"
39+
nvm_is_valid_version v9.4.0-rc.1 || die "nvm_is_valid_version v9.4.0-rc.1 did not return 0"
40+
nvm_is_valid_version v9.9.1-nightly201803256591d9f761 || die "nvm_is_valid_version v9.9.1-nightly201803256591d9f761 did not return 0"
41+
nvm_is_valid_version v9.0.0-v8-canary201710301f8f3dcf36 || die "nvm_is_valid_version v9.0.0-v8-canary201710301f8f3dcf36 did not return 0"
42+
nvm_is_valid_version v8.16.2-test201910293843c56739f || die "nvm_is_valid_version v8.16.2-test201910293843c56739f did not return 0"
43+
nvm_is_valid_version iojs-v3.2.1-nightly201508291a531b4e44 || die "nvm_is_valid_version iojs-v3.2.1-nightly201508291a531b4e44 did not return 0"

0 commit comments

Comments
 (0)