[Fix] nvm_write_nvmrc: don't write through a symlinked .nvmrc

`nvm use --save` and `nvm install --save` wrote the version to `$PWD/.nvmrc` with `tee`,
which follows a symlink,
so a repository that commits `.nvmrc` as a symlink to a path outside itself made `--save` truncate any file the user can write
(eg, a shell startup file or `~/.ssh/authorized_keys`)
and replace it with the version string,
or create the target of a dangling link.
The link itself was left in place, so `git status` stayed clean.

`nvm_write_nvmrc` now refuses, with a warning, when `.nvmrc` in the current directory is a symlink,
dangling or not.
This commit is contained in:
Jordan Harband committed 2026-11-02 11:27:17 -06:00
1 parent 03f74a8666
commit 3f8411e58d
2 files changed
+55

No files matched your search

+7
View File
@@ -4376,6 +4376,13 @@ nvm_write_nvmrc() {
if [ "${VERSION_STRING}" = '∞' ] || [ "${VERSION_STRING}" = 'N/A' ]; then
return 1
fi
# writing through a symlink would overwrite its target, which can be any file
if [ -L "$PWD"/.nvmrc ]; then
if [ "${NVM_SILENT:-0}" -ne 1 ]; then
nvm_err "Warning: Not writing version number ($VERSION_STRING) to .nvmrc, because it is a symbolic link"
fi
return 3
fi
echo "${VERSION_STRING}" | tee "$PWD"/.nvmrc > /dev/null || {
if [ "${NVM_SILENT:-0}" -ne 1 ]; then
nvm_err "Warning: Unable to write version number ($VERSION_STRING) to .nvmrc"
@@ -0,0 +1,48 @@
#!/bin/sh
die () { echo "$@" ; cleanup ; exit 1; }
cleanup() {
cd "${ORIG_PWD}" || :
nvm deactivate > /dev/null 2>&1
rm -rf "${TEST_DIR-}" "${NVM_DIR}/${TEST_VERSION}"
unset TEST_DIR TEST_VERSION ORIG_PWD NVM_SILENT
}
: nvm.sh
\. ../../../nvm.sh
\. ../../common.sh
ORIG_PWD="${PWD}"
TEST_VERSION='v0.2.4'
make_fake_node "${TEST_VERSION}" || die 'failed to create a fake node'
TEST_DIR="${PWD}/nvm_write_nvmrc_symlink_tmp"
mkdir -p "${TEST_DIR}/project" || die 'failed to create test dirs'
echo 'KEEPME' > "${TEST_DIR}/target"
ln -s ../target "${TEST_DIR}/project/.nvmrc" || die 'failed to create the symlink'
cd "${TEST_DIR}/project" || die 'failed to enter the project dir'
try_err nvm_write_nvmrc "${TEST_VERSION}"
[ "${CAPTURED_EXIT_CODE}" = 3 ] || die "expected exit code 3 for a symlinked .nvmrc; got ${CAPTURED_EXIT_CODE}"
[ "${CAPTURED_STDERR}" = "Warning: Not writing version number (${TEST_VERSION}) to .nvmrc, because it is a symbolic link" ] || die "expected a warning about the symlink; got \`${CAPTURED_STDERR}\`"
[ "$(cat ../target)" = 'KEEPME' ] || die "expected the symlink's target to be unchanged; got \`$(cat ../target)\`"
[ -L .nvmrc ] || die 'expected .nvmrc to still be a symlink'
nvm use --save "${TEST_VERSION}" > /dev/null 2>&1
[ "$(cat ../target)" = 'KEEPME' ] || die "expected \`nvm use --save\` to leave the symlink's target unchanged; got \`$(cat ../target)\`"
rm .nvmrc
ln -s ../dangling .nvmrc || die 'failed to create the dangling symlink'
try_err nvm_write_nvmrc "${TEST_VERSION}"
[ "${CAPTURED_EXIT_CODE}" = 3 ] || die "expected exit code 3 for a dangling symlinked .nvmrc; got ${CAPTURED_EXIT_CODE}"
[ ! -e ../dangling ] || die "expected a dangling symlink's target to not be created"
export NVM_SILENT=1
try_err nvm_write_nvmrc "${TEST_VERSION}"
[ -z "${CAPTURED_STDERR}" ] || die "expected NVM_SILENT=1 to silence the warning; got \`${CAPTURED_STDERR}\`"
unset NVM_SILENT
cleanup