Skip to content

Add failing test for npm installer authenticated proxy bug#2330

Open
tnkuehne wants to merge 2 commits intoaxodotdev:mainfrom
tnkuehne:add-failing-test-npm-install-proxy
Open

Add failing test for npm installer authenticated proxy bug#2330
tnkuehne wants to merge 2 commits intoaxodotdev:mainfrom
tnkuehne:add-failing-test-npm-install-proxy

Conversation

@tnkuehne
Copy link
Copy Markdown

Context

We use @j178/prek, a Rust CLI distributed via npm through cargo-dist. The generated npm installer uses axios + axios-proxy-builder to download binaries. We discovered that authenticated proxies don't work. When a proxy (e.g. Claude Code web sandbox) requires credentials in the URL (HTTPS_PROXY=http://user:pass@proxy:8080), axios-proxy-builder silently drops them. It only passes host and port to tunnel.httpsOverHttp(), ignoring proxyAuth entirely.

We patched it locally:

// axios-proxy-builder dist/proxy-builder.js
-  const agent = tunnel.httpsOverHttp({
-    proxy: {
-      host: hostname,
-      port: parseInt(port),
-    },
-  });
+  const proxyConfig = {
+    host: hostname,
+    port: parseInt(port),
+  };
+  if (username && password) {
+    proxyConfig.proxyAuth = `${username}:${password}`;
+  }
+  const agent = tunnel.httpsOverHttp({
+    proxy: proxyConfig,
+  });

Patching this in every project isn't sustainable. axios-proxy-builder hasn't received an update in years, so fixing it upstream isn't realistic either. This is why I am here.

What this PR does

Adds a failing regression test (npm_installer_sends_proxy_auth) that demonstrates the bug. The test:

  1. Starts a local HTTP CONNECT proxy requiring Basic auth
  2. Sets HTTPS_PROXY with credentials
  3. Runs the actual installer code path (binary.js -> install() -> HTTP request)
  4. Checks whether the proxy received the Proxy-Authorization header

The test fails because axios-proxy-builder drops the credentials from the CONNECT request.

Suggested fix

Replace axios + axios-proxy-builder with native fetch() and undici's EnvHttpProxyAgent, which handles authenticated proxies, NO_PROXY, and all standard proxy env vars correctly out of the box. This requires bumping the node engine minimum from >=14 to >=18 (for native fetch() and Readable.fromWeb()), but Node 14 has been EOL since April 2023, so that should be safe.

For reference, Node.js v22.21.0+ added native proxy support for fetch() via --use-env-proxy / NODE_USE_ENV_PROXY, which would eliminate the undici dependency entirely. However, that requires Node 22.21+ and the end user has to opt in themselves. We can't set it from within the installer. undici's EnvHttpProxyAgent works on Node 18+ with no user configuration required.

I have a working implementation on this branch. Happy to open a follow-up PR if this direction sounds good.

@mistydemeo
Copy link
Copy Markdown
Contributor

It also appears axios itself has builtin proxy-from-env support. A simpler patch here might be to just rip out axios-proxy-builder and see if it works with axios's automatic feature?

@tnkuehne
Copy link
Copy Markdown
Author

tnkuehne commented Mar 31, 2026

It also appears axios itself has builtin proxy-from-env support. A simpler patch here might be to just rip out axios-proxy-builder and see if it works with axios's automatic feature?

Actually, I hadn't thought of this in the first place but I tested it now and removing axios-proxy-builder and letting axios handle proxies natively does fix the auth issue. But axios sends HTTPS requests as plain HTTP through the proxy instead of using CONNECT tunneling. Some proxies reject that and require proper CONNECT for HTTPS (see axios/axios#5256). So we'd fix authenticated proxies but break strict CONNECT-only proxies. The undici approach handles both correctly.

@rafaeelaudibert
Copy link
Copy Markdown

See #2350, I'm proposing axios to be dropped, which means I had to reimplement the proxy in this library. Can you check whether the implementation makes sense for you?

@tnkuehne
Copy link
Copy Markdown
Author

tnkuehne commented Apr 7, 2026

See #2350, I'm proposing axios to be dropped, which means I had to reimplement the proxy in this library. Can you check whether the implementation makes sense for you?

yes perfect, thats actually the cleanest approach! Thanks

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.

3 participants