1
0
mirror of https://github.com/nvm-sh/nvm.git synced 2026-10-26 00:14:32 +08:00

450 Commits

Author SHA1 Message Date
Jordan Harband
e780535240 [Fix] nvm_download: with GNU Wget, follow only same-origin redirects when sending $NVM_AUTH_HEADER
curl 7.58.0 and later drop a custom Authorization header when a redirect leads to another host,
but wget sends `--header` along to every redirect target,
so on a machine without curl,
a mirror that redirected elsewhere handed the credential to that other host.

When a header is sent with GNU Wget,
run it with `--max-redirect=0`.
When it then exits with 8
(an HTTP error, which includes a redirect it did not follow),
read the `Location` with one more request,
and re-request it only if it keeps the same scheme,
host, and port, up to 20 times.
A redirect to another origin,
including a downgrade to `http`,
fails with exit code 8 and an error that names the URL nvm requested and suggests curl.
`--max-redirect` is not a BusyBox wget option,
so other wgets are invoked as before;
the README says so

Discovered by Anthropic's OSS Scanner, as ANT-2026-PKUZHTT3; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
e3aa232901 [Fix] nvm_download: only send $NVM_AUTH_HEADER to URLs under a mirror it applies to
The header was added to every download,
whatever the host.
With only `$NVM_NODEJS_ORG_MIRROR` set,
a private mirror's credential therefore also went to the default `https://iojs.org/dist`
(eg, from `nvm ls-remote`),
and with only `$NVM_IOJS_ORG_MIRROR` set,
to `https://nodejs.org/dist`;
it also went to `npmjs.org` when installing an old npm.

Add `nvm_auth_header_applies_to`,
which uses `nvm_get_mirror` and accepts a URL only when it is under a mirror,
compared literally,
with a `/` after the mirror's prefix.
When exactly one of the two mirror variables is set,
the header belongs to that mirror alone;
when both or neither are set,
both mirrors
(including the defaults, when neither is)
still receive it,
so the documented example keeps working.
An invalid mirror matches nothing,
and no empty `Authorization:` header is sent when sanitizing leaves nothing

Discovered by Anthropic's OSS Scanner, as ANT-2026-YPW87EDH; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
6bbbf3e8d3 [Tests] nvm_download: point the httpbin Authorization check at a configured mirror
The httpbin check sends `$NVM_AUTH_HEADER` to `http://127.0.0.1/bearer` without configuring a mirror.
Set `$NVM_NODEJS_ORG_MIRROR` to that host,
as a real user of the header would,
so the check keeps exercising the header once it is only sent to URLs under a mirror

Discovered by Anthropic's OSS Scanner, as ANT-2026-YPW87EDH; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
cbd8f8e920 [Fix] nvm_remote_versions: only consult io.js's version list for majors 1 through 3
io.js only released v1 through v3,
but its version list was downloaded and searched for every pattern,
including ones like `0.12` or `20` that can only ever match node versions.
Besides the needless index.tab download on every such `nvm install`,
io.js's "no match" status was returned even when node had matches,
so `nvm ls-remote 20` printed node's versions and then exited 3.
Patterns whose major is 0 or 4+ now only consult node's list,
so `nvm ls-remote` exits 0 for them when node has matching versions.
The listed and resolved versions are unchanged

Discovered by Anthropic's OSS Scanner, as ANT-2026-AKH0HYWD; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
59e008fd62 [Fix] nvm_remote_versions: place io.js versions by major version, not at the literal v4.0.0
io.js versions were inserted into node's version list immediately before the `v4.0.0` line.
Whenever node's filtered list did not contain that exact line,
every io.js version was appended after all node versions,
including newer ones,
so `nvm_remote_version`'s `tail -1` could pick an io.js version over the latest matching node version.
Node's list is sorted,
so it is now split at the first version with a nonzero major,
which keeps io.js versions between node v0.x and node v4+ regardless of which node versions the pattern matched

Discovered by Anthropic's OSS Scanner, as ANT-2026-AKH0HYWD; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
35d6e5a000 [Fix] nvm.sh: detect NVM_DIR without $0/$_; fall back to the default install location instead of the working directory
When `nvm.sh` is sourced with `NVM_DIR` unset,
it derived `NVM_DIR` from the sourced file in bash
(via `BASH_SOURCE`),
and otherwise from `dirname "${_:-$0}"`.
In shells that do not set `BASH_SOURCE`
(dash, sh, ksh, and zsh under `emulate sh` or without `FUNCTION_ARGZERO`),
`$0` is the shell's or the calling script's name and `$_` is whatever was inherited,
so `NVM_DIR` became an unrelated directory:
for `sh -c '. ~/.nvm/nvm.sh'`,
as run by `system(3)` or `popen(3)`,
that is the working directory,
whose `alias/default` and `versions/` nvm then used,
and from which `nvm cache clear` and friends would operate.

Detection now uses only reliable sources:
`BASH_SOURCE` in bash,
`${(%):-%x}` in zsh
(which names the sourced file regardless of options),
and `${.sh.file}` in ksh93
(probed in a subshell first, since other shells with `KSH_VERSION` reject the syntax)
A detected source that is not a regular file
(eg, `/dev/fd/63` from `. <(…)`)
is ignored.
Otherwise, nvm uses the default install location that `install.sh` uses
(`$XDG_CONFIG_HOME/nvm`, else `~/.nvm`)
when it contains `nvm.sh`, and if not,
it prints an error asking for `NVM_DIR` to be set and returns 1,
instead of guessing.
Setups that export `NVM_DIR` before sourcing `nvm.sh`,
as `install.sh` and the README do, are unaffected

Discovered by Anthropic's OSS Scanner, as ANT-2026-8RIT0MGO; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
c07e8722a1 [Tests] nvm_get_arch_unofficial: set NVM_DIR when sourcing nvm.sh in the chroots
The chroot checks source `nvm.sh` from `/bin/sh` without `NVM_DIR`,
so nvm has to guess it:
today from `dirname "$0"`,
which is `/bin` here.
That guess is incidental to what the test checks
(architecture detection),
so set `NVM_DIR` explicitly to the directory the chroot's `nvm.sh` lives in,
as the README instructs

Discovered by Anthropic's OSS Scanner, as ANT-2026-8RIT0MGO; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
8610e1578d [Fix] nvm_process_nvmrc_content: reject control characters
An `.nvmrc` line containing a control character
(eg, an escape, a bell, or a carriage return in the middle of the version)
was accepted as-is,
so the raw bytes were passed along as the version and printed verbatim in the "Found '…' with version <…>" and "N/A: version … is not yet installed" messages,
garbling the terminal output.

Any line that still contains a control character other than a tab,
after comments and leading and trailing white space are removed,
now makes the file invalid.
CRLF line endings keep working,
since the trailing carriage return is trimmed as white space before the check.
The "non-commented content parsed" part of the invalid `.nvmrc` message now shows each control character
(other than tabs and newlines) as `?`,
so it prints as plain text

Discovered by Anthropic's OSS Scanner, as ANT-2026-R3FR49Z7; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
4a1f7d2875 [Fix] nvm_download_artifact, nvm debug: mask credentials in mirror URLs when printing them
`nvm_get_mirror` accepts `user:password@` in `$NVM_NODEJS_ORG_MIRROR` and `$NVM_IOJS_ORG_MIRROR`,
but `nvm_download_artifact` printed the full tarball URL in its "Downloading",
"download from ... failed",
and "HTTP 404 at URL" messages,
and `nvm debug` printed both mirror variables as-is,
so the credentials showed up in terminal output and build logs.

Add `nvm_sanitize_url`,
which replaces the userinfo part of any URL with `***@`,
and use it in those messages.
The download itself still uses the full URL,
so authenticating to the mirror keeps working.
The README now notes that `$NVM_AUTH_HEADER` is the recommended way to authenticate to a mirror

Discovered by Anthropic's OSS Scanner, as ANT-2026-X03SZ7C6; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:59 -07:00
Jordan Harband
37c578d973 [Fix] nvm_install_binary, nvm_install_source: only use a tarball that nvm_download_artifact put in the cache
`nvm_install_binary` and `nvm_install_source` took the tarball path from the last line of `nvm_download_artifact`'s output via `| tail -1`.
That pipeline's exit status is `tail`'s,
so a failed download went unnoticed,
and the downloader's own stdout was not separated from the path:
anything it printed
(eg, from a curl `write-out` setting)
could be the last line.
If that line happened to name an existing file relative to the current directory,
it was extracted and installed as if it were the downloaded tarball,
instead of the install failing.

`nvm_download_artifact` now sends the downloader's output to stderr,
so its stdout is only ever the tarball path.
Both install functions capture its output directly and honor its exit status,
and accept the result only if it is an existing file inside nvm's cache directory,
via a new `nvm_is_cached_artifact` helper

Discovered by Anthropic's OSS Scanner, as ANT-2026-DESJ2ESZ; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:58 -07:00
Jordan Harband
c8a2a81f92 [Fix] nvm_download, nvm_get_latest, install.sh: pass -q to curl first, so .curlrc is ignored again
curl only skips reading `.curlrc` when `-q` is its very first argument;
anywhere else, the config file has already been read by the time `-q` is seen.
a4f89c62 added `-q` to every curl call so that a user's `.curlrc` would not affect nvm,
but 6a3b1dd2 (v0.33.2)
put `--compressed` in front of it everywhere.
bd090ef7 (v0.40.0)
moved it back to the front in `nvm.sh`'s `nvm_download`,
and then 6d870d18 (v0.40.5) began building those arguments with `set --`,
prepending `--compressed` after `-q`,
so whenever curl supports compression, `-q` was second again.
`nvm_get_latest` and `install.sh`'s `nvm_download` have passed
`--compressed` before `-q` since v0.33.2.

Pass `-q` first in all three places.
Settings in a user's `.curlrc`
(such as a proxy, CA bundle, or `write-out`)
no longer apply to nvm's own curl calls,
as was intended,
and as in v0.40.0 through v0.40.4 for `nvm_download`;
the corresponding environment variables
(eg, `https_proxy`, `CURL_CA_BUNDLE`) still apply.

The existing `nvm_get_latest` test pinned the old argument order,
so its expectation is updated

Discovered by Anthropic's OSS Scanner, as ANT-2026-DESJ2ESZ; the scanner reported it as a vulnerability, but it is not one.
2026-10-23 08:53:58 -07:00
Jordan Harband
013b291d36 [meta] release-notes.sh: replace control characters in commit subjects
Commit subjects were printed as-is,
so escape sequences, carriage returns,
and other control characters in a subject reached the terminal during `make release-notes` and `make release`,
where they could hide or rewrite lines of the notes being reviewed,
and were then written verbatim into the bump commit and the release tag.
A tab in a subject also corrupted the tab-separated intermediate file.

Subjects are now filtered as they are read from `git log`,
replacing C0 controls, DEL,
and UTF-8-encoded C1 controls with `?`.
They are replaced rather than removed so that the bytes on either side cannot join into a new control character,
and so the change stays visible in the notes.
The section `grep` now matches bytes,
because in a UTF-8 locale GNU grep stops printing matches at a line that is not valid UTF-8,
which silently dropped bullets.
Lowercasing a subject to find its category now works on bytes too,
because in a UTF-8 locale BSD `tr` stops at a byte that is not valid UTF-8.

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-INF3JLG6; the scanner correctly classified it as not a vulnerability.
2026-10-23 08:53:58 -07:00
Jordan Harband
d639676bb0 [Fix] nvm_is_version_installed: fail when nvm_version_path fails
Since 072622c7, `nvm_version_path` rejects a version with a `..` path component, printing nothing and returning 3.

`nvm_is_version_installed` ignored that status and appended `/bin/node` to the empty output,
so for such a version (eg, `v/../x` in an `.nvmrc`)
it checked `/bin/node`,
and reported the version as installed whenever a system node lived there.
`nvm use` then put `/bin` in place of the active version on `PATH`,
and `nvm which` printed `/bin/node`.

Propagate `nvm_version_path`'s failure,
so such a version is never installed

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-O1QONH45; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
39fca6c905 [Fix] nvm use, nvm-exec: stop option parsing at --
`nvm use` treated `--` as a no-op and kept parsing options after it,
so `nvm use -- -w` enabled `--save`,
fell back to `.nvmrc`,
and rewrote it with the resolved version;
`--delete-prefix` and unknown `--*` arguments after
`--` were likewise acted on or silently dropped.

Now the argument after `--` is taken as the version and option parsing stops there;
a lone `--` still falls back to `.nvmrc`.
`nvm-exec` now passes `NODE_VERSION` after `--`,
so its value is always treated as a version and never as an option

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-4RO8S9N0; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
020503b536 [Fix] nvm install: validate an inherited NVM_MAKE_JOBS like -j
A job count given with `-j` is checked by `nvm_get_make_jobs`,
which rejects anything but a natural number and falls back to the detected core count.
A non-empty `NVM_MAKE_JOBS` already in the environment skipped that check entirely,
and was passed as-is to `make -j`,
where a value like `V=1` or `-f/dev/null` is taken as a separate `make` argument.
Now an inherited value that is not a natural number gets the same error and fallback as an invalid `-j`;
a valid one is still used,
and an empty one still triggers detection silently

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-SYLQD6PZ; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
813b5f6dcc [Fix] nvm_is_natural_num: reject anything but digits
`[ "$1" -eq "$1" ]` accepts more than a natural number:
a leading `+`, and leading or trailing whitespace,
and in ksh it evaluates its operands as arithmetic expressions,
so words like `install` and expressions like `1+1` or `V=1` pass too.
Values with any non-digit character are now rejected before that test.

The sunos and aix branches of `nvm_get_make_jobs` count cores with `wc -l`,
which on those platforms pads the count with whitespace,
so that count is now stripped of whitespace before it is checked

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-SYLQD6PZ; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
8df635740e [Fix] nvm_remote_version: match the requested version as a whole version component
The check that the resolved version actually matches the request used an unanchored substring match on the raw pattern,
so `nvm install 20` could accept `v10.20.0` when a remote list's row was selected by a `v20` in its LTS column and no 20.x rows were present.

Match the `v`-prefixed pattern followed by a non-digit against the version instead,
the same way `nvm_ls_remote_index_tab` normalizes it,
so `20` no longer matches `v10.20.0` or `v200.0.0`,
while regex-style requests like `4.*` and `v[68]`,
and a trailing dot like `8.`, keep working.
This also fixes `iojs-1` and `iojs-3`,
which previously resolved to N/A because `iojs-3` is not a substring of `iojs-v3.3.1`.

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-7GFFYVJP; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
66228bd1fa [Fix] nvm_ls: match the version pattern literally
nvm_ls escaped only `.` and `#` before using the pattern
(which may come from a `.nvmrc`)
as a sed address,
so any other regex characters in it were interpreted:
`v2[0-9]` matched installed versions,
`[` made sed fail with an error,
and nested bounded repetitions like `\(v\{1,300\}\)\{1,300\}`
made sed take a very long time and a lot of memory to compile,
stalling `nvm use` and shell startup.
Escape every BRE metacharacter,
including the backslash,
so the pattern is always matched as literal text

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-HZAFTTUK; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
6b9650c47f [Fix] bash_completion: list alias and version names without expanding them
`__nvm_generate_completion` passed its word list to `compgen -W`,
which runs every word through shell expansion.
Since that list includes alias file names and installed version directory names,
pressing Tab after `nvm use`, `nvm alias`, etc,
ran any command substitution in those names,
expanded variables in them, and globbed `*` against the current directory.
Now the list is split with globbing disabled,
and each word is matched against the current word as a literal prefix,
so names are offered exactly as written

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-QDARMGPO; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
d8b39c746e [Fix] nvm_sanitize_auth_header: strip newlines, so wrapped base64 credentials work
`sed` works one line at a time,
so the allowlist never removed newlines inside `NVM_AUTH_HEADER`.
A `Basic` credential built with GNU `base64`,
which wraps its output at 76 columns,
therefore reached the downloader split across several lines,
and authentication failed (wget rejects such a header outright).

Filter with `tr -cd` instead,
using the same set of allowed characters,
so CR and LF are deleted like any other disallowed character and a wrapped credential is joined back into one line

Discovered by Anthropic's OSS Scanner, as bug ANT-2026-W1E9TAK3; the scanner correctly classified it as not a vulnerability.
2026-10-22 20:10:42 -07:00
Jordan Harband
b1b3a0038c [Fix] nvm_cd: bypass a user-defined cd function
`\cd` only bypasses aliases, so a user-defined `cd` function (eg, cdhist's) still ran,
and broke `NVM_DIR` auto-detection when it rejected zsh's `-q`.

Use `command cd`, which skips functions in POSIX shells.
zsh's `command` runs `/usr/bin/cd` instead of the builtin,
so use `builtin cd` there, as `nvm_echo` does for `printf`.
`nvm_die_on_prefix` also now uses `nvm_cd` instead of a bare `cd`.

Fixes #3835. See #3836.

Co-authored-by: Elan Ruusamäe <glen@delfi.ee>
2026-10-09 21:16:17 -07:00
Saadanjum0
906fb605bc [Fix] nvm use: handle regex-special characters in $NVM_DIR
nvm_change_path interpolated `$NVM_DIR` and the new version dir unescaped into grep and sed expressions,
so a path containing `&`, `#`, `[`, `*`, etc. corrupted `$PATH` (or made sed fail) when switching versions.
Escape them before use.
2026-10-01 16:35:00 +05:00
Cassidy Marble
d55a55c387 [Perf] nvm_version_greater, nvm_version_greater_than_or_equal_to: avoid awk where possible 2026-10-07 15:30:01 -03:00
Cassidy Marble
9c8cac4a2e [Perf] nvm_is_valid_version, nvm_validate_implicit_alias: avoid forks 2026-10-06 18:01:38 -03:00
Cassidy Marble
66640e6000 [Perf] nvm_echo: use the printf shell builtin under zsh
In zsh, `command printf` skips the builtin and forks `/usr/bin/printf`.
2026-10-06 17:54:48 -03:00
Cassidy Marble
1dd1d8c82e [Perf] nvm_get_os: read /etc/os-release instead of forking uname -a
The file is read, not sourced. An `ID` with no OS of its own is only taken as Linux when the kernel's `ostype` says so; otherwise `uname -a` decides.
2026-10-06 14:23:02 -03:00
Cassidy Marble
c28f2b70db [Perf] nvm_npmrc_bad_news_bears: scan npmrc files without forking grep 2026-10-06 14:01:11 -03:00
Cassidy Marble
f296b8d210 [Perf] nvm_die_on_prefix: detect the OS only when it is needed 2026-10-06 14:00:46 -03:00
Cassidy Marble
c7ba8d2106 [Perf] nvm_version: resolve an exact, installed version without nvm_ls
An alias of the same name still takes precedence, as it does in `nvm_ls`.
2026-10-06 12:43:22 -03:00
Cassidy Marble
d211d5fdb8 [Perf] nvm_is_version_installed: check the modern layout's path before the full lookup 2026-10-06 12:43:22 -03:00
Hridhin Varkey Joseph
913b8cb625 [Perf] nvm_resolve_local_alias: skip nvm_version for an exact, installed version
See #3416
2026-10-04 02:22:58 +05:30
Hridhin Varkey Joseph
f1f3d463ab [Perf] nvm use: skip re-resolving an exact, installed version
When `nvm use` is given an exact `vX.Y.Z` that is already installed,
and no alias with that exact name exists,
use it directly instead of re-resolving it through
`nvm_match_version` and `nvm_ensure_version_installed`,
both of which call `nvm_version`.

`nvm_auto` takes this path on every shell startup with an already-resolved version,
so this removes ~95ms from each new shell
(~330ms -> ~235ms in a clean environment on macOS/zsh).

Fixes #3930
2026-10-01 09:51:16 +05:30
Shrikant
5326eaab9d [Tests] add unit coverage for nvm_version_greater_than_or_equal_to 2026-10-03 12:49:00 +05:30
Hridhin Varkey Joseph
a27fb0bd78 [Tests] add unit tests for nvm_version_greater_than_or_equal_to 2026-10-03 11:51:31 +05:30
Mingyang Wu
cb96d2a56a [Fix] Handle filesystem identity when checking path containment
Fixes #1049.

Assisted-by: OpenAI GPT-6 <noreply@openai.com>
2026-09-11 16:49:46 +08:00
Jordan Harband
46ca42f939 [Tests] nvm install --reinstall-packages-from: mock the remote version instead of fetching it 2026-10-05 10:52:19 -07:00
Jordan Harband
79bc9b90dd [Tests] make the fast suite pass on macOS
- `nvm_get_arch`: the unmocked smartos cases fall through to the host, so only run them on x86_64
- `nvm_get_arch_unofficial`: the chroot checks need x86_64 glibc Linux
- `nvm_install_no_progress_bar`: derive the tarball slug and checksum tool instead of assuming linux-x64 and `sha256sum`
2026-09-30 16:09:09 +09:00
Jordan Harband
0add6b8555 [Fix] nvm: do not change or export the caller's IFS
`IFS=... nvm "$@"` is a prefix assignment to a function call, which bash's POSIX mode (macOS `/bin/sh`) keeps after the call returns, and exports.
2026-09-30 15:50:38 +09:00
Jordan Harband
04ac3f2a83 [Fix] nvm install: do not remove the caller's TMPDIR after a failed download
`nvm_install_binary` and `nvm_install_source` declared `local TMPDIR` without initializing it, and remove it with `rm -rf` on failure. dash's `local` keeps the caller's value, as does bash's when `TMPDIR` is passed as a prefix assignment (`TMPDIR=… nvm install`), so a failed download removed the caller's temp directory; on macOS, `tar` instead blocked reading the archive from the terminal.
2026-10-05 10:02:48 -07:00
Ben Younes
a8bb497400 [New] nvm ls-remote: annotate versions with the node alias and local aliases
Fixes #2786.

Ben Younes added the alias annotations to bare `nvm ls-remote`, with their regression tests;
Jordan Harband moved `(Latest: node)` onto the newest node release, gave each kind of annotation a fixed column so the output can be sliced with `cut`, matched aliases to versions in one pass, and kept local aliases out of the mocks and tests.

Co-authored-by: Ben Younes <2910651+ousamabenyounes@users.noreply.github.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>
2026-09-23 21:35:43 +00:00
Jordan Harband
04fbd9570c [New] add nvm_has_italics, to detect terminal italics support via terminfo 2026-09-29 17:08:36 +09:00
Jordan Harband
09822033d2 [Fix] nvm exec/nvm run: do not treat filenames with digits as versions
`nvm_is_valid_version` accepted strings like `123.js`, `v1.js`, and `1.2.3.4.5.6.js`, because `nvm_version_greater_than_or_equal_to` exits as soon as the first number is greater than zero, without looking at the rest.
So `nvm run 123.js` failed with `N/A: version "v123.js" is not yet installed` instead of running the file.

It now checks the shape first: one to three dot-separated numbers, optionally with a trailing dot, and a prerelease suffix (rc, nightly, v8-canary, test builds) only after a full `x.y.z`.
A suffix with a dot followed by a non-digit, like a file extension, is rejected, so names like `1.2.3-foo.js` are treated as files too.
Every version listed in the nodejs.org and iojs.org `index.tab` files is still accepted, so shell startup keeps using a `default` alias that resolves to an installed prerelease.

Names that are also valid versions, like `123` or `v1`, are still treated as versions; `nvm run current 123` runs such a file.

Refs #1166. See #3931.
2026-09-28 15:05:28 +09:00
Jordan Harband
072622c748 [Fix] nvm_alias, nvm_version_path: reject .. path components
`nvm_alias` concatenated the requested name onto `$NVM_DIR/alias` and read whatever that resolved to,
so a name with a `..` component escaped the alias dir
- under the default layout, `../../.npmrc` reaches `$HOME/.npmrc`.
`nvm_print_alias_file` echoes every non-comment line of what it opens,
and `nvm use` reports the first one in its "is not yet installed" error,
so an untrusted `.nvmrc` could disclose any file the invoking user can read.

The write side already rejected `..` in an alias name (9275c5ba);
apply the same check on the read side, and to `nvm_version_path`,
which composes a version into a path with no check of its own.

Slashes stay legal, since `lts/iron` and the `lts/*` alias file depend on them.
2026-09-21 10:01:34 +08:00
Jordan Harband
a4f801ed72 [Fix] nvm_get_make_jobs: count cores on platforms the case does not name
`nvm_get_os` also returns `win` (Cygwin/MSYS/MinGW) and the empty string,
neither of which had an arm,
so `NVM_CPU_CORES` went unassigned
and nvm asked the user to report a gap it already knew about.
On an unrecognized `uname` that also meant a single-threaded source build;
`win` refuses source builds a few lines later either way.

`NUMBER_OF_PROCESSORS` comes last because Windows scopes it to the calling process' processor group.

Assigning `NVM_CPU_CORES` in every arm also keeps the
`nvm_is_natural_num` check below from aborting dash and ksh under `set -u`.
2026-09-10 17:08:50 -07:00
Jordan Harband
74bb42ee23 [Fix] nvm-exec: improve the no-version failure message
The message claimed "no .nvmrc file found" even when one was found and its version simply was not installed,
and it never mentioned that `NODE_VERSION=default` selects the `default` alias.

Refs #3810
2026-09-08 23:47:09 -07:00
Thanniru Sai Teja
cd84623ab2 [Perf] Optimize nvm_tree_contains_path with in-memory POSIX parent-walk
Replace looping subshell process forks (dirname)
in nvm_tree_contains_path with case-guarded in-memory POSIX parameter expansion () and exact string equality comparisons.

Ensures literal string matching for glob metacharacters (*, ?, [])
and full zsh compatibility across all platforms.
2026-09-03 12:24:16 +05:30
Jordan Harband
e361b93e4b [Fix] nvm use: do not clobber man's default search path
`man` consults its own configured search path only when MANPATH holds an empty entry;
a list without one replaces the default outright.
`nvm use` produced exactly that,
so activating a version could hide every system man page.

Contribute the empty entry as a trailing one,
so that nvm's directory keeps precedence over the system's,
and only when the list has none already,
so that repeated `nvm use` calls are idempotent and an empty entry the user placed stays where they put it.

The `$(manpath)` snapshot this replaces went stale the moment man's configuration changed,
and cost a subprocess per `nvm use`.
It was also dead: `local MANPATH` scoped the assignment to `nvm()`,
so `export` did not outlive the call and MANPATH went untouched whenever it started out unset.
Only ksh, where `local` is not a builtin, ever ran it.

`nvm deactivate` now unsets MANPATH where nvm's was the only real entry,
rather than leaving a bare `:` behind.

Reported and diagnosed by @al0ksar in #3890, which used a leading empty entry instead.
That one accrues one more colon on every `nvm use`,
which `nvm deactivate` then leaves behind,
and it loses to the system's man pages wherever nvm's bin directory is not first on PATH.

Supersedes #2077.

Refs #3890
Refs #2077
2026-09-08 11:01:03 -07:00
Lazizbek Ergashev
ccb49e5cd0 [Fix] nvm_err, nvm_err_with_colors: do not fail when stderr is closed
Fixes #3906
2026-09-04 14:08:01 -07:00
Jordan Harband
aeace4606f [Fix] nvm_hash_reset: only run hash -r where the shell supports it
Under bash's `set +h`, `hash -r` errors with "hash: hashing disabled",
which is then printed on every `nvm use`, including the one at shell startup.

Guarding on `$-` alone would be wrong:
`zsh` never puts `h` in `$-` - there `h` is `HIST_IGNORE_DUPS` -
and `dash` has no `h` flag at all,
so it would skip the call in 3 of the 4 shells the fast suite runs.

Gate on the shell instead, and skip only where the call fails:
bash under `set +h`, and `ksh93`, whose `hash` takes no `-r` and whose
usage error aborts `nvm use` before it can export `NVM_BIN`.
`ksh` is detected by probing rather than by version,
so `mksh` and `pdksh`, which do accept `-r`, keep getting the call.

The `${-#*h}` test is from #3910.

Refs #2065
Refs #3247
2026-09-04 09:16:10 -07:00
debaditya
80e700ec21 [Fix] nvm_alias: unset zsh's extendedglob while reading an alias file
`#` is a repetition operator under `extendedglob`,
so `${VAR%%#*}` is an invalid pattern:
zsh aborts the expansion and `nvm_alias` silently prints nothing,
leaving the `default` alias unresolved when nvm.sh is sourced.

The read loop moves into `nvm_print_alias_file` so that `local_options`
scopes the option change to that section alone,
rather than to everything `nvm_alias` does,
and restores whatever the caller had set.

Fixes #3885

Co-authored-by: debaditya <debaditya2005hait@gmail.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>
2026-09-03 20:54:28 -07:00