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

[Fix] nvm_ls: match the version pattern literally

nvm_ls escaped only `.` and `#` before using the pattern
(which may come from a `.nvmrc`)
as a sed address,
so any other regex characters in it were interpreted:
`v2[0-9]` matched installed versions,
`[` made sed fail with an error,
and nested bounded repetitions like `\(v\{1,300\}\)\{1,300\}`
made sed take a very long time and a lot of memory to compile,
stalling `nvm use` and shell startup.
Escape every BRE metacharacter,
including the backslash,
so the pattern is always matched as literal text

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-HZAFTTUK; the scanner correctly classified it as not a vulnerability.
This commit is contained in:
Jordan Harband
2026-10-18 23:06:20 -07:00
parent b076039a60
commit 66228bd1fa
2 changed files with 58 additions and 1 deletions

2
nvm.sh
View File

@@ -1942,7 +1942,7 @@ nvm_ls() {
PATTERN='v'
SEARCH_PATTERN='.*'
else
SEARCH_PATTERN="$(nvm_echo "${PATTERN}" | command sed 's#\.#\\\.#g; s|#|\\#|g')"
SEARCH_PATTERN="$(nvm_echo "${PATTERN}" | command sed 's/[[\.*^$#]/\\&/g')"
fi
if [ -n "${NVM_DIRS_TO_SEARCH1}${NVM_DIRS_TO_SEARCH2}${NVM_DIRS_TO_SEARCH3}" ]; then
VERSIONS="$(command find "${NVM_DIRS_TO_SEARCH1}"/* "${NVM_DIRS_TO_SEARCH2}"/* "${NVM_DIRS_TO_SEARCH3}"/* -name . -o -type d -prune -o -path "${PATTERN}*" \

View File

@@ -0,0 +1,57 @@
#!/bin/sh
die () { echo "$@" ; cleanup ; exit 1; }
cleanup() {
rm -rf "${TEST_DIR-}"
unset -f assert_ls assert_no_match
unset TEST_DIR NVM_DIR PATTERN
}
: nvm.sh
\. ../../../nvm.sh
\. ../../common.sh
# an isolated NVM_DIR with known versions, so that ambient versions
# and aliases can not affect any assertions
TEST_DIR="${PWD}/nvm_ls_literal_pattern_tmp"
for VERSION in v20.0.0 v22.1.0 v24.13.0; do
mkdir -p "${TEST_DIR}/versions/node/${VERSION}/bin" || die "failed to create ${VERSION} dir"
make_echo "${TEST_DIR}/versions/node/${VERSION}/bin/node" "${VERSION}" || die "failed to create ${VERSION} node binary"
done
unset VERSION
NVM_DIR="${TEST_DIR}"
assert_ls() {
try nvm_ls "$1"
[ "_${CAPTURED_STDOUT}" = "_$2" ] || die "\`nvm_ls '$1'\` should output >$2<; got >${CAPTURED_STDOUT}<"
}
# a pattern (which may come from a `.nvmrc`) is literal text, not a sed regex:
# each of these matched installed versions, failed to compile, or made sed slow
assert_no_match() {
try_err nvm_ls "$1"
[ -z "${CAPTURED_STDERR}" ] || die "\`nvm_ls '$1'\` should not print to stderr; got >${CAPTURED_STDERR}<"
try nvm_ls "$1"
[ "_${CAPTURED_STDOUT}" = '_N/A' ] || die "\`nvm_ls '$1'\` should output N/A; got >${CAPTURED_STDOUT}<"
[ "_${CAPTURED_EXIT_CODE}" = '_3' ] || die "\`nvm_ls '$1'\` should exit with code 3; got ${CAPTURED_EXIT_CODE}"
}
assert_ls 24 'v24.13.0'
assert_ls v22 'v22.1.0'
assert_ls 24.13 'v24.13.0'
assert_ls v20.0.0 'v20.0.0'
for PATTERN in \
'v2[0-9]' \
'v2*4' \
'\(v24\)' \
'v24\|x' \
'\(v\{1,9\}\)\{1,9\}' \
'[' \
'\'; do
assert_no_match "${PATTERN}"
done
cleanup