NO_PROXY domain suffix can be used to evade proxy by a non-subdomainΒ #62907
Description
Doc says: https://nodejs.org/api/http.html#no-proxy-format
.example.com - Domain suffix match (matches sub.example.com)
But .example.com also matches totally-not-example.com.
Also (more trivially verifiable), .ample.com matches example.com:
% cat deepview-broken-proxy.js
require('node:https').request('https://example.com', (res) => {
console.log('status:', res.statusCode)
process.exit(0)
}).end()
% HTTPS_PROXY='http://10.0.0.0:1' node --use-env-proxy deepview-broken-proxy.js
node:events:487
throw er; // Unhandled 'error' event
^
Error [ERR_PROXY_TUNNEL]: Connection to establish proxy tunnel timed out after 5000ms
...
% HTTPS_PROXY='http://10.0.0.0:1' NO_PROXY='.ample.com' node --use-env-proxy deepview-broken-proxy.js
status: 200https://github.com/nodejs/node/blob/HEAD/lib/internal/http.js#L161
Details
In
ProxyConfig#shouldUseProxy(the transitive helper thatcheckShouldUseProxyalways delegates to), the.suffixbypass-list rule useshost.endsWith(suffix)after stripping the leading dot. This matches across domain-label boundaries: aNO_PROXY=.example.comrule causescheckShouldUseProxy(..., { host: 'evilexample.com' })to returnfalse(bypass proxy) because'evilexample.com'.endsWith('example.com')istrue. An attacker who can choose/influence the target hostname can therefore route traffic around a proxy that was meant to intercept all*.example.comtraffic. The check should require the character immediately preceding the suffix to be.(or the host to equal the suffix exactly), e.g.host === suffix || host.endsWith('.' + suffix)β or reuse the same form as the*.example.combranch (which is already safe because its effective suffix retains the leading dot).
Activity
DivyanshuX9 commentedon Apr 26, 2026
Hi, I'd like to work on this. I've looked some at the relevant code in lib/internal/http.js around line 161 in ProxyConfig#shouldUseProxy.
The fix seems straightforward : change the suffix check from:
jshost.endsWith(suffix)
to:
jshost === suffix || host.endsWith('.' + suffix)
This aligns with how the *.example.com branch already handles matching safely.
A couple of questions before I open a PR:
Should the fix also update the documentation to clarify that .example.com only matches actual subdomains (not suffix overlaps like evilexample.com)?
Are there existing tests for checkShouldUseProxy / shouldUseProxy I should extend, or should I add a new test file?
Happy to open a draft PR for early review if this direction looks good.
joyeecheung commentedon Apr 29, 2026
I think this has been fixed by #62333