`nvm use --delete-prefix`, which nvm itself recommends when it finds a `prefix` or `globalconfig` setting, ran `npm config delete` from the current directory.
For a project `.npmrc`, it ran a bare `npm config delete prefix`/`globalconfig`, so npm loaded that untrusted file first, and its `userconfig`, `globalconfig`, or `location=global` keys picked which file npm rewrote, deleted, or chmodded to 0666 - eg, `~/.bashrc` or `~/.aws/credentials`.
The builtin and user branches pinned `--userconfig`, but still loaded the project file, so a project `location=global` plus an indented ` globalconfig=` (which nvm's check does not match) redirected those writes the same way.
`npm config delete` never edits the project file anyway, so that branch could not do what it claimed.
For the project npmrc, no longer run npm at all: with `--delete-prefix`, warn that project npmrc files are not modified and must be edited by hand, and continue, as before.
For the builtin, global, and user npmrc files, pass `--prefix` set to the version directory, so npm does not walk up from the current directory to load the project npmrc, and its global config resolves to that version's `etc/npmrc`.
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-WYRTKBQ5.
`nvm install --offline` installs whatever archive sits at the cache path without checking it again, which is only safe if everything nvm writes there has been verified.
When a download failed its checksum, `nvm_download_artifact` removed only the empty `files` directory and left the rejected archive in `$NVM_DIR/.cache`, so a later `nvm install --offline` would install the very tarball the online install had just refused.
An interrupted download was also left at the final cache name.
Download to a temporary `.unverified` name instead, run the 404 and checksum checks on that file, and move it to the cache path only once the checksum has passed.
Every failure path, including a checksum mismatch or a missing expected checksum, now removes the temporary file and the version's cache directory, so `--offline` no longer finds the version at all.
The README's offline section now says that only verified downloads are cached and that `--offline` trusts the cache as-is.
Discovered by Anthropic's OSS Scanner, as vulnerability ANT-2026-7NCT51FE.
`nvm_normalize_lts` matched `lts/-N` with a glob that only pins the first digit after `lts/-`, then evaluated the rest inside `$((N+1))`.
bash and zsh evaluate a variable's contents as an arithmetic expression there, including command substitutions in array subscripts, so a name like `lts/-10*path[$(cmd)1]` ran `cmd`.
Such a name reaches this function from `nvm alias`, `nvm use`, `nvm install`, and `.nvmrc`, and also from LTS alias files written from a mirror's `index.tab`, which are resolved whenever `lts/*` or that codename is used.
The version column of a mirror's `index.tab` was used verbatim: printed by `nvm ls-remote` and `nvm version-remote`, matched against patterns, and stored as the target of `lts/*` codename aliases.
A hostile or compromised mirror could thus persist such a payload into `$NVM_DIR/alias/lts/`, where it is replayed later by `nvm alias` and alias resolution without the mirror being involved, and could put terminal escape sequences on the user's screen.
`nvm_normalize_lts` now takes everything after `lts/-` with parameter expansion and passes the name through unchanged unless it is all digits, before any arithmetic happens.
`lts/-1` through `lts/-N`, and the error when there are not that many LTS lines, behave as before; malformed offsets like `lts/-1x` or `lts/-10+1` are now returned unchanged in every shell, instead of erroring in dash or being evaluated in bash and zsh.
This also drops the `$?` check after the arithmetic, which could never fire.
`nvm_ls_remote_index_tab` now skips the header and keeps only rows whose version is `vX.Y.Z`, optionally followed by a `-` prerelease suffix of letters, digits, `.` and `-`, before anything is printed, stored, or matched.
The check runs on the raw column, before the `iojs-` prefix is added.
Every row of the current nodejs.org, nodejs.org nightly, and iojs.org index files passes, including `rc`, `nightly`, and `v8-canary` versions.
Discovered by Anthropic's OSS Scanner, as vulnerabilities ANT-2026-0GXXE91V and ANT-2026-31ZIEY85.
Options after the version went to `./configure` for a source install and were ignored for a binary install.
`--latest-npm`, `--default`, `--alias`, `--save` and `--no-progress` now also work after the version,
like `--reinstall-packages-from` already does.
`-s`, `-b`, `-j`, `--offline` and `--lts` are checked,
or used to find the version, before it is resolved:
after the version they now fail right away with a message to put them first,
and so do `---` typos.
Fixes#2066.
`nvm_install_binary` set the alias from its caller's variables,
so a source install or an `$NVM_INSTALL_THIRD_PARTY_HOOK` install never got it,
and no fresh install wrote `.nvmrc`.
Apply both at the end of `nvm install`, after the install is validated.
`--lts` without a version, or `lts/*`, leaves no provided version,
and `nvm alias` treats an empty target as `nvm unalias`:
`nvm install --lts --default` on an installed version removed the default alias.
In that case point the alias at `lts/<name>`, as the default alias already is.
`nvm install` passed the progress setting to `nvm_install_binary` and `nvm_install_source` as a prefix assignment,
which bash 3.2 in POSIX mode (macOS's `sh`) keeps after a function call returns,
so one install left `NVM_NO_PROGRESS=0` set,
and every later `nvm install --no-progress` was ignored.
Set it as a local instead.
To resolve `stable` and `unstable`,
`nvm_print_implicit_alias` started an awk process per installed minor version,
plus another per pre-1.0 one to check its parity.
Classify the whole list in a single awk pass instead,
and skip it when there is nothing to classify.
The results are unchanged.
Refs #3416. Refs #3811.
A `default-packages` file saved with Windows line endings left a carriage return on every package name,
and turned each blank line into a lone `\r` entry, so `npm install -g` was given `rimraf\r` and failed.
Strip a trailing `\r` from each line before parsing it,
like `.nvmrc` parsing already tolerates.
Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wei-Lun Li <b84330808@gmail.com>
`STABLE` is only assigned inside the loop over `LAST_TWO`,
but it is echoed unconditionally afterwards.
With nothing installed, or with only an odd minor,
the loop never runs, so under `set -u` `nvm ls` dies on
`STABLE: unbound variable` instead of resolving `stable` to nothing.
`local STABLE` without an initializer also keeps the caller's value in sh and dash,
so `STABLE=x nvm ls stable` resolves the alias to something that isn't installed.
Initializing it where it is declared fixes both,
the way 139ed9c did for `PATTERN`, `NVM_LTS`, `LTS` and `VERSION`.
`local UNSTABLE` keeps the caller's value the same way,
which its `N/A` fallback doesn't cover, so it is initialized too.
The nounset test now covers the two states that leave `stable` unresolved,
and the caller test that `nvm ls stable` answers the same either way,
and that an outer `UNSTABLE` doesn't become the `unstable` alias.
Co-authored-by: NUAAwqy <146524024+NUAAwqy@users.noreply.github.com>
Co-authored-by: Jordan Harband <ljharb@gmail.com>
`nvm_ls` decides whether a version dir is worth searching with `ls -1qA`, which
also counts dotfiles, but then collects versions with `find "${DIR}"/*`, which
never matches them. A dir holding nothing but hidden entries passes the guard,
so when `nullglob` is not set `find` is handed the literal `*` and `nvm ls`
prints "No such file or directory" to stderr.
Drop `-A` from the guard so both halves agree on what counts as an entry.
Fixes#3727.
nvm_ls collects installed versions with `find <dirs>/* -name . -o -type d -prune -o -path ...`,
and `-type d` is false for a symlink,
so a version dir that links to another volume was skipped from the listing entirely
- while `nvm version`, `nvm which` and `nvm use` all kept working on it.
Have that find follow symlinks,
which makes linked-in versions visible and still ignores a broken link.
Fixes#617.
On Windows, `npm_config_prefix` is resolved with `cd` + `pwd` so it can be compared with `$NVM_DIR`.
When the directory does not exist yet, that resolution fails,
the value becomes empty, and the check is skipped.
Keep the value as given in that case, like on other systems.
Assisted-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Wei-Lun Li <b84330808@gmail.com>
bash 3.2, macOS's `/bin/sh`, matches a bracket range like `[a-z]` by collation order in a UTF-8 locale,
which interleaves upper- and lowercase letters,
so `lts/Iron` from a symlinked `.nvmrc` was accepted there.
Match with `[:lower:]` instead.
`.nvmrc` is found and read through symlinks,
so a repository that commits `.nvmrc` as a symlink to a path outside itself
(eg, `../../.npmrc`)
made a plain `nvm use`, `nvm install`, or `nvm which`
(or `nvm-exec`, or the README's auto-switching shell hook)
print that file:
the invalid `.nvmrc` message shows everything that was parsed,
even with `--silent`,
and a one-line file was taken as the version,
which is printed in the "Found" message and in later errors.
A symlinked `.nvmrc` is still followed,
so linking to another version file
(eg, `.node-version`, or a parent directory's `.nvmrc`)
keeps working,
but its content is never shown,
and it is valid only if it names a version or an existing alias,
via a new `nvm_is_version_or_alias` helper.
`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.
`nvm_ls` strips `$NVM_DIR` and the io.js version dir from each installed version's path with sed,
but put both into the regex as-is,
so an `NVM_DIR` containing a backslash, `[`, or `*` listed no installed versions,
and one containing `#` made sed fail.
Escape both before use, as `SEARCH_PATTERN` already is.
`nvm_change_path` prepends the new version
(instead of replacing the existing nvm path)
when a system bin dir precedes an nvm path,
to handle macOS `path_helper` reordering the `PATH` of nested sessions
(#1652). However, the check matched a system dir followed by *any* nvm path.
Once a stale nvm path sits after the system dirs
(e.g. inherited by a nested shell, tmux, or an editor's terminal),
the check stays true forever,
so every `nvm use` prepends yet another entry and the previously used version is never removed from the `PATH`.
Only consider the first nvm path:
if it is not preceded by a system bin dir,
replace it in place as usual.
Fixes#3561
These passed a path under `$NVM_DIR` to awk with `-v`,
which processes backslash escapes,
so with a backslash in `NVM_DIR` awk opened the wrong file:
the LTS status record read as empty,
so `nvm_filter_installed_versions` treated every version as non-LTS
and `nvm_filter_lts_versions` matched none,
`nvm_record_lts_status` could not merge an index without LTS versions into the record,
and `nvm_get_default_packages` named the wrong file in its error.
The LTS functions now pass the path through the environment,
like `nvm_strip_path` and `nvm_replace_literal`;
`nvm_get_default_packages` uses awk's `FILENAME`, which is taken literally.
With a Windows-style `$PWD` (e.g. `D:/Work`, as Git Bash inherits when launched
by a native Windows program), `${path_%/*}` stops changing at `D:`, so the walk
never ends and sourcing `nvm.sh` hangs. Stop when going up doesn't change the
path, which also covers the existing `.` case.
Fixes#3967.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBGzjdkmgq6LSA7SFtjRsY
The pattern reached `grep` as a regex, so its dots matched any character: `20.1.1` also matched a cached `v20.111.1`, which `nvm install --offline 20.1.1` could then pick as the newest match.
A trailing dot, as in `20.1.`, likewise matched `v20.10.0` but not `v20.1.x`.
A bare `v`, `iojs-`, or `iojs-v` now matches every cached node or io.js version respectively.
Node 27 and later publish Alpha prereleases, like `v27.0.0-alpha.1.0.0`, in the main `index.tab`,
so `nvm install node` or `nvm install 27` would otherwise pick one.
Every version is in exactly one category: `alpha` and `lts` or `non-lts` (by index.tab's LTS column) on the node mirror, and `iojs` on the io.js mirror.
Commands select from every category but `alpha`, unless `$NVM_ONLY` lists others,
and every command that selects or lists versions takes `--only=<categories>` to override both;
an exact alpha needs `alpha` selected, like any other.
An LTS selection, like `--lts` or an `lts/` version, can't combine with an `--only=` that excludes `lts`.
Telling installed `lts` and `non-lts` versions apart uses the recorded LTS status, and fails rather than guess without it.
`nvm alias` also stores `--only=`, so `nvm alias default --only=alpha node` makes the latest installed alpha the default, including in a new shell.
`nvm alias default --lts node` writes `--lts node` to the alias file,
so it resolves to the latest installed LTS version, using each version's recorded LTS status;
`--lts=<name>` limits it to one LTS line.
The flags of the outermost alias apply when one alias points at another,
an alias with a flag this version of nvm doesn't recognize resolves to nothing, rather than ignoring the flag,
and `nvm uninstall` also removes aliases with flags that point at the uninstalled version.
A new shell never downloads index.tab, so it explains a default alias that needs LTS status that isn't recorded yet.
Each download of node's `index.tab` now merges every version's LTS codename, or `-`, into `$NVM_DIR/.lts-status`,
keeping versions that download doesn't list,
so the status of an installed version is known, and refreshed on every remote connection.
Only an index that includes LTS versions can start the record, so a nightly or release candidate mirror can't,
and `nvm_ensure_lts_status` downloads it once for versions installed before it was recorded.
`update_test_mocks.sh` writes the record of its `index.tab` mock to `nodejs.org-dist-lts-status.txt`.
In dash, and in bash for a variable assigned in the command's own prefix (like `PATTERN=x nvm ls`), `local` keeps the outer value,
so a variable a subcommand only sets conditionally, like `PATTERN`, `NVM_LTS`, `LTS`, or `VERSION`, could come from the caller,
replacing the version argument, applying an LTS filter that was never requested,
or making `nvm use`, without a version or an .nvmrc, use the caller's `VERSION`.
Each such local is now initialized where it is declared.
`if ! cmd; then return $?` returns the status of the negation, which is 0,
so an uppercase LTS name, or an unusable installed version, was reported but still exited successfully.
With nothing installed, `nvm_ls` prints `N/A`, which then counted as an installed version,
so `nvm ls` and an unmatched `nvm ls-remote` printed `N/A *`.
Versions longer than 15 characters, like nightlies and release candidates,
overflowed the fixed-width column, so `nvm ls` and `nvm ls-remote` misaligned them;
the column now also fits the `->` before the current version.
A prerelease pattern matched any installed version containing it,
so `nvm use v24.0.0-rc.1` could pick `v24.0.0-rc.10`,
and a partial prerelease like `v24.0.0-rc` matched nothing.
An installed exact prerelease now resolves to itself,
and a partial one matches only versions that continue with a `.`.
`sort -u` keyed only on X.Y.Z treated prereleases of a release, and the release itself, as duplicates,
so `nvm ls` hid all but one of them, and which one survived depended on directory order.
`nvm_sort_versions` orders them by semver precedence instead,
so `v24.0.0-rc.2` sorts before `v24.0.0-rc.10`, and both before `v24.0.0`.
Matching alias files by substring also removed aliases to longer versions,
so uninstalling v20.1.1 deleted an alias to v20.1.10.
Each alias's target is now read the way `nvm_alias` reads it, ignoring comments,
and in zsh an empty alias directory no longer errors with "no matches found".
An unanchored `grep` let a pattern match any cached version containing it,
so `nvm install --offline 20.1.1` could install a cached v20.1.10,
and `nvm install --offline iojs`, whose pattern is empty, matched every cached node version.
busybox's shells have no `--version`, and start an interactive shell instead,
so with `$SHELL` set to busybox `sh` on a terminal, `nvm debug` waited for input forever,
which hung the alpine `sh` CI lanes in the "mirror URL credentials are masked in output" test.
Like the other busybox tools, its version now comes from `--help`,
and every shell is given no stdin to read.
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.
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.
`install_nvm_from_git` passed `$NVM_INSTALL_VERSION` to grep as a bare argument,
and `nvm uninstall` did the same with the resolved version when looking for aliases that point to it,
so a value starting with `-` would have been parsed as grep options rather than as the pattern.
Neither value crosses a trust boundary
(the first is the user's own environment variable, and the second is an already-installed version, which never starts with `-`),
but every grep that receives a version should put it after `-e`,
which POSIX specifies and which nvm.sh already uses elsewhere
Discovered by Anthropic's OSS Scanner, alongside vulnerability ANT-2026-EN9DM7WC; this change is not itself a vulnerability fix.
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.
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.
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.
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.
`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.
`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.
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.
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.
`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.
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.
`[ "$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.