From 26066c10f42afdef9085a30f522b4e73cc2a4e59 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 19 Jan 2026 22:58:43 -0800 Subject: [PATCH] [Fix] prevent sed errors when pattern contains `#` When `.nvmrc` or alias files contained comments (lines with `#`), the `#` character could end up in the search pattern passed to sed, causing "unterminated regular expression" errors because `#` is used as the sed address delimiter. This commit fixes the issue in two places: 1. `nvm_alias`: Strip comments from alias file contents before returning them, and trim trailing whitespace 2. `nvm_ls`: Escape `#` characters in SEARCH_PATTERN so they're treated as literal characters in the sed address Fixes #3761 --- nvm.sh | 4 +-- .../Unit tests/nvm_alias handles comments | 33 +++++++++++++++++++ .../Unit tests/nvm_ls handles hash in pattern | 23 +++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100755 test/fast/Unit tests/nvm_alias handles comments create mode 100755 test/fast/Unit tests/nvm_ls handles hash in pattern diff --git a/nvm.sh b/nvm.sh index 01013b8..5db4df3 100755 --- a/nvm.sh +++ b/nvm.sh @@ -1294,7 +1294,7 @@ nvm_alias() { return 2 fi - command awk 'NF' "${NVM_ALIAS_PATH}" + command sed 's/#.*//; s/[[:space:]]*$//' "${NVM_ALIAS_PATH}" | command awk 'NF' } nvm_ls_current() { @@ -1529,7 +1529,7 @@ nvm_ls() { PATTERN='v' SEARCH_PATTERN='.*' else - SEARCH_PATTERN="$(nvm_echo "${PATTERN}" | command sed 's#\.#\\\.#g;')" + SEARCH_PATTERN="$(nvm_echo "${PATTERN}" | command sed 's#\.#\\\.#g; s|#|\\#|g')" fi if [ -n "${NVM_DIRS_TO_SEARCH1}${NVM_DIRS_TO_SEARCH2}${NVM_DIRS_TO_SEARCH3}" ]; then VERSIONS="$(command find "${NVM_DIRS_TO_SEARCH1}"/* "${NVM_DIRS_TO_SEARCH2}"/* "${NVM_DIRS_TO_SEARCH3}"/* -name . -o -type d -prune -o -path "${PATTERN}*" \ diff --git a/test/fast/Unit tests/nvm_alias handles comments b/test/fast/Unit tests/nvm_alias handles comments new file mode 100755 index 0000000..9e4d166 --- /dev/null +++ b/test/fast/Unit tests/nvm_alias handles comments @@ -0,0 +1,33 @@ +#!/bin/sh + +die () { echo "$@" ; cleanup ; exit 1; } + +cleanup () { + rm -rf ../../../alias/test-comment + rm -rf ../../../alias/test-inline-comment + rm -rf ../../../alias/test-comment-first +} + +\. ../../../nvm.sh + +# Test: alias file with comment on separate line +echo "v0.10 +# this is a comment" > ../../../alias/test-comment +OUTPUT="$(nvm_alias test-comment)" +EXPECTED_OUTPUT="v0.10" +[ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_alias test-comment' should ignore comment line; expected '$EXPECTED_OUTPUT', got '$OUTPUT'" + +# Test: alias file with inline comment +echo "v0.11 # inline comment" > ../../../alias/test-inline-comment +OUTPUT="$(nvm_alias test-inline-comment)" +EXPECTED_OUTPUT="v0.11" +[ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_alias test-inline-comment' should strip inline comment; expected '$EXPECTED_OUTPUT', got '$OUTPUT'" + +# Test: alias file with comment as first line +echo "# comment first +v0.12" > ../../../alias/test-comment-first +OUTPUT="$(nvm_alias test-comment-first)" +EXPECTED_OUTPUT="v0.12" +[ "_$OUTPUT" = "_$EXPECTED_OUTPUT" ] || die "'nvm_alias test-comment-first' should skip comment-only first line; expected '$EXPECTED_OUTPUT', got '$OUTPUT'" + +cleanup diff --git a/test/fast/Unit tests/nvm_ls handles hash in pattern b/test/fast/Unit tests/nvm_ls handles hash in pattern new file mode 100755 index 0000000..815169e --- /dev/null +++ b/test/fast/Unit tests/nvm_ls handles hash in pattern @@ -0,0 +1,23 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +\. ../../../nvm.sh + +# Test: nvm_ls with pattern containing # should not cause sed error +# This is a regression test for https://github.com/nvm-sh/nvm/issues/3761 + +# The pattern with # should not cause sed "unterminated regular expression" error +# It's OK if it returns N/A (no match), but it should not produce sed errors +OUTPUT="$(nvm_ls 'foo#bar' 2>&1)" +EXIT_CODE=$? + +# Check that output doesn't contain sed error message +echo "$OUTPUT" | grep -q "unterminated regular expression" && \ + die "nvm_ls with # in pattern caused sed 'unterminated regular expression' error: $OUTPUT" +echo "$OUTPUT" | grep -q "invalid command code" && \ + die "nvm_ls with # in pattern caused sed 'invalid command code' error: $OUTPUT" + +# Should return N/A with exit code 3 (not found) +[ "$EXIT_CODE" = "3" ] || die "nvm_ls 'foo#bar' should exit with code 3, got $EXIT_CODE" +echo "$OUTPUT" | grep -q "N/A" || die "nvm_ls 'foo#bar' should output N/A, got: $OUTPUT"