diff --git a/nvm.sh b/nvm.sh index fdc2b906ec..180828df24 100755 --- a/nvm.sh +++ b/nvm.sh @@ -159,7 +159,7 @@ nvm_download() { nvm_sanitize_auth_header() { # Remove potentially dangerous characters - nvm_echo "$1" | command sed 's/[^a-zA-Z0-9:;_. -]//g' + nvm_echo "$1" | command sed 's/[^a-zA-Z0-9:;_./+=~ -]//g' } nvm_has_system_node() { diff --git a/test/fast/Unit tests/nvm_download b/test/fast/Unit tests/nvm_download index 727fe13fab..4424d8b397 100755 --- a/test/fast/Unit tests/nvm_download +++ b/test/fast/Unit tests/nvm_download @@ -20,7 +20,12 @@ nvm_download "https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh" >/de # nvm_download should pass when calling with auth header docker pull kennethreitz/httpbin && SHELL=bash docker run -d --name httpbin -p 80:80 kennethreitz/httpbin sleep 1 # wait for httpbin to start -NVM_AUTH_HEADER="Bearer test-token" nvm_download "http://127.0.0.1/bearer" > /dev/null || die 'nvm_download with auth header should send correctly' +# NOTE: The test token here is derived from the RFC at https://datatracker.ietf.org/doc/html/rfc6750#section-2.1 and includes additional +# valid bearer token characters. +expected='{"authenticated":true,"token":"mF_9.B5f-4.1JqM~+/="}' +actual="$(NVM_AUTH_HEADER="Bearer mF_9.B5f-4.1JqM~+/=" nvm_download "http://127.0.0.1/bearer" | tr -d "[:space:]")" +[ "$?" = "0" ] || die 'nvm_download with auth header should send correctly' +[ "${expected}" = "${actual}" ] || die 'nvm_download with auth header should send correct bearer token' # nvm_download should fail when calling without auth header nvm_download "http://127.0.0.1/bearer" > /dev/null && die 'nvm_download with no auth header should not send the header and should fail' diff --git a/test/fast/Unit tests/nvm_download_with_basic_auth b/test/fast/Unit tests/nvm_download_with_basic_auth new file mode 100755 index 0000000000..fa261e6477 --- /dev/null +++ b/test/fast/Unit tests/nvm_download_with_basic_auth @@ -0,0 +1,28 @@ +#!/bin/sh + +cleanup () { + unset -f die cleanup + docker stop httpbin && docker rm httpbin +} +die () { echo "$@" ; cleanup ; exit 1; } + +\. ../../../nvm.sh + +set -ex + +# nvm_download install.sh +nvm_download "https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh" >/dev/null || die "nvm_download unable to download install.sh" + +# nvm_download should pass when calling with basic auth header with correct username and password +docker pull kennethreitz/httpbin && SHELL=bash docker run -d --name httpbin -p 80:80 kennethreitz/httpbin +sleep 1 # wait for httpbin to start +proxy="http://127.0.0.1/basic-auth/test/123%3F45%3E6" +# These credentials were generated by running `printf 'test:123?45>6' | base64`. These credentials were chosen because their +# base64 encoded value contain the necessary special characters '+', '/', and '='. +base64_encoded_credentials="dGVzdDoxMjM/NDU+Ng==" +NVM_AUTH_HEADER="Basic ${base64_encoded_credentials}" nvm_download "${proxy}" > /dev/null || die 'nvm_download should pass when calling with basic auth header with correct username and password' + +# nvm_download should fail when calling with basic auth header with incorrect password +NVM_AUTH_HEADER="Basic $(printf 'test:123?45>67' | base64)" nvm_download "${proxy}" > /dev/null && die 'nvm_download should fail when calling with basic auth header with incorrect password' + +cleanup diff --git a/test/fast/Unit tests/nvm_ls_remote_with_proxy_requiring_basic_auth b/test/fast/Unit tests/nvm_ls_remote_with_proxy_requiring_basic_auth new file mode 100755 index 0000000000..b85a9f62d7 --- /dev/null +++ b/test/fast/Unit tests/nvm_ls_remote_with_proxy_requiring_basic_auth @@ -0,0 +1,85 @@ +#!/bin/sh +# shellcheck shell=dash +# shellcheck disable=SC2039 # local is non-POSIX + +temp_nodejs_tmp_root="" +temp_iojs_tmp_root="" +cleanup () { + unset -f die cleanup NVM_NODEJS_ORG_MIRROR + docker stop ferron-nodejs-mirror && docker rm ferron-nodejs-mirror + docker stop ferron-iojs-mirror && docker rm ferron-iojs-mirror + if [ -n "${temp_nodejs_tmp_root}" ]; then + rm -rf -- "${temp_nodejs_tmp_root}" + fi + if [ -n "${temp_iojs_tmp_root}" ]; then + rm -rf -- "${temp_iojs_tmp_root}" + fi +} +die () { echo "$@" ; cleanup ; exit 1; } + +set -e + +# shellcheck source=/dev/null # The nvm.sh script is checked separately. +\. ../../../nvm.sh + +# Setup the temporary directories to host the mock nodejs and iojs mirrors. +MOCKS_DIR="$PWD/mocks" +temp_nodejs_tmp_root="$(mktemp --directory)" +mkdir -p "${temp_nodejs_tmp_root}/dist" +cp "$MOCKS_DIR/nodejs.org-dist-index.tab" "${temp_nodejs_tmp_root}/dist/index.tab" +temp_iojs_tmp_root="$(mktemp --directory)" +mkdir -p "${temp_iojs_tmp_root}/dist" +cp "$MOCKS_DIR/iojs.org-dist-index.tab" "${temp_iojs_tmp_root}/dist/index.tab" + +# Add necessary ferron config file needed for both containers. +# NOTE: The valid credentials for these test servers are 'test:123?45>6'. +cat <"${temp_nodejs_tmp_root}/ferron.kdl" +:8080 { + log "/dev/stderr" + error_log "/dev/stderr" + status 401 users="test" brute_protection=#false + user "test" "\$argon2id\$v=19\$m=19456,t=2,p=1\$emTillHaS3OqFuvITdXxzg\$G00heP8QSXk5H/ruTiLt302Xk3uETfU5QO8hBIwUq08" + root "/mnt/www" +} +EOF +cat <"${temp_iojs_tmp_root}/ferron.kdl" +:8081 { + log "/dev/stderr" + error_log "/dev/stderr" + status 401 users="test" brute_protection=#false + user "test" "\$argon2id\$v=19\$m=19456,t=2,p=1\$emTillHaS3OqFuvITdXxzg\$G00heP8QSXk5H/ruTiLt302Xk3uETfU5QO8hBIwUq08" + root "/mnt/www" +} +EOF +# Need to adjust permissions to ensure ferron running under container can read the files in the temp directories. +chmod a+rx "${temp_nodejs_tmp_root}" +chmod a+rx "${temp_iojs_tmp_root}" + +# Start containers +docker run --detach --name ferron-nodejs-mirror --publish 8080:8080 --volume "${temp_nodejs_tmp_root}:/mnt/www" --workdir "/mnt/www" ferronserver/ferron:2 ferron --config /mnt/www/ferron.kdl +docker run --detach --name ferron-iojs-mirror --publish 8081:8081 --volume "${temp_iojs_tmp_root}:/mnt/www" --workdir "/mnt/www" ferronserver/ferron:2 ferron --config /mnt/www/ferron.kdl +sleep 1 # wait for ferron containers to start + +export NVM_NODEJS_ORG_MIRROR="http://localhost:8080/dist" +export NVM_IOJS_ORG_MIRROR="http://localhost:8081/dist" +# These credentials were generated by running `printf 'test:123?45>6' | base64`. These credentials were chosen because their +# base64 encoded value contain the necessary special characters '+', '/', and '='. +base64_encoded_credentials="dGVzdDoxMjM/NDU+Ng==" +export NVM_AUTH_HEADER="Basic ${base64_encoded_credentials}" + +OUTPUT="$(nvm_ls_remote)" +echo "nvm_ls_remote output: ${OUTPUT}" +[ -n "${OUTPUT}" ] || die nvm_ls_remote_with_proxy_requiring_basic_auth Failed to run nvm_ls_remote through nodejs mirror requiring basic auth. + +OUTPUT="$(nvm_ls_remote_iojs)" +echo "nvm_ls_remote_iojs output: ${OUTPUT}" +[ -n "${OUTPUT}" ] || die nvm_ls_remote_with_proxy_requiring_basic_auth Failed to run nvm_ls_remote_iojs through iojs mirror requiring basic auth. + +# Remove auth header and try using mirrors. This time it should fail due to missing creds. +unset NVM_AUTH_HEADER + +nvm_ls_remote && die nvm_ls_remote_with_proxy_requiring_basic_auth Unexpected success of run of nvm_ls_remote through nodejs mirror without required credentials. + +nvm_ls_remote_iojs && die nvm_ls_remote_with_proxy_requiring_basic_auth Unexpected success of run of nvm_ls_remote_iojs through iojs mirror without required credentials. + +cleanup