#!/bin/sh ORIG_NVM_DIR="${NVM_DIR}" TMP_DIR='' cleanup () { case "${TMP_DIR}" in /*) rm -rf "${TMP_DIR}" ;; esac NVM_DIR="${ORIG_NVM_DIR}" export NVM_DIR } die () { echo "$@" ; cleanup ; exit 1; } : nvm.sh \. ../../../nvm.sh TMP_DIR="$(mktemp -d)" NVM_DIR="${TMP_DIR}/nvm" export NVM_DIR mkdir -p "${NVM_DIR}/alias/lts" || die 'could not create the scratch alias dir' # a file outside $NVM_DIR that alias resolution must never reach printf '%s\n' 'SECRET_OUTSIDE_NVM_DIR' > "${TMP_DIR}/outside" # legitimate aliases, to prove the guard does not reject the names nvm uses printf '%s\n' 'v1.2.3' > "${NVM_DIR}/alias/default" printf '%s\n' 'v4.5.6' > "${NVM_DIR}/alias/lts/iron" for TRAVERSAL in '../../outside' 'lts/../../../outside' '..' 'lts/..' 'a/../../outside'; do OUTPUT="$(nvm_alias "${TRAVERSAL}" 2>/dev/null)" EXIT_CODE=$? [ "_${EXIT_CODE}" = '_3' ] || die "'nvm_alias ${TRAVERSAL}' exited with ${EXIT_CODE}, expected 3" [ -z "${OUTPUT}" ] || die "'nvm_alias ${TRAVERSAL}' produced output: ${OUTPUT}" # capture stderr through a file, and match rather than compare: ksh 93u+ has # no `local`, so it interleaves its own noise with anything nvm.sh writes nvm_alias "${TRAVERSAL}" >/dev/null 2>"${TMP_DIR}/stderr" grep -qF "invalid alias name: ${TRAVERSAL}" "${TMP_DIR}/stderr" \ || die "'nvm_alias ${TRAVERSAL}' produced stderr: $(cat "${TMP_DIR}/stderr")" done # the disclosure path the advisory reports: resolution must not echo the file for TRAVERSAL in '../../outside' 'lts/../../../outside'; do OUTPUT="$(nvm_resolve_alias "${TRAVERSAL}" 2>&1)" case "${OUTPUT}" in *SECRET_OUTSIDE_NVM_DIR*) die "'nvm_resolve_alias ${TRAVERSAL}' leaked a file outside \$NVM_DIR" ;; esac done OUTPUT="$(nvm_alias default)" [ "_${OUTPUT}" = '_v1.2.3' ] || die "'nvm_alias default' produced \"${OUTPUT}\", expected v1.2.3" OUTPUT="$(nvm_alias lts/iron)" [ "_${OUTPUT}" = '_v4.5.6' ] || die "'nvm_alias lts/iron' produced \"${OUTPUT}\", expected v4.5.6" for BAD_VERSION in '../../outside' 'v1.2.3/../../..' '..'; do OUTPUT="$(nvm_version_path "${BAD_VERSION}" 2>/dev/null)" EXIT_CODE=$? [ "_${EXIT_CODE}" = '_3' ] || die "'nvm_version_path ${BAD_VERSION}' exited with ${EXIT_CODE}, expected 3" [ -z "${OUTPUT}" ] || die "'nvm_version_path ${BAD_VERSION}' produced output: ${OUTPUT}" done OUTPUT="$(nvm_version_path v18.0.0)" [ "_${OUTPUT}" = "_${NVM_DIR}/versions/node/v18.0.0" ] || die "'nvm_version_path v18.0.0' produced \"${OUTPUT}\"" cleanup