source: frontend/node_modules/axios/lib/helpers/shouldBypassProxy.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 13 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 4.9 KB
Line 
1const LOOPBACK_HOSTNAMES = new Set(['localhost']);
2
3const isIPv4Loopback = (host) => {
4 const parts = host.split('.');
5 if (parts.length !== 4) return false;
6 if (parts[0] !== '127') return false;
7 return parts.every((p) => /^\d+$/.test(p) && Number(p) >= 0 && Number(p) <= 255);
8};
9
10const isIPv6Loopback = (host) => {
11 // Collapse all-zero groups: any form of ::1 / 0:0:...:0:1
12 // First, strip any leading "::" by normalising with Set lookup of common forms,
13 // then fall back to structural check.
14 if (host === '::1') return true;
15
16 // Check IPv4-mapped IPv6 loopback: ::ffff:<v4-loopback> or ::ffff:<hex-v4-loopback>
17 // Node's URL parser normalises ::ffff:127.0.0.1 → ::ffff:7f00:1
18 const v4MappedDotted = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i);
19 if (v4MappedDotted) return isIPv4Loopback(v4MappedDotted[1]);
20
21 const v4MappedHex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
22 if (v4MappedHex) {
23 const high = parseInt(v4MappedHex[1], 16);
24 // High 16 bits must start with 127 (0x7f) — i.e. 0x7f00..0x7fff
25 return high >= 0x7f00 && high <= 0x7fff;
26 }
27
28 // Full-form ::1 variants: any number of zero groups followed by trailing 1
29 // e.g. 0:0:0:0:0:0:0:1, 0000:...:0001
30 const groups = host.split(':');
31 if (groups.length === 8) {
32 for (let i = 0; i < 7; i++) {
33 if (!/^0+$/.test(groups[i])) return false;
34 }
35 return /^0*1$/.test(groups[7]);
36 }
37
38 return false;
39};
40
41const isLoopback = (host) => {
42 if (!host) return false;
43 if (LOOPBACK_HOSTNAMES.has(host)) return true;
44 if (isIPv4Loopback(host)) return true;
45 return isIPv6Loopback(host);
46};
47
48const DEFAULT_PORTS = {
49 http: 80,
50 https: 443,
51 ws: 80,
52 wss: 443,
53 ftp: 21,
54};
55
56const parseNoProxyEntry = (entry) => {
57 let entryHost = entry;
58 let entryPort = 0;
59
60 if (entryHost.charAt(0) === '[') {
61 const bracketIndex = entryHost.indexOf(']');
62
63 if (bracketIndex !== -1) {
64 const host = entryHost.slice(1, bracketIndex);
65 const rest = entryHost.slice(bracketIndex + 1);
66
67 if (rest.charAt(0) === ':' && /^\d+$/.test(rest.slice(1))) {
68 entryPort = Number.parseInt(rest.slice(1), 10);
69 }
70
71 return [host, entryPort];
72 }
73 }
74
75 const firstColon = entryHost.indexOf(':');
76 const lastColon = entryHost.lastIndexOf(':');
77
78 if (
79 firstColon !== -1 &&
80 firstColon === lastColon &&
81 /^\d+$/.test(entryHost.slice(lastColon + 1))
82 ) {
83 entryPort = Number.parseInt(entryHost.slice(lastColon + 1), 10);
84 entryHost = entryHost.slice(0, lastColon);
85 }
86
87 return [entryHost, entryPort];
88};
89
90// Convert IPv4-mapped IPv6 (::ffff:0:0/96 prefix) to IPv4 dotted form so both
91// sides of a NO_PROXY comparison see the same canonical address. Without this,
92// `NO_PROXY=192.168.1.5` would not match a request to `http://[::ffff:192.168.1.5]/`
93// (Node's URL parser normalises that to `[::ffff:c0a8:105]`), and vice-versa,
94// allowing the proxy-bypass policy to be circumvented by using the alternate
95// representation. Returns the input unchanged when not IPv4-mapped.
96const IPV4_MAPPED_DOTTED_RE = /^(?:::|(?:0{1,4}:){1,4}:|(?:0{1,4}:){5})ffff:(\d+\.\d+\.\d+\.\d+)$/i;
97const IPV4_MAPPED_HEX_RE = /^(?:::|(?:0{1,4}:){1,4}:|(?:0{1,4}:){5})ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i;
98
99const unmapIPv4MappedIPv6 = (host) => {
100 if (typeof host !== 'string' || host.indexOf(':') === -1) return host;
101
102 const dotted = host.match(IPV4_MAPPED_DOTTED_RE);
103 if (dotted) return dotted[1];
104
105 const hex = host.match(IPV4_MAPPED_HEX_RE);
106 if (hex) {
107 const high = parseInt(hex[1], 16);
108 const low = parseInt(hex[2], 16);
109 return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`;
110 }
111
112 return host;
113};
114
115const normalizeNoProxyHost = (hostname) => {
116 if (!hostname) {
117 return hostname;
118 }
119
120 if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') {
121 hostname = hostname.slice(1, -1);
122 }
123
124 return unmapIPv4MappedIPv6(hostname.replace(/\.+$/, ''));
125};
126
127export default function shouldBypassProxy(location) {
128 let parsed;
129
130 try {
131 parsed = new URL(location);
132 } catch (_err) {
133 return false;
134 }
135
136 const noProxy = (process.env.no_proxy || process.env.NO_PROXY || '').toLowerCase();
137
138 if (!noProxy) {
139 return false;
140 }
141
142 if (noProxy === '*') {
143 return true;
144 }
145
146 const port =
147 Number.parseInt(parsed.port, 10) || DEFAULT_PORTS[parsed.protocol.split(':', 1)[0]] || 0;
148
149 const hostname = normalizeNoProxyHost(parsed.hostname.toLowerCase());
150
151 return noProxy.split(/[\s,]+/).some((entry) => {
152 if (!entry) {
153 return false;
154 }
155
156 let [entryHost, entryPort] = parseNoProxyEntry(entry);
157
158 entryHost = normalizeNoProxyHost(entryHost);
159
160 if (!entryHost) {
161 return false;
162 }
163
164 if (entryPort && entryPort !== port) {
165 return false;
166 }
167
168 if (entryHost.charAt(0) === '*') {
169 entryHost = entryHost.slice(1);
170 }
171
172 if (entryHost.charAt(0) === '.') {
173 return hostname.endsWith(entryHost);
174 }
175
176 return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost));
177 });
178}
Note: See TracBrowser for help on using the repository browser.