Skip to content

Commit e539856

Browse files
committed
Fix target ip
1 parent f573928 commit e539856

1 file changed

Lines changed: 32 additions & 12 deletions

File tree

src/bridge/utils.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,36 @@
88

99
export type Protocol = 'http' | 'https';
1010

11-
/** RFC 1918 + loopback regex — matches private/local network hosts. */
12-
const LOCAL_NETWORK_RE =
13-
/^(192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.|127\.|localhost)/i;
11+
/** RFC 1918 private addresses (192.168.x, 10.x, 172.16-31.x). */
12+
const PRIVATE_NETWORK_RE =
13+
/^(192\.168\.|10\.|172\.(1[6-9]|2\d|3[01])\.)/i;
14+
15+
/** Loopback addresses (127.x, localhost). */
16+
const LOOPBACK_RE = /^(127\.|localhost)/i;
1417

1518
/**
1619
* Returns true when `host` targets a private/local network address.
1720
* Used for protocol defaults and Chrome Local Network Access hints.
1821
*/
1922
export function isLocalNetworkHost(host: string): boolean {
20-
return LOCAL_NETWORK_RE.test(host);
23+
return PRIVATE_NETWORK_RE.test(host) || LOOPBACK_RE.test(host);
24+
}
25+
26+
/**
27+
* Chrome LNA `targetAddressSpace` value for a given host.
28+
*
29+
* The fetch spec defines three address spaces:
30+
* - `"local"` — loopback (127.x, localhost)
31+
* - `"private"` — RFC 1918 (10.x, 172.16-31.x, 192.168.x)
32+
* - `undefined` — public / not applicable
33+
*
34+
* Chrome validates that the resolved IP matches the declared space;
35+
* a mismatch causes the request to fail.
36+
*/
37+
export function targetAddressSpace(host: string): string | undefined {
38+
if (LOOPBACK_RE.test(host)) return 'local';
39+
if (PRIVATE_NETWORK_RE.test(host)) return 'private';
40+
return undefined;
2141
}
2242

2343
/**
@@ -40,17 +60,17 @@ export function resolveBaseUrl(host: string, protocol?: Protocol): string {
4060
/**
4161
* Build extra fetch options for Chrome Local Network Access (LNA).
4262
*
43-
* When the target URL points to a private/local address, returns
44-
* `{ targetAddressSpace: "local" }` so Chrome surfaces its LNA
45-
* permission prompt instead of silently blocking the request.
63+
* Sets `targetAddressSpace` to the correct value (`"private"` for
64+
* RFC 1918, `"local"` for loopback) so Chrome surfaces its LNA
65+
* permission prompt and relaxes mixed-content blocking.
4666
*
4767
* @see https://developer.chrome.com/blog/local-network-access
4868
*/
4969
export function localNetworkFetchOptions(url: string): RequestInit {
5070
try {
51-
const host = new URL(url).hostname;
52-
if (isLocalNetworkHost(host)) {
53-
return { targetAddressSpace: 'local' } as RequestInit;
71+
const space = targetAddressSpace(new URL(url).hostname);
72+
if (space) {
73+
return { targetAddressSpace: space } as RequestInit;
5474
}
5575
} catch {
5676
// invalid URL — ignore
@@ -65,8 +85,8 @@ export function localNetworkFetchOptions(url: string): RequestInit {
6585
* `timeoutMs` milliseconds. The AbortError can be caught upstream
6686
* to show a user-friendly timeout message.
6787
*
68-
* Automatically adds `targetAddressSpace: "local"` for private-network
69-
* URLs to cooperate with Chrome's Local Network Access restrictions.
88+
* Automatically sets `targetAddressSpace` for private/loopback URLs
89+
* to cooperate with Chrome's Local Network Access restrictions.
7090
*/
7191
export async function fetchWithTimeout(
7292
url: string,

0 commit comments

Comments
 (0)