Skip to content

Support setting Proxy-Authorization HTTP headers. - #3916

Draft
amejia1 wants to merge 1 commit into
nvm-sh:masterfrom
amejia1:master
Draft

amejia1 wants to merge 1 commit into
nvm-sh:masterfrom
amejia1:master

Conversation

@amejia1

@amejia1 amejia1 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

This PR adds changes to support setting "Proxy-Authorization" HTTP headers. This will allow for nvm with proxies that require authentication. The "Proxy-Authorization" headers are set through the NVM_PROXY_AUTH_HEADER environment variable which will behave similar to the NVM_AUTH_HEADER environment variable for setting "Authorization" headers.

Other changes included in this PR are updates to the dev container to make it easier to develop on the dev container and run the full test suite. I did this to verify I did not break anything else with the new "Proxy-Authorization" header support.

This fixes #3003.

schnare22

This comment was marked as spam.

@dev1ishere55-sudo

This comment was marked as off-topic.

@schnare22

This comment was marked as spam.

@dev1ishere55-sudo

This comment was marked as spam.

@dev1ishere55-sudo

This comment was marked as spam.

@ljharb ljharb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a massive PR diff, and i have no idea why nvm_download tests are deleted.

Any changes that are not strictly relevant to setting Proxy-Authorization headers do not belong in this PR - if you want to add dockerfile linting, or update AGENTS.md, or whatever, please do each thing in a separate PR.

@ljharb
ljharb marked this pull request as draft September 8, 2026 23:36
@amejia1

amejia1 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

this is a massive PR diff, and i have no idea why nvm_download tests are deleted.

Any changes that are not strictly relevant to setting Proxy-Authorization headers do not belong in this PR - if you want to add dockerfile linting, or update AGENTS.md, or whatever, please do each thing in a separate PR.

I just needed some way to ensure these changes to support the proxy-authorization headers would not affect something else so I made updates to get the entire test suite working for me through a container. I can move all that out in separate PRs.

@amejia1
amejia1 requested a review from ljharb September 10, 2026 00:10
@amejia1
amejia1 marked this pull request as ready for review September 10, 2026 00:11
@ljharb
ljharb marked this pull request as draft September 10, 2026 21:10
@ljharb

ljharb commented Sep 10, 2026

Copy link
Copy Markdown
Member

one of your added tests is failing

…ication

When downloads are routed through a proxy that requires its own
credentials (for example via http_proxy or https_proxy), set
NVM_PROXY_AUTH_HEADER to pass a Proxy-Authorization header to the proxy.
It is independent of NVM_AUTH_HEADER, which passes an Authorization
header through to the origin. The value is sanitized with
nvm_sanitize_auth_header before being appended to the curl or wget
invocation in nvm_download.

Add a test in the fast suite that installs a node release, runs the
dependency-free mock proxy (test/mocks/mock-proxy.js) with that
installation's node binary, and uses a local httpbin container as the
origin to verify the four combinations of Authorization and
Proxy-Authorization headers: no headers fails, an Authorization header
alone fails, a Proxy-Authorization header alone fails, and both headers
together succeed.

On Alpine, node -musl binaries are served by the unofficial-builds
mirror (the default nodejs.org mirror serves no arm64-musl artifacts),
and a -musl binary needs a libstdc++ as new as the Alpine it was built
on, so older Alpine releases can only run older node releases: the test
selects that mirror and tries candidate releases newest first (lts/*,
then v20.20.1, v18.20.4, v14.21.3 - the same-era pairings locked by the
'musl-binary' CI job), keeping the first whose binary runs. The
httpbin-dependent checks are skipped, rather than failing, if the image
cannot be pulled or run, and the test itself skips the same way if no
candidate release has a runnable binary (for example arm64 on Alpine
3.15, which no arm64-musl release targets).

Fixes: nvm-sh#3003

Assisted-by: omp:llama.cpp:ggml-org/Qwen3.8-27B-GGUF
@amejia1
amejia1 marked this pull request as ready for review September 12, 2026 17:40
Comment thread nvm.sh
Comment on lines +192 to +194
if [ -n "${NVM_PROXY_AUTH_HEADER:-}" ]; then
set -- "$@" --header "Proxy-Authorization: ${sanitized_proxy_header}"
fi

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--header is never sent on the CONNECT request, so this gets a 407 for every https:// download. Worse, when the proxy is authenticated some other way, the header goes through the tunnel to the origin server. curl has --proxy-header for exactly this (since 7.37.0, 2014). It's sent to the proxy only, both on CONNECT and on plain HTTP requests.

wget has no way to send an arbitrary header to the proxy (only --proxy-user/--proxy-password, which are Basic), so under wget this should fail clearly rather than fall back to --header:

Suggested change
if [ -n "${NVM_PROXY_AUTH_HEADER:-}" ]; then
set -- "$@" --header "Proxy-Authorization: ${sanitized_proxy_header}"
fi
if [ -n "${NVM_PROXY_AUTH_HEADER:-}" ]; then
if [ "${NVM_DOWNLOADER}" != 'curl' ]; then
nvm_err "\$NVM_PROXY_AUTH_HEADER requires curl; with wget, put Basic proxy credentials in the proxy URL instead (eg, https_proxy=http://user:pass@proxy.example.com:3128)"
return 1
fi
set -- "$@" --proxy-header "Proxy-Authorization: ${sanitized_proxy_header}"
fi

(I checked this in sh/dash/bash/zsh against the mock proxy: an HTTPS download through it succeeds, the header appears only on the CONNECT and not on the request to the origin, and under wget it errors without running wget.)

Comment thread README.md
Comment on lines +666 to +671

```sh
export http_proxy="http://proxy.example.com:3128"
export https_proxy="http://proxy.example.com:3128"
export NVM_PROXY_AUTH_HEADER="Basic dXNlcjpwYXNzd29yZA=="
nvm install node

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example is Basic auth, which already works with no nvm change: export https_proxy="http://user:password@proxy.example.com:3128" (and the same for http_proxy). I think this section should lead with that, and present $NVM_PROXY_AUTH_HEADER as being for other schemes (eg, Bearer), noting that it requires curl.

Comment on lines +103 to +110
# no Authorization and no Proxy-Authorization: expected fail
http_proxy="${proxy_url}" nvm_download "http://127.0.0.1/get" > /dev/null && die 'nvm_download with no Authorization and no Proxy-Authorization header should fail'
# Authorization but no Proxy-Authorization: expected fail
NVM_AUTH_HEADER="Bearer test-token" http_proxy="${proxy_url}" nvm_download "http://127.0.0.1/bearer" > /dev/null && die 'nvm_download with Authorization but no Proxy-Authorization header should fail'
# no Authorization but Proxy-Authorization: expected fail
NVM_PROXY_AUTH_HEADER="Bearer proxy-token" http_proxy="${proxy_url}" nvm_download "http://127.0.0.1/bearer" > /dev/null && die 'nvm_download with Proxy-Authorization but no Authorization header should fail'
# Authorization and Proxy-Authorization: expected success
NVM_AUTH_HEADER="Bearer test-token" NVM_PROXY_AUTH_HEADER="Bearer proxy-token" http_proxy="${proxy_url}" nvm_download "http://127.0.0.1/bearer" > /dev/null || die 'nvm_download with Authorization and Proxy-Authorization headers should succeed'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these use a plain http:// origin, so the proxy only ever sees absolute-form requests, and mock-proxy.js's CONNECT handler never runs. That's the path that's broken (and that nvm actually uses for nodejs.org). Please add an HTTPS case that goes through CONNECT, and ideally assert that Proxy-Authorization reaches the proxy but not the origin, eg by having httpbin echo the headers it received.

Comment on lines +46 to +57
for NODE_CANDIDATE in 'lts/*' v20.20.1 v18.20.4 v14.21.3; do
nvm install -b --no-progress --skip-default-packages "${NODE_CANDIDATE}" || continue
CURRENT_VERSION="$(nvm current)"
if [ -n "${CURRENT_VERSION}" ] && [ "${CURRENT_VERSION}" != 'none' ]; then
NODE_VERSION="${CURRENT_VERSION}"
break
fi
nvm uninstall "${NODE_CANDIDATE}" >/dev/null 2>&1 || true
done
if [ -z "${NODE_VERSION}" ]; then
echo 'skip: no node release with a runnable binary on this system (tried: lts/*, v20.20.1, v18.20.4, v14.21.3)'
exit 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This installs a full Node release from the network inside a fast test, only to run the mock proxy, and never uninstalls it, so it stays in $NVM_DIR/versions. If Node can't be installed, the test exits 0, so it "passes" without testing anything. Can the mock run some lighter way, or the test move out of fast and clean up after itself?

Alternatively, a much cheaper test wouldn't need a proxy at all: put an executable curl stub first on PATH that records its arguments (like the MOCK_BIN in test/fast/Unit tests/nvm_echo), then assert that nvm_download passes --proxy-header 'Proxy-Authorization: …' and never --header 'Proxy-Authorization: …'. (A shell function named curl won't work: nvm_download runs command curl, which skips functions; see test/fast/Unit tests/nvm_download and curl helpers bypass shadowed curl.) That only proves nvm passes the right flag, not that curl sends it to the proxy, but the mock proxy doesn't prove that either until the test has an HTTPS case.

Comment on lines +63 to +69
# `nvm install` and `nvm ls-remote` create aliases; keep the checkout clean
nvm_alias_dir="$(nvm_alias_path)"
nvm_alias_bak=''
if [ -d "${nvm_alias_dir}" ]; then
nvm_alias_bak="${nvm_alias_dir}.test-bak"
mv "${nvm_alias_dir}" "${nvm_alias_bak}"
fi

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This moves the real $NVM_DIR/alias aside under set -ex, but cleanup only runs from die or at the very end; there's no trap. If any command fails outside an || die, the script exits with the aliases still sitting in alias.test-bak. A trap cleanup EXIT (or avoiding the move entirely) would make this safe.

@ljharb
ljharb marked this pull request as draft October 10, 2026 04:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No proxy option in nvm install script

4 participants