mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-10-23 23:03:46 +08:00
[Fix] nvm use: handle regex-special characters in $NVM_DIR
nvm_change_path interpolated `$NVM_DIR` and the new version dir unescaped into grep and sed expressions, so a path containing `&`, `#`, `[`, `*`, etc. corrupted `$PATH` (or made sed fail) when switching versions. Escape them before use.
This commit is contained in:
committed by
Jordan Harband
parent
e45c2df7c0
commit
906fb605bc
44
nvm.sh
44
nvm.sh
@@ -1254,24 +1254,33 @@ nvm_change_path() {
|
|||||||
# if there’s no initial path, just return the supplementary path
|
# if there’s no initial path, just return the supplementary path
|
||||||
if [ -z "${1-}" ]; then
|
if [ -z "${1-}" ]; then
|
||||||
nvm_echo "${3-}${2-}"
|
nvm_echo "${3-}${2-}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
# `${NVM_DIR}` is matched literally, so escape characters that are special in regexes
|
||||||
|
local NVM_DIR_RE
|
||||||
|
NVM_DIR_RE="$(nvm_echo "${NVM_DIR}" | command sed 's/[][\.*^$+?(){}|]/\\&/g')"
|
||||||
# if the initial path doesn’t contain an nvm path, prepend the supplementary
|
# if the initial path doesn’t contain an nvm path, prepend the supplementary
|
||||||
# path
|
# path
|
||||||
elif ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/[^/]*${2-}" \
|
if ! nvm_echo "${1-}" | nvm_grep -Eq "${NVM_DIR_RE}/[^/]*${2-}" \
|
||||||
&& ! nvm_echo "${1-}" | nvm_grep -q "${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then
|
&& ! nvm_echo "${1-}" | nvm_grep -Eq "${NVM_DIR_RE}/versions/[^/]*/[^/]*${2-}"; then
|
||||||
nvm_echo "${3-}${2-}:${1-}"
|
nvm_echo "${3-}${2-}:${1-}"
|
||||||
# if the initial path contains BOTH an nvm path (checked for above) and
|
# if the initial path contains BOTH an nvm path (checked for above) and
|
||||||
# that nvm path is preceded by a system binary path, just prepend the
|
# that nvm path is preceded by a system binary path, just prepend the
|
||||||
# supplementary path instead of replacing it.
|
# supplementary path instead of replacing it.
|
||||||
# https://github.com/nvm-sh/nvm/issues/1652#issuecomment-342571223
|
# https://github.com/nvm-sh/nvm/issues/1652#issuecomment-342571223
|
||||||
elif nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR}/[^/]*${2-}" \
|
elif nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR_RE}/[^/]*${2-}" \
|
||||||
|| nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR}/versions/[^/]*/[^/]*${2-}"; then
|
|| nvm_echo "${1-}" | nvm_grep -Eq "(^|:)(/usr(/local)?)?${2-}:.*${NVM_DIR_RE}/versions/[^/]*/[^/]*${2-}"; then
|
||||||
nvm_echo "${3-}${2-}:${1-}"
|
nvm_echo "${3-}${2-}:${1-}"
|
||||||
# use sed to replace the existing nvm path with the supplementary path. This
|
# use sed to replace the existing nvm path with the supplementary path. This
|
||||||
# preserves the order of the path.
|
# preserves the order of the path.
|
||||||
else
|
else
|
||||||
nvm_echo "${1-}" | command sed \
|
local NVM_DIR_SED
|
||||||
-e "s#${NVM_DIR}/[^/]*${2-}[^:]*#${3-}${2-}#" \
|
NVM_DIR_SED="$(nvm_echo "${NVM_DIR_RE}" | command sed 's/#/\\#/g')"
|
||||||
-e "s#${NVM_DIR}/versions/[^/]*/[^/]*${2-}[^:]*#${3-}${2-}#"
|
local NEW_DIR_SED
|
||||||
|
NEW_DIR_SED="$(nvm_echo "${3-}" | command sed 's/[\&#]/\\&/g')"
|
||||||
|
nvm_echo "${1-}" | command sed -E \
|
||||||
|
-e "s#${NVM_DIR_SED}/[^/]*${2-}[^:]*#${NEW_DIR_SED}${2-}#" \
|
||||||
|
-e "s#${NVM_DIR_SED}/versions/[^/]*/[^/]*${2-}[^:]*#${NEW_DIR_SED}${2-}#"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3638,14 +3647,29 @@ nvm_has_solaris_binary() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# replaces every literal occurrence of $2 in $1 with $3; values go through the
|
||||||
|
# environment so awk does not interpret backslash escapes or regex characters
|
||||||
|
nvm_replace_literal() {
|
||||||
|
nvm_echo "${1-}" | NVM_FROM="${2-}" NVM_TO="${3-}" command awk '
|
||||||
|
BEGIN { from = ENVIRON["NVM_FROM"]; to = ENVIRON["NVM_TO"]; n = length(from) }
|
||||||
|
{
|
||||||
|
out = ""; s = $0
|
||||||
|
while (n > 0 && (i = index(s, from)) > 0) {
|
||||||
|
out = out substr(s, 1, i - 1) to
|
||||||
|
s = substr(s, i + n)
|
||||||
|
}
|
||||||
|
print out s
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
nvm_sanitize_path() {
|
nvm_sanitize_path() {
|
||||||
local SANITIZED_PATH
|
local SANITIZED_PATH
|
||||||
SANITIZED_PATH="${1-}"
|
SANITIZED_PATH="${1-}"
|
||||||
if [ "_${SANITIZED_PATH}" != "_${NVM_DIR}" ]; then
|
if [ "_${SANITIZED_PATH}" != "_${NVM_DIR}" ]; then
|
||||||
SANITIZED_PATH="$(nvm_echo "${SANITIZED_PATH}" | command sed -e "s#${NVM_DIR}#\${NVM_DIR}#g")"
|
SANITIZED_PATH="$(nvm_replace_literal "${SANITIZED_PATH}" "${NVM_DIR}" '${NVM_DIR}')"
|
||||||
fi
|
fi
|
||||||
if [ "_${SANITIZED_PATH}" != "_${HOME}" ]; then
|
if [ "_${SANITIZED_PATH}" != "_${HOME}" ]; then
|
||||||
SANITIZED_PATH="$(nvm_echo "${SANITIZED_PATH}" | command sed -e "s#${HOME}#\${HOME}#g")"
|
SANITIZED_PATH="$(nvm_replace_literal "${SANITIZED_PATH}" "${HOME}" '${HOME}')"
|
||||||
fi
|
fi
|
||||||
nvm_echo "${SANITIZED_PATH}"
|
nvm_echo "${SANITIZED_PATH}"
|
||||||
}
|
}
|
||||||
@@ -5426,7 +5450,7 @@ nvm() {
|
|||||||
nvm_install_lock_name nvm_acquire_install_lock nvm_release_install_lock \
|
nvm_install_lock_name nvm_acquire_install_lock nvm_release_install_lock \
|
||||||
nvm_list_aliases nvm_make_alias nvm_print_alias_file nvm_print_alias_path \
|
nvm_list_aliases nvm_make_alias nvm_print_alias_file nvm_print_alias_path \
|
||||||
nvm_print_default_alias nvm_print_formatted_alias nvm_resolve_local_alias \
|
nvm_print_default_alias nvm_print_formatted_alias nvm_resolve_local_alias \
|
||||||
nvm_sanitize_path nvm_has_colors nvm_has_italics nvm_process_parameters \
|
nvm_replace_literal nvm_sanitize_path nvm_has_colors nvm_has_italics nvm_process_parameters \
|
||||||
nvm_node_version_has_solaris_binary nvm_iojs_version_has_solaris_binary \
|
nvm_node_version_has_solaris_binary nvm_iojs_version_has_solaris_binary \
|
||||||
nvm_curl_libz_support nvm_command_info nvm_is_zsh nvm_stdout_is_terminal \
|
nvm_curl_libz_support nvm_command_info nvm_is_zsh nvm_stdout_is_terminal \
|
||||||
nvm_npmrc_bad_news_bears nvm_sanitize_auth_header \
|
nvm_npmrc_bad_news_bears nvm_sanitize_auth_header \
|
||||||
|
|||||||
37
test/fast/Unit tests/nvm_change_path special characters
Executable file
37
test/fast/Unit tests/nvm_change_path special characters
Executable file
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die () { echo "$@" ; exit 1; }
|
||||||
|
|
||||||
|
: nvm.sh
|
||||||
|
\. ../../../nvm.sh
|
||||||
|
|
||||||
|
for DIR in '/tmp/a&b/.nvm' '/tmp/a#b/.nvm' '/tmp/a[1]/.nvm' '/tmp/a*b/.nvm' '/tmp/a+(b)/.nvm' '/tmp/a.b/.nvm' '/tmp/a\b/.nvm' '/tmp/a$b/.nvm' '/tmp/a|b/.nvm'; do
|
||||||
|
NVM_DIR="${DIR}"
|
||||||
|
TEST_PATH="/opt/bin:${NVM_DIR}/versions/node/v4.5.0/bin:/usr/bin"
|
||||||
|
|
||||||
|
# replace branch
|
||||||
|
NEW_PATH="$(nvm_change_path "${TEST_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||||
|
EXPECTED="/opt/bin:${NVM_DIR}/versions/node/v7.1.0/bin:/usr/bin"
|
||||||
|
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR}: expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||||
|
|
||||||
|
# system path precedes the nvm path: prepend instead of replacing
|
||||||
|
SYSTEM_FIRST_PATH="/usr/bin:${NVM_DIR}/versions/node/v4.5.0/bin"
|
||||||
|
NEW_PATH="$(nvm_change_path "${SYSTEM_FIRST_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||||
|
EXPECTED="${NVM_DIR}/versions/node/v7.1.0/bin:${SYSTEM_FIRST_PATH}"
|
||||||
|
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR} (system path first): expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||||
|
|
||||||
|
# MANPATH call
|
||||||
|
TEST_MANPATH="/opt/share/man:${NVM_DIR}/versions/node/v4.5.0/share/man:/usr/share/man"
|
||||||
|
NEW_PATH="$(nvm_change_path "${TEST_MANPATH}" "/share/man" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||||
|
EXPECTED="/opt/share/man:${NVM_DIR}/versions/node/v7.1.0/share/man:/usr/share/man"
|
||||||
|
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR} (MANPATH): expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||||
|
done
|
||||||
|
|
||||||
|
# `.` in NVM_DIR must be escaped so it matches itself only, not "any character":
|
||||||
|
# an unrelated path entry that merely looks similar (some other char where the
|
||||||
|
# `.` is) must be left alone, not mistaken for an nvm path and rewritten.
|
||||||
|
NVM_DIR="/tmp/a.b/.nvm"
|
||||||
|
UNRELATED_PATH="/opt/bin:/tmp/aXb/.nvm/versions/node/v4.5.0/bin:/usr/bin"
|
||||||
|
NEW_PATH="$(nvm_change_path "${UNRELATED_PATH}" "/bin" "${NVM_DIR}/versions/node/v7.1.0")"
|
||||||
|
EXPECTED="${NVM_DIR}/versions/node/v7.1.0/bin:${UNRELATED_PATH}"
|
||||||
|
[ "${NEW_PATH}" = "${EXPECTED}" ] || die "unrelated PATH entry /tmp/aXb/.nvm/... should not be touched by NVM_DIR=${NVM_DIR}: expected >${EXPECTED}<, got >${NEW_PATH}<"
|
||||||
29
test/fast/Unit tests/nvm_sanitize_path special characters
Executable file
29
test/fast/Unit tests/nvm_sanitize_path special characters
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
die () { echo "$@" ; exit 1; }
|
||||||
|
|
||||||
|
: nvm.sh
|
||||||
|
\. ../../../nvm.sh
|
||||||
|
|
||||||
|
HOME='/home/user'
|
||||||
|
|
||||||
|
for DIR in '/tmp/a&b/.nvm' '/tmp/a#b/.nvm' '/tmp/a[1]/.nvm' '/tmp/a*b/.nvm' '/tmp/a+(b)/.nvm' '/tmp/a.b/.nvm' '/tmp/a\b/.nvm' '/tmp/a$b/.nvm' '/tmp/a|b/.nvm'; do
|
||||||
|
NVM_DIR="${DIR}"
|
||||||
|
|
||||||
|
OUTPUT="$(nvm_sanitize_path "${NVM_DIR}/versions/node/v4.5.0")"
|
||||||
|
EXPECTED='${NVM_DIR}/versions/node/v4.5.0'
|
||||||
|
[ "${OUTPUT}" = "${EXPECTED}" ] || die "NVM_DIR=${DIR}: expected >${EXPECTED}<, got >${OUTPUT}<"
|
||||||
|
|
||||||
|
OUTPUT="$(nvm_sanitize_path "${NVM_DIR}")"
|
||||||
|
[ "${OUTPUT}" = "${NVM_DIR}" ] || die "NVM_DIR=${DIR}: path equal to NVM_DIR should be left alone, got >${OUTPUT}<"
|
||||||
|
done
|
||||||
|
|
||||||
|
# `.` in NVM_DIR must match itself only, not any character
|
||||||
|
NVM_DIR='/tmp/a.b/.nvm'
|
||||||
|
OUTPUT="$(nvm_sanitize_path '/tmp/aXb/.nvm/foo')"
|
||||||
|
[ "${OUTPUT}" = '/tmp/aXb/.nvm/foo' ] || die "unrelated path was rewritten: >${OUTPUT}<"
|
||||||
|
|
||||||
|
# HOME is replaced too
|
||||||
|
NVM_DIR='/tmp/a#b/.nvm'
|
||||||
|
OUTPUT="$(nvm_sanitize_path "${HOME}/.npmrc")"
|
||||||
|
[ "${OUTPUT}" = '${HOME}/.npmrc' ] || die "expected >\${HOME}/.npmrc<, got >${OUTPUT}<"
|
||||||
Reference in New Issue
Block a user