From 3654973055952d1a21a28f8b96082e0348f75494 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 15 Sep 2026 14:08:20 +0200 Subject: [PATCH 1/3] http-client: support CIDR ranges in no_proxy Entries like 10.0.0.0/8 were compared as plain strings, so requests to IP hosts in such ranges went through the proxy even though curl, Go and most other clients bypass it. Co-Authored-By: Claude (Opus 5) --- packages/http-client/__tests__/proxy.test.ts | 7 +++++++ packages/http-client/src/proxy.ts | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/http-client/__tests__/proxy.test.ts b/packages/http-client/__tests__/proxy.test.ts index ddad334ee7..885f1a0284 100644 --- a/packages/http-client/__tests__/proxy.test.ts +++ b/packages/http-client/__tests__/proxy.test.ts @@ -183,6 +183,13 @@ describe('proxy', () => { expect(bypass).toBeTruthy() }) + it('checkBypass matches IP hosts against no_proxy CIDR ranges and skips invalid ones', () => { + process.env['no_proxy'] = '10.0.0.0/8,fd00::/8,10.0.0.0/33' + expect(pm.checkBypass(new URL('http://10.1.2.3:8088'))).toBeTruthy() + expect(pm.checkBypass(new URL('http://[fd00::1]'))).toBeTruthy() + expect(pm.checkBypass(new URL('http://11.1.2.3'))).toBeFalsy() + }) + it('checkBypass returns true if no_proxy is "*"', () => { process.env['no_proxy'] = '*' const bypass = pm.checkBypass(new URL('https://anything.whatsoever.com')) diff --git a/packages/http-client/src/proxy.ts b/packages/http-client/src/proxy.ts index 3a9c6834ec..7f831620d6 100644 --- a/packages/http-client/src/proxy.ts +++ b/packages/http-client/src/proxy.ts @@ -1,3 +1,5 @@ +import {BlockList, isIP} from 'net' + export function getProxyUrl(reqUrl: URL): URL | undefined { const usingSsl = reqUrl.protocol === 'https:' @@ -69,7 +71,8 @@ export function checkBypass(reqUrl: URL): boolean { x.endsWith(`.${upperNoProxyItem}`) || (upperNoProxyItem.startsWith('.') && x.endsWith(`${upperNoProxyItem}`)) - ) + ) || + matchesCidr(reqHost.replace(/^\[|\]$/g, ''), upperNoProxyItem) ) { return true } @@ -88,6 +91,19 @@ function isLoopbackAddress(host: string): boolean { ) } +function matchesCidr(ip: string, cidr: string): boolean { + const [network, prefix] = cidr.split('/') + if (!prefix || !isIP(ip)) return false + + const blockList = new BlockList() + try { + blockList.addSubnet(network, +prefix, isIP(network) === 6 ? 'ipv6' : 'ipv4') + return blockList.check(ip, isIP(ip) === 6 ? 'ipv6' : 'ipv4') + } catch { + return false + } +} + class DecodedURL extends URL { private _decodedUsername: string private _decodedPassword: string From 8c28864dbd77a1a2fa51638df01321cf340f396a Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 15 Sep 2026 14:25:24 +0200 Subject: [PATCH 2/3] http-client: reject no_proxy CIDR entries with extra segments An entry like 10.0.0.0/8/x was matched as 10.0.0.0/8. Co-Authored-By: Claude (Opus 5) --- packages/http-client/__tests__/proxy.test.ts | 2 +- packages/http-client/src/proxy.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/http-client/__tests__/proxy.test.ts b/packages/http-client/__tests__/proxy.test.ts index 885f1a0284..1716616387 100644 --- a/packages/http-client/__tests__/proxy.test.ts +++ b/packages/http-client/__tests__/proxy.test.ts @@ -184,7 +184,7 @@ describe('proxy', () => { }) it('checkBypass matches IP hosts against no_proxy CIDR ranges and skips invalid ones', () => { - process.env['no_proxy'] = '10.0.0.0/8,fd00::/8,10.0.0.0/33' + process.env['no_proxy'] = '10.0.0.0/8,fd00::/8,11.0.0.0/33,11.0.0.0/8/x' expect(pm.checkBypass(new URL('http://10.1.2.3:8088'))).toBeTruthy() expect(pm.checkBypass(new URL('http://[fd00::1]'))).toBeTruthy() expect(pm.checkBypass(new URL('http://11.1.2.3'))).toBeFalsy() diff --git a/packages/http-client/src/proxy.ts b/packages/http-client/src/proxy.ts index 7f831620d6..5009956f3e 100644 --- a/packages/http-client/src/proxy.ts +++ b/packages/http-client/src/proxy.ts @@ -92,8 +92,8 @@ function isLoopbackAddress(host: string): boolean { } function matchesCidr(ip: string, cidr: string): boolean { - const [network, prefix] = cidr.split('/') - if (!prefix || !isIP(ip)) return false + const [network, prefix, extra] = cidr.split('/') + if (!prefix || extra !== undefined || !isIP(ip)) return false const blockList = new BlockList() try { From ec78192ac6f3fb926f4b625987c4e262a85be341 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 15 Sep 2026 14:32:07 +0200 Subject: [PATCH 3/3] Apply suggestion from @silverwind --- packages/http-client/__tests__/proxy.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client/__tests__/proxy.test.ts b/packages/http-client/__tests__/proxy.test.ts index 1716616387..3659d26706 100644 --- a/packages/http-client/__tests__/proxy.test.ts +++ b/packages/http-client/__tests__/proxy.test.ts @@ -183,7 +183,7 @@ describe('proxy', () => { expect(bypass).toBeTruthy() }) - it('checkBypass matches IP hosts against no_proxy CIDR ranges and skips invalid ones', () => { + it('checkBypass supports CIDR in no_proxy', () => { process.env['no_proxy'] = '10.0.0.0/8,fd00::/8,11.0.0.0/33,11.0.0.0/8/x' expect(pm.checkBypass(new URL('http://10.1.2.3:8088'))).toBeTruthy() expect(pm.checkBypass(new URL('http://[fd00::1]'))).toBeTruthy()