mirror of
https://github.com/nvm-sh/nvm.git
synced 2026-09-21 00:00:07 +08:00
Compare commits
12
Commits
53855417eb
...
306f04a90a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
306f04a90a | ||
|
|
77fca77ddd | ||
|
|
1889911f08 | ||
|
|
70fb4ede6b | ||
|
|
90bb88748b | ||
|
|
6d870d182c | ||
|
|
d264b796a3 | ||
|
|
84079dbff3 | ||
|
|
61c9ff7371 | ||
|
|
16f6ddc3d5 | ||
|
|
c4d9239cbb | ||
|
|
dd13968c1e |
@@ -7,7 +7,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'nvm version tag (e.g., v0.40.4). Defaults to latest release.'
|
||||
description: 'nvm version tag (e.g., v0.40.4). Defaults to the latest version tag.'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
@@ -41,7 +41,8 @@ jobs:
|
||||
elif [ "${GITHUB_REF_TYPE}" = "tag" ]; then
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
else
|
||||
TAG="$(gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq '.tag_name')"
|
||||
TAG="$(gh api --paginate "repos/${GITHUB_REPOSITORY}/tags" --jq '.[].name' \
|
||||
| grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n 1)"
|
||||
fi
|
||||
|
||||
if ! printf '%s\n' "${TAG}" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
|
||||
@@ -74,7 +75,7 @@ jobs:
|
||||
gh api "repos/${FORK_OWNER}/nodejs.org/git/refs/heads/${BRANCH}" \
|
||||
-X PATCH \
|
||||
-f "sha=${UPSTREAM_SHA}" \
|
||||
-f "force=true" > /dev/null
|
||||
-F "force=true" > /dev/null
|
||||
fi
|
||||
|
||||
printf 'fork_owner=%s\n' "${FORK_OWNER}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
@@ -15,6 +15,8 @@ permissions:
|
||||
|
||||
jobs:
|
||||
matrix:
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.matrix.outputs.matrix }}
|
||||
@@ -43,6 +45,8 @@ jobs:
|
||||
fi
|
||||
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
needs: [matrix]
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.ref == 'v0.40.0' }} # https://github.com/nvm-sh/nvm/issues/3405
|
||||
@@ -101,6 +105,8 @@ jobs:
|
||||
fi
|
||||
|
||||
finisher:
|
||||
permissions:
|
||||
contents: none
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test]
|
||||
steps:
|
||||
|
||||
@@ -45,9 +45,19 @@ jobs:
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
with:
|
||||
submodules: true
|
||||
- name: 'nvmrc submodule fallback (forks without their own nvmrc)'
|
||||
if: steps.checkout.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
- name: Install zsh, additional shells, and awk variant
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
||||
@@ -39,8 +39,17 @@ jobs:
|
||||
azure.archive.ubuntu.com:80
|
||||
packages.microsoft.com:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
with:
|
||||
submodules: true
|
||||
- name: 'nvmrc submodule fallback (forks without their own nvmrc)'
|
||||
if: steps.checkout.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
- name: Install zsh and additional shells
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
||||
@@ -41,11 +41,21 @@ jobs:
|
||||
archive.ubuntu.com:80
|
||||
security.ubuntu.com:80
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
with:
|
||||
submodules: true
|
||||
- name: 'nvmrc submodule fallback (forks without their own nvmrc)'
|
||||
if: steps.checkout.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'npm install && version checks'
|
||||
with:
|
||||
@@ -55,6 +65,12 @@ jobs:
|
||||
- run: npx which urchin
|
||||
- name: Run installation_node tests in container
|
||||
run: |
|
||||
# Retry the image pull to tolerate transient Docker registry failures
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull ubuntu:16.04 && break
|
||||
echo "docker pull failed, attempt $i/5"
|
||||
sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
|
||||
@@ -38,11 +38,21 @@ jobs:
|
||||
archive.ubuntu.com:80
|
||||
security.ubuntu.com:80
|
||||
production.cloudflare.docker.com:443
|
||||
production.cloudfront.docker.com:443
|
||||
registry-1.docker.io:443
|
||||
auth.docker.io:443
|
||||
- uses: actions/checkout@v6
|
||||
id: checkout
|
||||
continue-on-error: true
|
||||
with:
|
||||
submodules: true
|
||||
- name: 'nvmrc submodule fallback (forks without their own nvmrc)'
|
||||
if: steps.checkout.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
git submodule set-url test/fixtures/nvmrc https://github.com/nvm-sh/nvmrc.git
|
||||
git submodule sync --recursive
|
||||
git submodule update --init --recursive
|
||||
- uses: ljharb/actions/node/install@main
|
||||
name: 'npm install && version checks'
|
||||
with:
|
||||
@@ -52,6 +62,12 @@ jobs:
|
||||
- run: npx which urchin
|
||||
- name: Run xenial tests in container
|
||||
run: |
|
||||
# Retry the image pull to tolerate transient Docker registry failures
|
||||
for i in 1 2 3 4 5; do
|
||||
docker pull ubuntu:16.04 && break
|
||||
echo "docker pull failed, attempt $i/5"
|
||||
sleep $((i * 5))
|
||||
done
|
||||
docker run --rm \
|
||||
-v "${{ github.workspace }}:/workspace" \
|
||||
-w /workspace \
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</a>
|
||||
|
||||
|
||||
# Node Version Manager [][3] [][4] [](https://bestpractices.dev/projects/684)
|
||||
# Node Version Manager [][3] [][4] [](https://bestpractices.dev/projects/684)
|
||||
|
||||
<!-- To update this table of contents, ensure you have run `npm install` then `npm run doctoc` -->
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
@@ -105,10 +105,10 @@ nvm is a version manager for [node.js](https://nodejs.org/en/), designed to be i
|
||||
|
||||
To **install** or **update** nvm, you should run the [install script][2]. To do that, you may either download and run the script manually, or use the following cURL or Wget command:
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
```sh
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
Running either of the above commands downloads a script and runs it. The script clones the nvm repository to `~/.nvm`, and attempts to add the source lines from the snippet below to the correct profile file (`~/.bashrc`, `~/.bash_profile`, `~/.zshrc`, or `~/.profile`). If you find the install script is updating the wrong profile file, set the `$PROFILE` env var to the profile file’s path, and then rerun the installation script.
|
||||
@@ -135,7 +135,7 @@ Eg: `curl ... | NVM_DIR="path/to/nvm"`. Ensure that the `NVM_DIR` does not conta
|
||||
|
||||
- The installer can use `git`, `curl`, or `wget` to download `nvm`, whichever is available.
|
||||
|
||||
- You can instruct the installer to not edit your shell config (for example if you already get completions via a [zsh nvm plugin](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/nvm)) by setting `PROFILE=/dev/null` before running the `install.sh` script. Here's an example one-line command to do that: `PROFILE=/dev/null bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash'`
|
||||
- You can instruct the installer to not edit your shell config (for example if you already get completions via a [zsh nvm plugin](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/nvm)) by setting `PROFILE=/dev/null` before running the `install.sh` script. Here's an example one-line command to do that: `PROFILE=/dev/null bash -c 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash'`
|
||||
|
||||
#### Installing in Docker
|
||||
|
||||
@@ -151,7 +151,7 @@ RUN touch "${BASH_ENV}"
|
||||
RUN echo '. "${BASH_ENV}"' >> ~/.bashrc
|
||||
|
||||
# Download and install nvm
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | PROFILE="${BASH_ENV}" bash
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | PROFILE="${BASH_ENV}" bash
|
||||
RUN echo node > .nvmrc
|
||||
RUN nvm install
|
||||
```
|
||||
@@ -169,7 +169,7 @@ ARG NODE_VERSION=20
|
||||
RUN apt update && apt install curl -y
|
||||
|
||||
# install nvm
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
|
||||
# set env
|
||||
ENV NVM_DIR=/root/.nvm
|
||||
@@ -195,7 +195,7 @@ After creation of the image you can start container interactively and run comman
|
||||
docker run --rm -it nvmimage
|
||||
|
||||
root@0a6b5a237c14:/# nvm -v
|
||||
0.40.4
|
||||
0.40.5
|
||||
|
||||
root@0a6b5a237c14:/# node -v
|
||||
v19.9.0
|
||||
@@ -258,7 +258,7 @@ You can use a task:
|
||||
```yaml
|
||||
- name: Install nvm
|
||||
ansible.builtin.shell: >
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
args:
|
||||
creates: "{{ ansible_env.HOME }}/.nvm/nvm.sh"
|
||||
```
|
||||
@@ -320,7 +320,7 @@ If you have `git` installed (requires git v1.7.10+):
|
||||
|
||||
1. clone this repo in the root of your user profile
|
||||
- `cd ~/` from anywhere then `git clone https://github.com/nvm-sh/nvm.git .nvm`
|
||||
1. `cd ~/.nvm` and check out the latest version with `git checkout v0.40.4`
|
||||
1. `cd ~/.nvm` and check out the latest version with `git checkout v0.40.5`
|
||||
1. activate `nvm` by sourcing it from your shell: `. ./nvm.sh`
|
||||
|
||||
Now add these lines to your `~/.bashrc`, `~/.profile`, or `~/.zshrc` file to have it automatically sourced upon login:
|
||||
@@ -945,13 +945,13 @@ If installing nvm on Alpine Linux *is* still what you want or need to do, you sh
|
||||
### Alpine Linux 3.13+
|
||||
```sh
|
||||
apk add -U curl bash ca-certificates openssl ncurses coreutils python3 make gcc g++ libgcc linux-headers grep util-linux binutils findutils
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
### Alpine Linux 3.5 - 3.12
|
||||
```sh
|
||||
apk add -U curl bash ca-certificates openssl ncurses coreutils python2 make gcc g++ libgcc linux-headers grep util-linux binutils findutils
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
```
|
||||
|
||||
_Note: Alpine 3.5 can only install NodeJS versions up to v6.9.5, Alpine 3.6 can only install versions up to v6.10.3, Alpine 3.7 installs versions up to v8.9.3, Alpine 3.8 installs versions up to v8.14.0, Alpine 3.9 installs versions up to v10.19.0, Alpine 3.10 installs versions up to v10.24.1, Alpine 3.11 installs versions up to v12.22.6, Alpine 3.12 installs versions up to v12.22.12, Alpine 3.13 & 3.14 install versions up to v14.20.0, Alpine 3.15 & 3.16 install versions up to v16.16.0 (**These are all versions on the main branch**). Alpine 3.5 - 3.12 required the package `python2` to build NodeJS, as they are older versions to build. Alpine 3.13+ requires `python3` to successfully build newer NodeJS versions, but you can use `python2` with Alpine 3.13+ if you need to build versions of node supported in Alpine 3.5 - 3.15, you just need to specify what version of NodeJS you need to install in the package install script._
|
||||
@@ -1054,9 +1054,9 @@ You have to make sure that the user directory name in `$HOME` and the user direc
|
||||
To change the user directory and/or account name follow the instructions [here](https://support.apple.com/en-us/HT201548)
|
||||
|
||||
[1]: https://github.com/nvm-sh/nvm.git
|
||||
[2]: https://github.com/nvm-sh/nvm/blob/v0.40.4/install.sh
|
||||
[2]: https://github.com/nvm-sh/nvm/blob/v0.40.5/install.sh
|
||||
[3]: https://github.com/nvm-sh/nvm/actions/workflows/tests-fast.yml
|
||||
[4]: https://github.com/nvm-sh/nvm/releases/tag/v0.40.4
|
||||
[4]: https://github.com/nvm-sh/nvm/releases/tag/v0.40.5
|
||||
[Urchin]: https://git.sdf.org/tlevine/urchin
|
||||
[Fish]: https://fishshell.com
|
||||
|
||||
@@ -1114,7 +1114,7 @@ Here's what you will need to do:
|
||||
If one of these broken versions is installed on your system, the above step will likely still succeed even if you didn't include the `--shared-zlib` flag.
|
||||
However, later, when you attempt to `npm install` something using your old version of node.js, you will see `incorrect data check` errors.
|
||||
If you want to avoid the possible hassle of dealing with this, include that flag.
|
||||
For more details, see [this issue](https://github.com/nodejs/node/issues/39313) and [this comment](https://github.com/nodejs/node/issues/39313#issuecomment-90.40.476)
|
||||
For more details, see [this issue](https://github.com/nodejs/node/issues/39313) and [this comment](https://github.com/nodejs/node/issues/39313#issuecomment-90.40.576)
|
||||
|
||||
- Exit back to your native shell.
|
||||
|
||||
@@ -1141,7 +1141,7 @@ Now you should be able to use node as usual.
|
||||
If you've encountered this error on WSL-2:
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.5/install.sh | bash
|
||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||
Dload Upload Total Spent Left Speed
|
||||
0 0 0 0 0 0 0 0 --:--:-- 0:00:09 --:--:-- 0curl: (6) Could not resolve host: raw.githubusercontent.com
|
||||
@@ -1176,7 +1176,7 @@ Currently, the sole maintainer is [@ljharb](https://github.com/ljharb) - more ma
|
||||
|
||||
## Project Support
|
||||
|
||||
Only the latest version (v0.40.4 at this time) is supported.
|
||||
Only the latest version (v0.40.5 at this time) is supported.
|
||||
|
||||
## Enterprise Support
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ nvm_install_dir() {
|
||||
}
|
||||
|
||||
nvm_latest_version() {
|
||||
nvm_echo "v0.40.4"
|
||||
nvm_echo "v0.40.5"
|
||||
}
|
||||
|
||||
nvm_profile_is_bash_or_zsh() {
|
||||
|
||||
@@ -115,47 +115,62 @@ nvm_get_latest() {
|
||||
nvm_echo "${NVM_LATEST_URL##*/}"
|
||||
}
|
||||
|
||||
# Every argument is passed through as a literal argv element so that untrusted,
|
||||
# mirror-supplied version strings in the URLs are never re-parsed by the shell
|
||||
# (which would allow command substitution / OS command injection).
|
||||
nvm_download() {
|
||||
if nvm_has "curl"; then
|
||||
local CURL_COMPRESSED_FLAG=""
|
||||
local CURL_HEADER_FLAG=""
|
||||
local sanitized_header
|
||||
|
||||
if [ -n "${NVM_AUTH_HEADER:-}" ]; then
|
||||
sanitized_header=$(nvm_sanitize_auth_header "${NVM_AUTH_HEADER}")
|
||||
CURL_HEADER_FLAG="--header \"Authorization: ${sanitized_header}\""
|
||||
fi
|
||||
|
||||
if nvm_curl_use_compression; then
|
||||
CURL_COMPRESSED_FLAG="--compressed"
|
||||
fi
|
||||
local NVM_DOWNLOAD_ARGS
|
||||
NVM_DOWNLOAD_ARGS=''
|
||||
for arg in "$@"; do
|
||||
NVM_DOWNLOAD_ARGS="${NVM_DOWNLOAD_ARGS} \"$arg\""
|
||||
done
|
||||
eval "curl -q --fail ${CURL_COMPRESSED_FLAG:-} ${CURL_HEADER_FLAG:-} ${NVM_DOWNLOAD_ARGS}"
|
||||
elif nvm_has "wget"; then
|
||||
# Emulate curl with wget
|
||||
ARGS=$(nvm_echo "$@" | command sed "
|
||||
s/--progress-bar /--progress=bar /
|
||||
s/--compressed //
|
||||
s/--fail //
|
||||
s/-L //
|
||||
s/-I /--server-response /
|
||||
s/-s /-q /
|
||||
s/-sS /-nv /
|
||||
s/-o /-O /
|
||||
s/-C - /-c /
|
||||
")
|
||||
|
||||
if [ -n "${NVM_AUTH_HEADER:-}" ]; then
|
||||
sanitized_header=$(nvm_sanitize_auth_header "${NVM_AUTH_HEADER}")
|
||||
ARGS="${ARGS} --header \"${sanitized_header}\""
|
||||
fi
|
||||
# shellcheck disable=SC2086
|
||||
eval wget $ARGS
|
||||
local sanitized_header
|
||||
sanitized_header=''
|
||||
if [ -n "${NVM_AUTH_HEADER:-}" ]; then
|
||||
sanitized_header="$(nvm_sanitize_auth_header "${NVM_AUTH_HEADER}")"
|
||||
fi
|
||||
|
||||
local NVM_DOWNLOADER
|
||||
NVM_DOWNLOADER=''
|
||||
if nvm_has "curl"; then
|
||||
NVM_DOWNLOADER='curl'
|
||||
set -- -q --fail "$@"
|
||||
if nvm_curl_use_compression; then
|
||||
set -- --compressed "$@"
|
||||
fi
|
||||
elif nvm_has "wget"; then
|
||||
NVM_DOWNLOADER='wget'
|
||||
# Emulate curl with wget
|
||||
local NVM_DOWNLOAD_WGET_COUNT
|
||||
NVM_DOWNLOAD_WGET_COUNT=$#
|
||||
local NVM_DOWNLOAD_WGET_SKIP
|
||||
NVM_DOWNLOAD_WGET_SKIP=0
|
||||
local NVM_DOWNLOAD_WGET_ARG
|
||||
for NVM_DOWNLOAD_WGET_ARG in "$@"; do
|
||||
if [ "${NVM_DOWNLOAD_WGET_SKIP}" = '1' ]; then
|
||||
NVM_DOWNLOAD_WGET_SKIP=0
|
||||
continue
|
||||
fi
|
||||
case "${NVM_DOWNLOAD_WGET_ARG}" in
|
||||
'--progress-bar') set -- "$@" '--progress=bar' ;;
|
||||
'--compressed') : ;;
|
||||
'--fail') : ;;
|
||||
'-L') : ;;
|
||||
'-I') set -- "$@" '--server-response' ;;
|
||||
'-s') set -- "$@" '-q' ;;
|
||||
'-sS') set -- "$@" '-nv' ;;
|
||||
'-o') set -- "$@" '-O' ;;
|
||||
'-C') NVM_DOWNLOAD_WGET_SKIP=1; set -- "$@" '-c' ;;
|
||||
*) set -- "$@" "${NVM_DOWNLOAD_WGET_ARG}" ;;
|
||||
esac
|
||||
done
|
||||
shift "${NVM_DOWNLOAD_WGET_COUNT}"
|
||||
fi
|
||||
|
||||
if [ -z "${NVM_DOWNLOADER}" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -n "${NVM_AUTH_HEADER:-}" ]; then
|
||||
set -- "$@" --header "Authorization: ${sanitized_header}"
|
||||
fi
|
||||
|
||||
"${NVM_DOWNLOADER}" "$@"
|
||||
}
|
||||
|
||||
nvm_sanitize_auth_header() {
|
||||
@@ -1912,7 +1927,7 @@ nvm_get_checksum() {
|
||||
SHASUMS_URL="${MIRROR}/${3}/SHASUMS.txt"
|
||||
fi
|
||||
|
||||
nvm_download -L -s "${SHASUMS_URL}" -o - | command awk "{ if (\"${4}.${5}\" == \$2) print \$1}"
|
||||
nvm_download -L -s "${SHASUMS_URL}" -o - | command awk -v tarball="${4}.${5}" '{ if (tarball == $2) print $1 }'
|
||||
}
|
||||
|
||||
nvm_print_versions() {
|
||||
@@ -2491,10 +2506,16 @@ nvm_download_artifact() {
|
||||
local VERSION
|
||||
VERSION="${4}"
|
||||
|
||||
if [ -z "${VERSION}" ]; then
|
||||
nvm_err 'A version number is required.'
|
||||
return 3
|
||||
fi
|
||||
case "${VERSION}" in
|
||||
'')
|
||||
nvm_err 'A version number is required.'
|
||||
return 3
|
||||
;;
|
||||
*[!0-9A-Za-z._+-]*)
|
||||
nvm_err 'Invalid version: contains disallowed characters'
|
||||
return 3
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "${KIND}" = 'binary' ] && ! nvm_binary_available "${VERSION}"; then
|
||||
nvm_err "No precompiled binary available for ${VERSION}."
|
||||
@@ -4602,7 +4623,7 @@ nvm() {
|
||||
NVM_VERSION_ONLY=true NVM_LTS="${NVM_LTS-}" nvm_remote_version "${PATTERN:-node}"
|
||||
;;
|
||||
"--version" | "-v")
|
||||
nvm_echo '0.40.4'
|
||||
nvm_echo '0.40.5'
|
||||
;;
|
||||
"unload")
|
||||
nvm deactivate >/dev/null 2>&1
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nvm",
|
||||
"version": "0.40.4",
|
||||
"version": "0.40.5",
|
||||
"description": "Node Version Manager - Simple bash script to manage multiple active node.js versions",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
cleanup () {
|
||||
unset -f die cleanup
|
||||
docker stop httpbin && docker rm httpbin
|
||||
docker rm -f httpbin >/dev/null 2>&1 || true
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
@@ -17,13 +17,24 @@ nvm_download "https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh" >/de
|
||||
# nvm_download should fail to download wrong_install.sh
|
||||
! nvm_download "https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/wrong_install.sh" >/dev/null || die "nvm_download should fail to download no existing file"
|
||||
|
||||
# nvm_download should pass when calling with auth header
|
||||
docker pull kennethreitz/httpbin && SHELL=bash docker run -d --name httpbin -p 80:80 kennethreitz/httpbin
|
||||
sleep 1 # wait for httpbin to start
|
||||
NVM_AUTH_HEADER="Bearer test-token" nvm_download "http://127.0.0.1/bearer" > /dev/null || die 'nvm_download with auth header should send correctly'
|
||||
|
||||
# nvm_download should fail when calling without auth header
|
||||
nvm_download "http://127.0.0.1/bearer" > /dev/null && die 'nvm_download with no auth header should not send the header and should fail'
|
||||
# the auth header checks need a local httpbin container; retry the pull, and
|
||||
# skip (rather than fail) if the image cannot be pulled or run, so a transient
|
||||
# Docker registry outage does not fail the suite
|
||||
httpbin_pulled=0
|
||||
for i in 1 2 3 4 5; do
|
||||
if docker pull kennethreitz/httpbin; then httpbin_pulled=1; break; fi
|
||||
echo "docker pull httpbin failed, attempt $i/5"
|
||||
sleep $((i * 5))
|
||||
done
|
||||
if [ "${httpbin_pulled}" = 1 ] && SHELL=bash docker run -d --name httpbin -p 80:80 kennethreitz/httpbin; then
|
||||
sleep 1 # wait for httpbin to start
|
||||
# nvm_download should pass when calling with auth header
|
||||
NVM_AUTH_HEADER="Bearer test-token" nvm_download "http://127.0.0.1/bearer" > /dev/null || die 'nvm_download with auth header should send correctly'
|
||||
# nvm_download should fail when calling without auth header
|
||||
nvm_download "http://127.0.0.1/bearer" > /dev/null && die 'nvm_download with no auth header should not send the header and should fail'
|
||||
else
|
||||
echo 'skipping auth header checks: unable to pull or run httpbin'
|
||||
fi
|
||||
|
||||
# ensure quoted extra args remain quoted
|
||||
nvm_download "https://raw.githubusercontent.com/nvm-sh/nvm/HEAD/install.sh" -o "; die quoted-command-not-quoted" || die 'command failed'
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
#!/bin/sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
WORK="$PWD/nvm_download-noeval-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
ARGV_LOG="$WORK/argv.log"
|
||||
PROOF="$WORK/nvm_injection_proof"
|
||||
|
||||
cleanup() {
|
||||
unset -f die cleanup
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
mkdir -p "$TEST_BIN"
|
||||
|
||||
# fake curl/wget: record each received argument verbatim, then succeed
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo ': > "$ARGV_LOG"'
|
||||
echo 'for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done'
|
||||
echo 'exit 0'
|
||||
} > "$TEST_BIN/curl"
|
||||
chmod +x "$TEST_BIN/curl"
|
||||
cp "$TEST_BIN/curl" "$TEST_BIN/wget"
|
||||
|
||||
export ARGV_LOG
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
|
||||
# given a url containing command-substitution syntax (mirror-supplied)
|
||||
INJECT_URL="http://example.test/v1\$(touch ${PROOF})/x.tar.gz"
|
||||
|
||||
# when nvm_download is invoked (curl path)
|
||||
rm -f "$PROOF"
|
||||
nvm_download "$INJECT_URL" -o - || die 'nvm_download (curl) returned nonzero on injection url'
|
||||
# then the substitution must not have executed
|
||||
[ ! -e "$PROOF" ] || die "command injection fired via curl path: proof file was created"
|
||||
# and curl must have received the url as one literal argument
|
||||
grep -Fxq "$INJECT_URL" "$ARGV_LOG" || die "curl did not receive the url as a single literal argument; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# given a normal download (curl path)
|
||||
URL="https://nodejs.org/dist/index.tab"
|
||||
FILE="$WORK/target"
|
||||
# when invoked with -L -s URL -o FILE
|
||||
nvm_download -L -s "$URL" -o "$FILE" || die 'nvm_download (curl) returned nonzero on normal url'
|
||||
# then the url, -o, and output file are passed through verbatim
|
||||
grep -Fxq "$URL" "$ARGV_LOG" || die "curl did not receive the url; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-o" "$ARGV_LOG" || die "curl did not receive -o; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxq "$FILE" "$ARGV_LOG" || die "curl did not receive the output file; got: $(cat "$ARGV_LOG")"
|
||||
# and curl-style flags are passed through verbatim (curl keeps -s; it is not translated to wget's -q)
|
||||
grep -Fxqe "-s" "$ARGV_LOG" || die "curl did not receive -s verbatim; got: $(cat "$ARGV_LOG")"
|
||||
rm -f "$FILE"
|
||||
|
||||
# given curl is unavailable (the wget-path calls run with PATH limited to our
|
||||
# fake wget, so neither the fake nor the system curl is found)
|
||||
rm -f "$TEST_BIN/curl"
|
||||
|
||||
# when nvm_download is invoked with the injection url (wget path)
|
||||
rm -f "$PROOF"
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download "$INJECT_URL" -o - ) || die 'nvm_download (wget) returned nonzero on injection url'
|
||||
# then the substitution must not have executed
|
||||
[ ! -e "$PROOF" ] || die "command injection fired via wget path: proof file was created"
|
||||
grep -Fxq "$INJECT_URL" "$ARGV_LOG" || die "wget did not receive the url as a single literal argument; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# when invoked with -L -C - --progress-bar URL -o FILE (wget path)
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -L -C - --progress-bar "$URL" -o "$FILE" ) || die 'nvm_download (wget) returned nonzero on normal url'
|
||||
# then flags are translated to wget equivalents
|
||||
grep -Fxqe "-c" "$ARGV_LOG" || die "wget did not translate -C - to -c; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "--progress=bar" "$ARGV_LOG" || die "wget did not translate --progress-bar; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-O" "$ARGV_LOG" || die "wget did not translate -o to -O; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-L" "$ARGV_LOG" && die "wget should drop -L; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-C" "$ARGV_LOG" && die "wget should not pass -C through; got: $(cat "$ARGV_LOG")"
|
||||
grep -Fxqe "-" "$ARGV_LOG" && die "wget should drop the lone - after -C; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
# when invoked with -s (wget path)
|
||||
( PATH="$TEST_BIN"; export PATH; nvm_download -L -s "$URL" -o "$FILE" ) || die 'nvm_download (wget) returned nonzero on -s url'
|
||||
# then -s becomes -q
|
||||
grep -Fxqe "-q" "$ARGV_LOG" || die "wget did not translate -s to -q; got: $(cat "$ARGV_LOG")"
|
||||
|
||||
cleanup
|
||||
echo "nvm_download no eval injection: passed"
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/bin/sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
WORK="$PWD/nvm_download-wgetauth-work.$$"
|
||||
TEST_BIN="$WORK/bin"
|
||||
ARGV_LOG="$WORK/argv.log"
|
||||
|
||||
cleanup() {
|
||||
unset -f die cleanup nvm_has
|
||||
rm -rf "$WORK"
|
||||
export PATH="$OLDPATH"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
OLDPATH="$PATH"
|
||||
|
||||
mkdir -p "$TEST_BIN"
|
||||
|
||||
# fake wget: record each received argument verbatim, then succeed
|
||||
{
|
||||
echo '#!/bin/sh'
|
||||
echo ': > "$ARGV_LOG"'
|
||||
echo 'for a in "$@"; do printf "%s\n" "$a" >> "$ARGV_LOG"; done'
|
||||
echo 'exit 0'
|
||||
} > "$TEST_BIN/wget"
|
||||
chmod +x "$TEST_BIN/wget"
|
||||
|
||||
export ARGV_LOG
|
||||
export PATH="$TEST_BIN:$OLDPATH"
|
||||
# force the wget path while keeping system tools (sed) available for sanitization
|
||||
nvm_has() { [ "$1" != curl ] && command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# given an Authorization credential in NVM_AUTH_HEADER
|
||||
# when nvm_download uses the wget path
|
||||
NVM_AUTH_HEADER='Bearer test-token' nvm_download "https://nodejs.org/dist/x" -o - || die 'nvm_download (wget) returned nonzero'
|
||||
# then wget receives a well-formed Authorization header (with the header name, like the curl path)
|
||||
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")"
|
||||
|
||||
cleanup
|
||||
echo "nvm_download wget Authorization header: passed"
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="${TMPDIR:-/tmp}/nvm_version_injection.$$"
|
||||
PROOF="${WORK}/PWNED"
|
||||
|
||||
cleanup () {
|
||||
unset -f die cleanup nvm_download
|
||||
rm -rf "${WORK}"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
mkdir -p "${WORK}"
|
||||
export NVM_DIR="${WORK}"
|
||||
|
||||
# GHSA-3c52-35h2-gfmm: a mirror-supplied version with shell/awk metacharacters
|
||||
# must be rejected before it is used in URLs, paths, or awk. Neutralize network.
|
||||
nvm_download () { return 0; }
|
||||
|
||||
# given a version containing command-substitution syntax
|
||||
# when nvm_download_artifact is asked to download it
|
||||
# then it is rejected for disallowed characters and nothing is executed
|
||||
rm -f "${PROOF}"
|
||||
out="$(nvm_download_artifact node source std 'v1$(touch '"${PROOF}"')' 2>&1 </dev/null)"
|
||||
case "${out}" in
|
||||
*'disallowed characters'*) : ;;
|
||||
*) die "command-substitution version was not rejected; got: ${out}" ;;
|
||||
esac
|
||||
[ ! -e "${PROOF}" ] || die 'command-substitution payload executed via nvm_download_artifact'
|
||||
|
||||
# and an awk-breakout version is likewise rejected
|
||||
out="$(nvm_download_artifact node source std 'v1"==$2){system("x")}#' 2>&1 </dev/null)"
|
||||
case "${out}" in
|
||||
*'disallowed characters'*) : ;;
|
||||
*) die "awk-style version was not rejected; got: ${out}" ;;
|
||||
esac
|
||||
|
||||
# and a legitimate nightly version must NOT be rejected by the character guard
|
||||
out="$(nvm_download_artifact node source std 'v22.0.0-nightly20240101abcdef' 2>&1 </dev/null)"
|
||||
case "${out}" in
|
||||
*'disallowed characters'*) die 'a legitimate nightly version was wrongly rejected by the guard' ;;
|
||||
esac
|
||||
|
||||
cleanup
|
||||
echo 'nvm_download_artifact version injection: passed'
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
WORK="${TMPDIR:-/tmp}/nvm_get_checksum_awk.$$"
|
||||
PROOF="${WORK}/PWNED"
|
||||
|
||||
cleanup () {
|
||||
unset -f die cleanup nvm_download
|
||||
rm -rf "${WORK}"
|
||||
}
|
||||
die () { echo "$@" ; cleanup ; exit 1; }
|
||||
|
||||
\. ../../../nvm.sh
|
||||
|
||||
mkdir -p "${WORK}"
|
||||
|
||||
# GHSA-3c52-35h2-gfmm: nvm_get_checksum must treat the (untrusted, version-derived)
|
||||
# slug as awk data, never as awk program text.
|
||||
# given a crafted slug carrying an unconditional awk system() action
|
||||
# and a mock that supplies one SHASUMS record (so such an action would fire)
|
||||
nvm_download () { printf 'deadbeef sometarball\n'; }
|
||||
# when nvm_get_checksum runs with that slug as its 4th argument
|
||||
rm -f "${PROOF}"
|
||||
nvm_get_checksum node std v1 'x" == $2) print $1} {system("touch${IFS}'"$PROOF"'")} #' tar.gz >/dev/null 2>&1
|
||||
# then the injected awk code must not execute
|
||||
[ ! -e "${PROOF}" ] || die 'awk injection fires in nvm_get_checksum (slug interpolated into awk program text)'
|
||||
|
||||
cleanup
|
||||
echo 'nvm_get_checksum awk injection: passed'
|
||||
@@ -17,6 +17,9 @@ NVM_ENV=testing \. ../../install.sh
|
||||
|
||||
set -ex
|
||||
|
||||
git config --global gc.autoDetach false
|
||||
git config --global maintenance.auto false
|
||||
|
||||
# nvm_do_install is available
|
||||
type install_nvm_from_git > /dev/null 2>&1 || die 'install_nvm_from_git is not available'
|
||||
|
||||
@@ -61,7 +64,7 @@ test_install_data() {
|
||||
fi
|
||||
|
||||
local head_ref="$(git for-each-ref --points-at HEAD --format='%(refname:short)' 'refs/tags/')"
|
||||
if [ -n "${avoid_ref}" ] && [ "${head_ref}" != "${avoid_ref}"]; then
|
||||
if [ -n "${avoid_ref}" ] && [ "${head_ref}" != "${avoid_ref}" ]; then
|
||||
echo "${current_ref}" | grep -q "$avoid_ref" && die "install_nvm_from_git ${message} did clone with unwanted ref ${avoid_ref}"
|
||||
fi
|
||||
|
||||
@@ -94,14 +97,14 @@ test_install() {
|
||||
test_step "Clones repo $message"
|
||||
install_nvm_from_git
|
||||
test_install_data "$(get_head_ref)" "$(get_head_changeset)" "$message" "$ref" "$changeset" "$avoid_ref"
|
||||
rm -rf "$NVM_DIR"
|
||||
rm -rf "$NVM_DIR" || { sleep 1; rm -rf "$NVM_DIR"; }
|
||||
|
||||
# Ensure it initializes the repository for an empty existing repository
|
||||
mkdir -p "$NVM_DIR" || die 'Unable to create directory'
|
||||
test_step "Initialize repo $message"
|
||||
install_nvm_from_git
|
||||
test_install_data "$(get_head_ref)" "$(get_head_changeset)" "$message" "$ref" "$changeset" "$avoid_ref"
|
||||
rm -rf "$NVM_DIR"
|
||||
rm -rf "$NVM_DIR" || { sleep 1; rm -rf "$NVM_DIR"; }
|
||||
|
||||
# Ensure it updates the repository for an existing git repository
|
||||
git clone "$(nvm_source "git")" -b "v0.36.0" --depth=2 "$NVM_DIR" || die 'Unable to clone repository'
|
||||
@@ -109,7 +112,7 @@ test_install() {
|
||||
test_step "Updates repo $message"
|
||||
install_nvm_from_git
|
||||
test_install_data "$(get_head_ref)" "$(get_head_changeset)" "$message" "$ref" "$changeset" "$avoid_ref"
|
||||
rm -rf "$NVM_DIR"
|
||||
rm -rf "$NVM_DIR" || { sleep 1; rm -rf "$NVM_DIR"; }
|
||||
}
|
||||
|
||||
# Handle latest version
|
||||
|
||||
Reference in New Issue
Block a user