From dd0f702fb1d51866f5592739fe722afa60240f42 Mon Sep 17 00:00:00 2001 From: Andres Mejia Sanchez Date: Wed, 2 Sep 2026 19:35:06 -0700 Subject: [PATCH] [Fix] `nvm_sanitize_auth_header`: allow `~`, completing RFC 7235 `token68` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `~` is a member of RFC 7235 §2.1 `token68`, and thus of RFC 6750 §2.1 `b64token`, but the allowlist stripped it, silently corrupting any opaque Bearer credential containing it: Bearer mF_9.B5f-4.1JqM~+/= -> Bearer mF_9.B5f-4.1JqM+/= `Basic` credentials were never affected, since RFC 4648 §4 base64 cannot emit `~`; `;` stays stripped, as it belongs to no auth-scheme production. Cover the charset in the unit test, and assert the credential reaches the downloader intact via the existing fake-`wget` harness, which needs no container. --- nvm.sh | 5 +++-- .../nvm_download wget Authorization header | 6 ++++++ test/fast/Unit tests/nvm_sanitize_auth_header | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/nvm.sh b/nvm.sh index 42a63ee8..0c48cb94 100755 --- a/nvm.sh +++ b/nvm.sh @@ -190,9 +190,10 @@ nvm_sanitize_auth_header() { # and base64url (A-Za-z0-9-_=) charsets, plus the space, colon, dot, and # underscore the previous allowlist already permitted, so that values like # `Basic ` and `Bearer ` survive intact. - # token68's '~' is still stripped. + # '~' is also allowed, completing RFC 7235 `token68` (and thus RFC 6750 + # `b64token`), so opaque Bearer tokens containing it are not corrupted. # Note: '-' must be at the end of the bracket expression to be treated as a literal. - 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 wget Authorization header b/test/fast/Unit tests/nvm_download wget Authorization header index f0900c0a..931da4e9 100755 --- a/test/fast/Unit tests/nvm_download wget Authorization header +++ b/test/fast/Unit tests/nvm_download wget Authorization header @@ -39,5 +39,11 @@ NVM_AUTH_HEADER='Bearer test-token' nvm_download "https://nodejs.org/dist/x" -o grep -Fxqe '--header' "$ARGV_LOG" || die "wget did not receive --header; got: $(cat "$ARGV_LOG")" grep -Fxq 'Authorization: Bearer test-token' "$ARGV_LOG" || die "wget did not receive a well-formed Authorization header; got: $(cat "$ARGV_LOG")" +# given a credential using every character RFC 7235 token68 permits, including '~' +# when nvm_download uses the wget path +NVM_AUTH_HEADER='Bearer mF_9.B5f-4.1JqM~+/=' nvm_download "https://nodejs.org/dist/x" -o - || die 'nvm_download (wget) returned nonzero for a token68 credential' +# then it reaches wget byte-for-byte, with no character silently dropped +grep -Fxq 'Authorization: Bearer mF_9.B5f-4.1JqM~+/=' "$ARGV_LOG" || die "wget did not receive the token68 credential intact; got: $(cat "$ARGV_LOG")" + cleanup echo "nvm_download wget Authorization header: passed" diff --git a/test/fast/Unit tests/nvm_sanitize_auth_header b/test/fast/Unit tests/nvm_sanitize_auth_header index e10f1b6e..d7ea8192 100755 --- a/test/fast/Unit tests/nvm_sanitize_auth_header +++ b/test/fast/Unit tests/nvm_sanitize_auth_header @@ -35,4 +35,18 @@ case "${RESULT}" in ;; esac +# Test 6: every character of RFC 7235 §2.1 `token68` is preserved. +# token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"=" +# This is a superset of both base64 and base64url; note the '~', which no +# base64 variant emits but which an opaque token may legitimately contain. +TOKEN68="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~+/=" +RESULT=$(nvm_sanitize_auth_header "${TOKEN68}") +[ "${RESULT}" = "${TOKEN68}" ] || die "FAIL: token68 chars were stripped. Got: '${RESULT}'" + +# Test 7: the RFC 6750 §2.1 example Bearer token, extended with the remaining +# valid `b64token` characters, survives intact. +B64TOKEN="Bearer mF_9.B5f-4.1JqM~+/=" +RESULT=$(nvm_sanitize_auth_header "${B64TOKEN}") +[ "${RESULT}" = "${B64TOKEN}" ] || die "FAIL: b64token chars were stripped. Got: '${RESULT}'" + echo "All nvm_sanitize_auth_header tests passed"