mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-06-07 23:02:14 +08:00
[Fix] nvm_download_artifact: reject version strings with disallowed characters
The mirror-supplied (untrusted) version flows into download URLs, filesystem paths, and the checksum awk match. Reject any version outside the node/io.js grammar (`[0-9A-Za-z._+-]`) before it is used. A blocklist of metacharacters is used rather than a strict semver allowlist so RCs, nightlies, v8-canary, and io.js versions still install. Completes the remediation of GHSA-3c52-35h2-gfmm.
This commit is contained in:
14
nvm.sh
14
nvm.sh
@@ -2506,10 +2506,16 @@ nvm_download_artifact() {
|
|||||||
local VERSION
|
local VERSION
|
||||||
VERSION="${4}"
|
VERSION="${4}"
|
||||||
|
|
||||||
if [ -z "${VERSION}" ]; then
|
case "${VERSION}" in
|
||||||
nvm_err 'A version number is required.'
|
'')
|
||||||
return 3
|
nvm_err 'A version number is required.'
|
||||||
fi
|
return 3
|
||||||
|
;;
|
||||||
|
*[!0-9A-Za-z._+-]*)
|
||||||
|
nvm_err 'Invalid version: contains disallowed characters'
|
||||||
|
return 3
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
if [ "${KIND}" = 'binary' ] && ! nvm_binary_available "${VERSION}"; then
|
if [ "${KIND}" = 'binary' ] && ! nvm_binary_available "${VERSION}"; then
|
||||||
nvm_err "No precompiled binary available for ${VERSION}."
|
nvm_err "No precompiled binary available for ${VERSION}."
|
||||||
|
|||||||
46
test/fast/Unit tests/nvm_download_artifact version injection
Executable file
46
test/fast/Unit tests/nvm_download_artifact version injection
Executable file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
WORK="${TMPDIR:-/tmp}/nvm_version_injection.$$"
|
||||||
|
PROOF="${WORK}/PWNED"
|
||||||
|
|
||||||
|
cleanup () {
|
||||||
|
unset -f die cleanup nvm_download
|
||||||
|
rm -rf "${WORK}"
|
||||||
|
}
|
||||||
|
die () { echo "$@" ; cleanup ; exit 1; }
|
||||||
|
|
||||||
|
\. ../../../nvm.sh
|
||||||
|
|
||||||
|
mkdir -p "${WORK}"
|
||||||
|
export NVM_DIR="${WORK}"
|
||||||
|
|
||||||
|
# GHSA-3c52-35h2-gfmm: a mirror-supplied version with shell/awk metacharacters
|
||||||
|
# must be rejected before it is used in URLs, paths, or awk. Neutralize network.
|
||||||
|
nvm_download () { return 0; }
|
||||||
|
|
||||||
|
# given a version containing command-substitution syntax
|
||||||
|
# when nvm_download_artifact is asked to download it
|
||||||
|
# then it is rejected for disallowed characters and nothing is executed
|
||||||
|
rm -f "${PROOF}"
|
||||||
|
out="$(nvm_download_artifact node source std 'v1$(touch '"${PROOF}"')' 2>&1 </dev/null)"
|
||||||
|
case "${out}" in
|
||||||
|
*'disallowed characters'*) : ;;
|
||||||
|
*) die "command-substitution version was not rejected; got: ${out}" ;;
|
||||||
|
esac
|
||||||
|
[ ! -e "${PROOF}" ] || die 'command-substitution payload executed via nvm_download_artifact'
|
||||||
|
|
||||||
|
# and an awk-breakout version is likewise rejected
|
||||||
|
out="$(nvm_download_artifact node source std 'v1"==$2){system("x")}#' 2>&1 </dev/null)"
|
||||||
|
case "${out}" in
|
||||||
|
*'disallowed characters'*) : ;;
|
||||||
|
*) die "awk-style version was not rejected; got: ${out}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# and a legitimate nightly version must NOT be rejected by the character guard
|
||||||
|
out="$(nvm_download_artifact node source std 'v22.0.0-nightly20240101abcdef' 2>&1 </dev/null)"
|
||||||
|
case "${out}" in
|
||||||
|
*'disallowed characters'*) die 'a legitimate nightly version was wrongly rejected by the guard' ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cleanup
|
||||||
|
echo 'nvm_download_artifact version injection: passed'
|
||||||
Reference in New Issue
Block a user