@openclaw/gateway-client 2026.8.1-beta.1 → 2026.8.1-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/dist/browser-device-auth-Dz6H7eXy.d.mts +112 -0
- package/dist/browser.d.mts +5 -3
- package/dist/browser.mjs +2 -2
- package/dist/index.d.mts +16 -3
- package/dist/index.mjs +653 -1136
- package/dist/{protocol-client-U142L0j3.d.mts → protocol-client-BbN-I582.d.mts} +15 -30
- package/dist/protocol-request-D4mVhLxE.d.mts +32 -0
- package/dist/{readiness-uSDrqxDM.d.mts → readiness-JllUDKLg.d.mts} +28 -14
- package/dist/readiness.d.mts +1 -1
- package/dist/scope-upgrade.d.mts +38 -0
- package/dist/scope-upgrade.mjs +73 -0
- package/dist/{session-subscriptions-Das1PATe.d.mts → session-subscriptions-CKF7acYN.d.mts} +17 -119
- package/dist/{session-subscriptions-D4Xv60cN.mjs → session-subscriptions-OJnDJHPL.mjs} +336 -191
- package/dist/timeouts.mjs +3 -3
- package/dist/websocket-data.d.mts +6 -0
- package/dist/websocket-data.mjs +8 -0
- package/package.json +16 -3
package/dist/index.mjs
CHANGED
|
@@ -1,1003 +1,344 @@
|
|
|
1
|
-
import { C as buildDeviceAuthPayload, S as
|
|
1
|
+
import { C as resolveGatewayConnectScopes, D as buildDeviceAuthPayloadV3, E as buildDeviceAuthPayload, O as normalizeDeviceMetadataForAuth, S as buildGatewayConnectAuth, T as shouldRetryGatewayWithDeviceToken, _ as GatewayProtocolRequestError, a as createSessionProjection, b as gatewayOriginScope, c as projectLiveSessionMessage, d as reduceSessionProjectionRunEvent, f as normalizeSessionProjectionRunId, g as GatewayProtocolClient, h as shouldPauseGatewayReconnect, i as resetGatewaySessionMessageSubscriptionCoordinator, l as reconcileSessionProjectionSnapshot, m as readSessionMessageSequence, n as getGatewaySessionMessageSubscriptionCoordinator, o as hasSessionProjectionAcceptedFinal, p as readSessionMessageIdentity, r as releaseGatewaySessionMessageSubscription, s as isLocallyOptimisticSessionMessage, t as GatewaySessionMessageSubscriptionCoordinator, u as reduceSessionProjection, v as GatewayProtocolRequestTimeoutError, w as selectGatewayConnectAuth, x as GatewayBrowserDeviceAuthLifecycle, y as gatewayCredentialScope } from "./session-subscriptions-OJnDJHPL.mjs";
|
|
2
2
|
import { DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, getConnectChallengeTimeoutMsFromEnv, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, startGatewayConnectTimeout } from "./timeouts.mjs";
|
|
3
|
+
import { rawDataToString } from "./websocket-data.mjs";
|
|
3
4
|
import { n as startGatewayClientWithReadinessWait, r as waitForEventLoopReady, t as startGatewayClientWhenEventLoopReady } from "./readiness-CrWxd4TI.mjs";
|
|
5
|
+
import { GatewayScopeUpgrade } from "./scope-upgrade.mjs";
|
|
4
6
|
import { ConnectErrorDetailCodes, formatConnectErrorMessage, readConnectErrorDetailCode } from "@openclaw/gateway-protocol/connect-error-details";
|
|
5
7
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "@openclaw/gateway-protocol/client-info";
|
|
6
8
|
import { resolveGatewayStartupRetryAfterMs } from "@openclaw/gateway-protocol/startup-unavailable";
|
|
7
|
-
import { MIN_CLIENT_PROTOCOL_VERSION, PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
|
|
9
|
+
import { MIN_CLIENT_PROTOCOL_VERSION, MIN_NODE_PROTOCOL_VERSION, MIN_PROBE_PROTOCOL_VERSION, PROTOCOL_VERSION } from "@openclaw/gateway-protocol/version";
|
|
8
10
|
import { randomUUID } from "node:crypto";
|
|
9
11
|
import { WebSocket } from "ws";
|
|
10
|
-
import
|
|
11
|
-
//#region
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
value: mod,
|
|
31
|
-
enumerable: true
|
|
32
|
-
}) : target, mod));
|
|
12
|
+
import ipaddr from "ipaddr.js";
|
|
13
|
+
//#region packages/normalization-core/src/string-coerce.ts
|
|
14
|
+
/** Trims string input and returns null for non-strings or empty strings. */
|
|
15
|
+
function normalizeNullableString(value) {
|
|
16
|
+
if (typeof value !== "string") return null;
|
|
17
|
+
const trimmed = value.trim();
|
|
18
|
+
return trimmed ? trimmed : null;
|
|
19
|
+
}
|
|
20
|
+
/** Trims string input and returns undefined for non-strings or empty strings. */
|
|
21
|
+
function normalizeOptionalString(value) {
|
|
22
|
+
return normalizeNullableString(value) ?? void 0;
|
|
23
|
+
}
|
|
24
|
+
/** Lowercases a normalized optional string. */
|
|
25
|
+
function normalizeOptionalLowercaseString(value) {
|
|
26
|
+
return normalizeOptionalString(value)?.toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
/** Lowercases a normalized string or returns an empty string when absent. */
|
|
29
|
+
function normalizeLowercaseStringOrEmpty(value) {
|
|
30
|
+
return normalizeOptionalLowercaseString(value) ?? "";
|
|
31
|
+
}
|
|
33
32
|
//#endregion
|
|
34
|
-
//#region packages/net-policy/src/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
33
|
+
//#region packages/net-policy/src/redact-sensitive-url.ts
|
|
34
|
+
const SENSITIVE_URL_QUERY_PARAM_NAMES = /* @__PURE__ */ new Set([
|
|
35
|
+
"token",
|
|
36
|
+
"key",
|
|
37
|
+
"api_key",
|
|
38
|
+
"apikey",
|
|
39
|
+
"secret",
|
|
40
|
+
"access_token",
|
|
41
|
+
"auth_token",
|
|
42
|
+
"password",
|
|
43
|
+
"pass",
|
|
44
|
+
"passwd",
|
|
45
|
+
"auth",
|
|
46
|
+
"jwt",
|
|
47
|
+
"session",
|
|
48
|
+
"id_token",
|
|
49
|
+
"code",
|
|
50
|
+
"client_secret",
|
|
51
|
+
"app_secret",
|
|
52
|
+
"hook_token",
|
|
53
|
+
"refresh_token",
|
|
54
|
+
"signature",
|
|
55
|
+
"x_amz_signature",
|
|
56
|
+
"x_amz_security_token",
|
|
57
|
+
"private_key",
|
|
58
|
+
"credential",
|
|
59
|
+
"authorization",
|
|
60
|
+
"sig",
|
|
61
|
+
"x_api_key",
|
|
62
|
+
"x_access_token",
|
|
63
|
+
"x_auth_token"
|
|
64
|
+
]);
|
|
65
|
+
const URL_QUERY_NAME_SEPARATOR_RE = /[\p{C}\p{Z}\u115F\u1160\u3164\uFFA0+]/gu;
|
|
66
|
+
const SUFFIXED_OR_SCOPED_TOKEN_QUERY_PARAM_RE = /(?:^|_)token(?:_[a-f0-9]{16,})?$/u;
|
|
67
|
+
const TELEGRAM_BOT_TOKEN_PATH_RE = /\/bot\d{6,}(?::|%3[aA])[A-Za-z0-9_-]{20,}(?=\/|$)/giu;
|
|
68
|
+
const MAX_NESTED_URL_REDACTION_DEPTH = 8;
|
|
69
|
+
const URL_SCHEME_RE = /(?:^|[^a-z\d+.-])[a-z][a-z\d+.-]{0,31}:/iu;
|
|
70
|
+
const SPECIAL_SCHEME_AUTHORITY_RE = /\b(?:https?|wss?|ftp):[\\/]{0,2}[^\\/?#\s]*/giu;
|
|
71
|
+
const SPECIAL_SCHEME_SPILLED_USERINFO_RE = /\b(?:https?|wss?|ftp):[\\/]{0,2}[^\s]*@[^\\/?#\s]*/giu;
|
|
72
|
+
const PROTOCOL_RELATIVE_AUTHORITY_RE = /[\\/]{2,}[^\\/?#\s]*/gu;
|
|
73
|
+
function redactSensitiveUrlPath(value) {
|
|
74
|
+
return value.replace(TELEGRAM_BOT_TOKEN_PATH_RE, "/bot***");
|
|
75
|
+
}
|
|
76
|
+
function normalizeUrlQueryParamName(name) {
|
|
77
|
+
let current = name.replace(URL_QUERY_NAME_SEPARATOR_RE, "");
|
|
78
|
+
for (let depth = 0; depth <= MAX_NESTED_URL_REDACTION_DEPTH; depth += 1) {
|
|
79
|
+
let decoded;
|
|
80
|
+
try {
|
|
81
|
+
decoded = decodeURIComponent(current).replace(URL_QUERY_NAME_SEPARATOR_RE, "");
|
|
82
|
+
} catch {
|
|
81
83
|
return {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
|
|
85
|
+
unresolvedEncoding: current.includes("%")
|
|
84
86
|
};
|
|
85
87
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
if (decoded === current) return {
|
|
89
|
+
value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
|
|
90
|
+
unresolvedEncoding: false
|
|
91
|
+
};
|
|
92
|
+
current = decoded;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
value: normalizeLowercaseStringOrEmpty(current).replaceAll("-", "_"),
|
|
96
|
+
unresolvedEncoding: current.includes("%")
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function looksLikeNestedUrlValue(value) {
|
|
100
|
+
if (URL_SCHEME_RE.test(value)) return true;
|
|
101
|
+
const forwardAuthorityIndex = value.indexOf("//");
|
|
102
|
+
const backwardAuthorityIndex = value.indexOf("\\\\");
|
|
103
|
+
const authorityIndex = forwardAuthorityIndex < 0 ? backwardAuthorityIndex : backwardAuthorityIndex < 0 ? forwardAuthorityIndex : Math.min(forwardAuthorityIndex, backwardAuthorityIndex);
|
|
104
|
+
if (authorityIndex >= 0 && value.includes("@", authorityIndex + 2)) return true;
|
|
105
|
+
const queryIndex = value.search(/[?&]/u);
|
|
106
|
+
if (queryIndex >= 0 && value.includes("=", queryIndex + 1)) return true;
|
|
107
|
+
const fragmentIndex = value.indexOf("#");
|
|
108
|
+
if (fragmentIndex >= 0 && value.includes("=", fragmentIndex + 1)) return true;
|
|
109
|
+
return /%[\da-f]{2}/iu.test(value);
|
|
110
|
+
}
|
|
111
|
+
/** True for auth-like URL query parameter names that should be redacted. */
|
|
112
|
+
function isSensitiveUrlQueryParamName$1(name) {
|
|
113
|
+
const normalized = normalizeUrlQueryParamName(name);
|
|
114
|
+
return normalized.unresolvedEncoding || SENSITIVE_URL_QUERY_PARAM_NAMES.has(normalized.value) || SUFFIXED_OR_SCOPED_TOKEN_QUERY_PARAM_RE.test(normalized.value);
|
|
115
|
+
}
|
|
116
|
+
function redactDirectSensitiveUrl(value) {
|
|
117
|
+
try {
|
|
118
|
+
const parsed = new URL(value);
|
|
119
|
+
let mutated = false;
|
|
120
|
+
const redactedPath = redactSensitiveUrlPath(parsed.pathname);
|
|
121
|
+
if (redactedPath !== parsed.pathname) {
|
|
122
|
+
parsed.pathname = redactedPath;
|
|
123
|
+
mutated = true;
|
|
98
124
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
throw new Error(`ipaddr: cannot parse ${string} as octal`);
|
|
104
|
-
}
|
|
105
|
-
return parseInt(string, 10);
|
|
125
|
+
if (parsed.username || parsed.password) {
|
|
126
|
+
parsed.username = parsed.username ? "***" : "";
|
|
127
|
+
parsed.password = parsed.password ? "***" : "";
|
|
128
|
+
mutated = true;
|
|
106
129
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
130
|
+
for (const key of Array.from(parsed.searchParams.keys())) if (isSensitiveUrlQueryParamName$1(key)) {
|
|
131
|
+
parsed.searchParams.set(key, "***");
|
|
132
|
+
mutated = true;
|
|
110
133
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
]), 8]],
|
|
129
|
-
broadcast: [[new IPv4([
|
|
130
|
-
255,
|
|
131
|
-
255,
|
|
132
|
-
255,
|
|
133
|
-
255
|
|
134
|
-
]), 32]],
|
|
135
|
-
multicast: [[new IPv4([
|
|
136
|
-
224,
|
|
137
|
-
0,
|
|
138
|
-
0,
|
|
139
|
-
0
|
|
140
|
-
]), 4]],
|
|
141
|
-
linkLocal: [[new IPv4([
|
|
142
|
-
169,
|
|
143
|
-
254,
|
|
144
|
-
0,
|
|
145
|
-
0
|
|
146
|
-
]), 16]],
|
|
147
|
-
loopback: [[new IPv4([
|
|
148
|
-
127,
|
|
149
|
-
0,
|
|
150
|
-
0,
|
|
151
|
-
0
|
|
152
|
-
]), 8]],
|
|
153
|
-
carrierGradeNat: [[new IPv4([
|
|
154
|
-
100,
|
|
155
|
-
64,
|
|
156
|
-
0,
|
|
157
|
-
0
|
|
158
|
-
]), 10]],
|
|
159
|
-
"private": [
|
|
160
|
-
[new IPv4([
|
|
161
|
-
10,
|
|
162
|
-
0,
|
|
163
|
-
0,
|
|
164
|
-
0
|
|
165
|
-
]), 8],
|
|
166
|
-
[new IPv4([
|
|
167
|
-
172,
|
|
168
|
-
16,
|
|
169
|
-
0,
|
|
170
|
-
0
|
|
171
|
-
]), 12],
|
|
172
|
-
[new IPv4([
|
|
173
|
-
192,
|
|
174
|
-
168,
|
|
175
|
-
0,
|
|
176
|
-
0
|
|
177
|
-
]), 16]
|
|
178
|
-
],
|
|
179
|
-
reserved: [
|
|
180
|
-
[new IPv4([
|
|
181
|
-
192,
|
|
182
|
-
0,
|
|
183
|
-
0,
|
|
184
|
-
0
|
|
185
|
-
]), 24],
|
|
186
|
-
[new IPv4([
|
|
187
|
-
192,
|
|
188
|
-
0,
|
|
189
|
-
2,
|
|
190
|
-
0
|
|
191
|
-
]), 24],
|
|
192
|
-
[new IPv4([
|
|
193
|
-
192,
|
|
194
|
-
88,
|
|
195
|
-
99,
|
|
196
|
-
0
|
|
197
|
-
]), 24],
|
|
198
|
-
[new IPv4([
|
|
199
|
-
198,
|
|
200
|
-
18,
|
|
201
|
-
0,
|
|
202
|
-
0
|
|
203
|
-
]), 15],
|
|
204
|
-
[new IPv4([
|
|
205
|
-
198,
|
|
206
|
-
51,
|
|
207
|
-
100,
|
|
208
|
-
0
|
|
209
|
-
]), 24],
|
|
210
|
-
[new IPv4([
|
|
211
|
-
203,
|
|
212
|
-
0,
|
|
213
|
-
113,
|
|
214
|
-
0
|
|
215
|
-
]), 24],
|
|
216
|
-
[new IPv4([
|
|
217
|
-
240,
|
|
218
|
-
0,
|
|
219
|
-
0,
|
|
220
|
-
0
|
|
221
|
-
]), 4]
|
|
222
|
-
],
|
|
223
|
-
as112: [[new IPv4([
|
|
224
|
-
192,
|
|
225
|
-
175,
|
|
226
|
-
48,
|
|
227
|
-
0
|
|
228
|
-
]), 24], [new IPv4([
|
|
229
|
-
192,
|
|
230
|
-
31,
|
|
231
|
-
196,
|
|
232
|
-
0
|
|
233
|
-
]), 24]],
|
|
234
|
-
amt: [[new IPv4([
|
|
235
|
-
192,
|
|
236
|
-
52,
|
|
237
|
-
193,
|
|
238
|
-
0
|
|
239
|
-
]), 24]]
|
|
240
|
-
};
|
|
241
|
-
IPv4.prototype.kind = function() {
|
|
242
|
-
return "ipv4";
|
|
243
|
-
};
|
|
244
|
-
IPv4.prototype.match = function(other, cidrRange) {
|
|
245
|
-
let ref;
|
|
246
|
-
if (cidrRange === void 0) {
|
|
247
|
-
ref = other;
|
|
248
|
-
other = ref[0];
|
|
249
|
-
cidrRange = ref[1];
|
|
250
|
-
}
|
|
251
|
-
if (other.kind() !== "ipv4") throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");
|
|
252
|
-
return matchCIDR(this.octets, other.octets, 8, cidrRange);
|
|
253
|
-
};
|
|
254
|
-
IPv4.prototype.prefixLengthFromSubnetMask = function() {
|
|
255
|
-
let cidr = 0;
|
|
256
|
-
let stop = false;
|
|
257
|
-
const zerotable = {
|
|
258
|
-
0: 8,
|
|
259
|
-
128: 7,
|
|
260
|
-
192: 6,
|
|
261
|
-
224: 5,
|
|
262
|
-
240: 4,
|
|
263
|
-
248: 3,
|
|
264
|
-
252: 2,
|
|
265
|
-
254: 1,
|
|
266
|
-
255: 0
|
|
267
|
-
};
|
|
268
|
-
let i, octet, zeros;
|
|
269
|
-
for (i = 3; i >= 0; i -= 1) {
|
|
270
|
-
octet = this.octets[i];
|
|
271
|
-
if (octet in zerotable) {
|
|
272
|
-
zeros = zerotable[octet];
|
|
273
|
-
if (stop && zeros !== 0) return null;
|
|
274
|
-
if (zeros !== 8) stop = true;
|
|
275
|
-
cidr += zeros;
|
|
276
|
-
} else return null;
|
|
277
|
-
}
|
|
278
|
-
return 32 - cidr;
|
|
279
|
-
};
|
|
280
|
-
IPv4.prototype.range = function() {
|
|
281
|
-
return ipaddr.subnetMatch(this, this.SpecialRanges);
|
|
282
|
-
};
|
|
283
|
-
IPv4.prototype.toByteArray = function() {
|
|
284
|
-
return this.octets.slice(0);
|
|
285
|
-
};
|
|
286
|
-
IPv4.prototype.toIPv4MappedAddress = function() {
|
|
287
|
-
return ipaddr.IPv6.parse(`::ffff:${this.toString()}`);
|
|
288
|
-
};
|
|
289
|
-
IPv4.prototype.toNormalizedString = function() {
|
|
290
|
-
return this.toString();
|
|
291
|
-
};
|
|
292
|
-
IPv4.prototype.toString = function() {
|
|
293
|
-
return this.octets.join(".");
|
|
294
|
-
};
|
|
295
|
-
return IPv4;
|
|
296
|
-
})();
|
|
297
|
-
ipaddr.IPv4.broadcastAddressFromCIDR = function(string) {
|
|
298
|
-
try {
|
|
299
|
-
const cidr = this.parseCIDR(string);
|
|
300
|
-
const ipInterfaceOctets = cidr[0].toByteArray();
|
|
301
|
-
const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
302
|
-
const octets = [];
|
|
303
|
-
let i = 0;
|
|
304
|
-
while (i < 4) {
|
|
305
|
-
octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
|
|
306
|
-
i++;
|
|
307
|
-
}
|
|
308
|
-
return new this(octets);
|
|
309
|
-
} catch (e) {
|
|
310
|
-
throw new Error("ipaddr: the address does not have IPv4 CIDR format");
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
ipaddr.IPv4.isIPv4 = function(string) {
|
|
314
|
-
return this.parser(string) !== null;
|
|
315
|
-
};
|
|
316
|
-
ipaddr.IPv4.isValid = function(string) {
|
|
317
|
-
try {
|
|
318
|
-
new this(this.parser(string));
|
|
319
|
-
return true;
|
|
320
|
-
} catch (e) {
|
|
321
|
-
return false;
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
ipaddr.IPv4.isValidCIDR = function(string) {
|
|
325
|
-
try {
|
|
326
|
-
this.parseCIDR(string);
|
|
327
|
-
return true;
|
|
328
|
-
} catch (e) {
|
|
329
|
-
return false;
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
ipaddr.IPv4.isValidFourPartDecimal = function(string) {
|
|
333
|
-
if (ipaddr.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) return true;
|
|
334
|
-
else return false;
|
|
335
|
-
};
|
|
336
|
-
ipaddr.IPv4.isValidCIDRFourPartDecimal = function(string) {
|
|
337
|
-
const match = string.match(/^(.+)\/(\d+)$/);
|
|
338
|
-
if (!ipaddr.IPv4.isValidCIDR(string) || !match) return false;
|
|
339
|
-
return ipaddr.IPv4.isValidFourPartDecimal(match[1]);
|
|
340
|
-
};
|
|
341
|
-
ipaddr.IPv4.networkAddressFromCIDR = function(string) {
|
|
342
|
-
let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
|
|
343
|
-
try {
|
|
344
|
-
cidr = this.parseCIDR(string);
|
|
345
|
-
ipInterfaceOctets = cidr[0].toByteArray();
|
|
346
|
-
subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
347
|
-
octets = [];
|
|
348
|
-
i = 0;
|
|
349
|
-
while (i < 4) {
|
|
350
|
-
octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
|
|
351
|
-
i++;
|
|
352
|
-
}
|
|
353
|
-
return new this(octets);
|
|
354
|
-
} catch (e) {
|
|
355
|
-
throw new Error("ipaddr: the address does not have IPv4 CIDR format");
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
ipaddr.IPv4.parse = function(string) {
|
|
359
|
-
const parts = this.parser(string);
|
|
360
|
-
if (parts === null) throw new Error("ipaddr: string is not formatted like an IPv4 Address");
|
|
361
|
-
return new this(parts);
|
|
362
|
-
};
|
|
363
|
-
ipaddr.IPv4.parseCIDR = function(string) {
|
|
364
|
-
let match;
|
|
365
|
-
if (match = string.match(/^(.+)\/(\d+)$/)) {
|
|
366
|
-
const maskLength = parseInt(match[2]);
|
|
367
|
-
if (maskLength >= 0 && maskLength <= 32) {
|
|
368
|
-
const parsed = [this.parse(match[1]), maskLength];
|
|
369
|
-
Object.defineProperty(parsed, "toString", { value: function() {
|
|
370
|
-
return this.join("/");
|
|
371
|
-
} });
|
|
372
|
-
return parsed;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range");
|
|
376
|
-
};
|
|
377
|
-
ipaddr.IPv4.parser = function(string) {
|
|
378
|
-
let match, part, value;
|
|
379
|
-
if (match = string.match(ipv4Regexes.fourOctet)) return (function() {
|
|
380
|
-
const ref = match.slice(1, 6);
|
|
381
|
-
const results = [];
|
|
382
|
-
for (let i = 0; i < ref.length; i++) {
|
|
383
|
-
part = ref[i];
|
|
384
|
-
results.push(parseIntAuto(part));
|
|
385
|
-
}
|
|
386
|
-
return results;
|
|
387
|
-
})();
|
|
388
|
-
else if (match = string.match(ipv4Regexes.longValue)) {
|
|
389
|
-
value = parseIntAuto(match[1]);
|
|
390
|
-
if (value > 4294967295 || value < 0) throw new Error("ipaddr: address outside defined range");
|
|
391
|
-
return (function() {
|
|
392
|
-
const results = [];
|
|
393
|
-
let shift;
|
|
394
|
-
for (shift = 0; shift <= 24; shift += 8) results.push(value >> shift & 255);
|
|
395
|
-
return results;
|
|
396
|
-
})().reverse();
|
|
397
|
-
} else if (match = string.match(ipv4Regexes.twoOctet)) return (function() {
|
|
398
|
-
const ref = match.slice(1, 4);
|
|
399
|
-
const results = [];
|
|
400
|
-
value = parseIntAuto(ref[1]);
|
|
401
|
-
if (value > 16777215 || value < 0) throw new Error("ipaddr: address outside defined range");
|
|
402
|
-
results.push(parseIntAuto(ref[0]));
|
|
403
|
-
results.push(value >> 16 & 255);
|
|
404
|
-
results.push(value >> 8 & 255);
|
|
405
|
-
results.push(value & 255);
|
|
406
|
-
return results;
|
|
407
|
-
})();
|
|
408
|
-
else if (match = string.match(ipv4Regexes.threeOctet)) return (function() {
|
|
409
|
-
const ref = match.slice(1, 5);
|
|
410
|
-
const results = [];
|
|
411
|
-
value = parseIntAuto(ref[2]);
|
|
412
|
-
if (value > 65535 || value < 0) throw new Error("ipaddr: address outside defined range");
|
|
413
|
-
results.push(parseIntAuto(ref[0]));
|
|
414
|
-
results.push(parseIntAuto(ref[1]));
|
|
415
|
-
results.push(value >> 8 & 255);
|
|
416
|
-
results.push(value & 255);
|
|
417
|
-
return results;
|
|
418
|
-
})();
|
|
419
|
-
else return null;
|
|
420
|
-
};
|
|
421
|
-
ipaddr.IPv4.subnetMaskFromPrefixLength = function(prefix) {
|
|
422
|
-
prefix = parseInt(prefix);
|
|
423
|
-
if (prefix < 0 || prefix > 32) throw new Error("ipaddr: invalid IPv4 prefix length");
|
|
424
|
-
const octets = [
|
|
425
|
-
0,
|
|
426
|
-
0,
|
|
427
|
-
0,
|
|
428
|
-
0
|
|
429
|
-
];
|
|
430
|
-
let j = 0;
|
|
431
|
-
const filledOctetCount = Math.floor(prefix / 8);
|
|
432
|
-
while (j < filledOctetCount) {
|
|
433
|
-
octets[j] = 255;
|
|
434
|
-
j++;
|
|
134
|
+
return mutated ? parsed.toString() : value;
|
|
135
|
+
} catch {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function redactQueryString(value, depth) {
|
|
140
|
+
const params = new URLSearchParams(value);
|
|
141
|
+
const entries = Array.from(params.entries());
|
|
142
|
+
const redactedEntries = [];
|
|
143
|
+
const seenSensitiveKeys = /* @__PURE__ */ new Set();
|
|
144
|
+
let mutated = false;
|
|
145
|
+
for (const [key, entryValue] of entries) {
|
|
146
|
+
if (isSensitiveUrlQueryParamName$1(key)) {
|
|
147
|
+
mutated = true;
|
|
148
|
+
if (!seenSensitiveKeys.has(key)) {
|
|
149
|
+
seenSensitiveKeys.add(key);
|
|
150
|
+
redactedEntries.push([key, "***"]);
|
|
435
151
|
}
|
|
436
|
-
|
|
437
|
-
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const redactedKey = redactNestedUrlValue(key, depth + 1);
|
|
155
|
+
const redactedValue = redactNestedUrlValue(entryValue, depth + 1);
|
|
156
|
+
if (redactedKey !== key || redactedValue !== entryValue) mutated = true;
|
|
157
|
+
redactedEntries.push([redactedKey, redactedValue]);
|
|
158
|
+
}
|
|
159
|
+
if (!mutated) return value;
|
|
160
|
+
const redactedParams = new URLSearchParams();
|
|
161
|
+
for (const [key, entryValue] of redactedEntries) redactedParams.append(key, entryValue);
|
|
162
|
+
return redactedParams.toString();
|
|
163
|
+
}
|
|
164
|
+
function redactUrlLikeFallback(value) {
|
|
165
|
+
return redactSensitiveUrlPath(redactEmbeddedUrlUserInfo(value).replace(/([?&])([^=&]+)=([^&]*)/g, (match, prefix, key) => isSensitiveUrlQueryParamName$1(key) ? `${prefix}${key}=***` : match));
|
|
166
|
+
}
|
|
167
|
+
function redactAuthorityUserInfo(candidate, authorityStart) {
|
|
168
|
+
const authority = candidate.slice(authorityStart);
|
|
169
|
+
const userInfoEnd = authority.lastIndexOf("@");
|
|
170
|
+
if (userInfoEnd < 0) return candidate;
|
|
171
|
+
return `${candidate.slice(0, authorityStart)}***:***@${authority.slice(userInfoEnd + 1)}`;
|
|
172
|
+
}
|
|
173
|
+
function redactEmbeddedUrlUserInfo(value) {
|
|
174
|
+
return value.replace(SPECIAL_SCHEME_AUTHORITY_RE, (candidate) => {
|
|
175
|
+
let authorityStart = candidate.indexOf(":") + 1;
|
|
176
|
+
while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
|
|
177
|
+
return redactAuthorityUserInfo(candidate, authorityStart);
|
|
178
|
+
}).replace(SPECIAL_SCHEME_SPILLED_USERINFO_RE, (candidate) => {
|
|
179
|
+
let authorityStart = candidate.indexOf(":") + 1;
|
|
180
|
+
while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
|
|
181
|
+
const userInfoEnd = candidate.lastIndexOf("@");
|
|
182
|
+
const firstReservedDelimiter = candidate.slice(authorityStart).search(/[\\/?#]/u);
|
|
183
|
+
if (userInfoEnd < 0 || firstReservedDelimiter < 0) return candidate;
|
|
184
|
+
const absoluteReservedDelimiter = authorityStart + firstReservedDelimiter;
|
|
185
|
+
if (absoluteReservedDelimiter >= userInfoEnd) return candidate;
|
|
186
|
+
const credentialSeparator = candidate.indexOf(":", authorityStart);
|
|
187
|
+
if (credentialSeparator < 0 || credentialSeparator > absoluteReservedDelimiter) return candidate;
|
|
188
|
+
const authorityPrefix = candidate.slice(authorityStart, absoluteReservedDelimiter);
|
|
189
|
+
const possiblePort = candidate.slice(credentialSeparator + 1, absoluteReservedDelimiter);
|
|
190
|
+
if (/^\d+$/u.test(possiblePort) || /^\[[^\]]+\](?::\d+)?$/u.test(authorityPrefix)) return candidate;
|
|
191
|
+
return `${candidate.slice(0, authorityStart)}***:***@${candidate.slice(userInfoEnd + 1)}`;
|
|
192
|
+
}).replace(PROTOCOL_RELATIVE_AUTHORITY_RE, (candidate) => {
|
|
193
|
+
let authorityStart = 0;
|
|
194
|
+
while (authorityStart < candidate.length && (candidate[authorityStart] === "/" || candidate[authorityStart] === "\\")) authorityStart += 1;
|
|
195
|
+
return redactAuthorityUserInfo(candidate, authorityStart);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function hasUnresolvedEmbeddedUrlUserInfo(value) {
|
|
199
|
+
for (const match of value.matchAll(/(?:\b(?:https?|wss?|ftp):[\\/]{0,2}|[\\/]{2,})/giu)) {
|
|
200
|
+
const remainder = value.slice((match.index ?? 0) + match[0].length);
|
|
201
|
+
const userInfoEnd = remainder.search(/(?<!\*\*\*:\*\*\*)@/u);
|
|
202
|
+
const authorityEnd = remainder.search(/[\\/?#]/u);
|
|
203
|
+
const pathBeforeAt = remainder.slice(authorityEnd + 1, userInfoEnd);
|
|
204
|
+
if (userInfoEnd >= 0 && (authorityEnd < 0 || userInfoEnd <= authorityEnd || remainder[authorityEnd] === "/" && (pathBeforeAt.includes(":") || /^[^/?#\s]+\.[^/?#\s]+(?:[/?#]|$)/u.test(remainder.slice(userInfoEnd + 1))))) return true;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
function redactRelativeUrlFragment(value, depth) {
|
|
209
|
+
const fragmentIndex = value.indexOf("#");
|
|
210
|
+
if (fragmentIndex < 0) return value;
|
|
211
|
+
const fragment = value.slice(fragmentIndex + 1);
|
|
212
|
+
const redactedFragment = redactFragment(fragment, depth + 1);
|
|
213
|
+
return redactedFragment === fragment ? value : `${value.slice(0, fragmentIndex + 1)}${redactedFragment}`;
|
|
214
|
+
}
|
|
215
|
+
function redactFragment(value, depth) {
|
|
216
|
+
if (!value) return value;
|
|
217
|
+
if (depth > MAX_NESTED_URL_REDACTION_DEPTH && looksLikeNestedUrlValue(value)) return "***";
|
|
218
|
+
const wholeUrl = redactSensitiveUrlAtDepth(value, depth);
|
|
219
|
+
if (wholeUrl.parsedWholeUrl) return redactUrlLikeFallback(wholeUrl.value);
|
|
220
|
+
const candidate = value;
|
|
221
|
+
const firstQueryDelimiter = candidate.search(/[?&]/u);
|
|
222
|
+
const firstEquals = candidate.indexOf("=");
|
|
223
|
+
if (firstEquals >= 0 && (firstQueryDelimiter < 0 || firstEquals < firstQueryDelimiter)) return redactQueryString(candidate, depth);
|
|
224
|
+
const hashRouterQueryIndex = candidate.indexOf("?");
|
|
225
|
+
if (hashRouterQueryIndex >= 0) {
|
|
226
|
+
const redactedQuery = redactQueryString(candidate.slice(hashRouterQueryIndex + 1), depth);
|
|
227
|
+
return `${redactEncodedUrlLikeString(redactUrlLikeFallback(candidate.slice(0, hashRouterQueryIndex + 1)), depth + 1)}${redactedQuery}`;
|
|
228
|
+
}
|
|
229
|
+
const fallback = redactUrlLikeFallback(candidate);
|
|
230
|
+
if (!looksLikeNestedUrlValue(fallback)) return fallback;
|
|
231
|
+
let decoded;
|
|
232
|
+
try {
|
|
233
|
+
decoded = decodeURIComponent(fallback);
|
|
234
|
+
} catch {
|
|
235
|
+
return "***";
|
|
236
|
+
}
|
|
237
|
+
if (decoded === fallback) return fallback;
|
|
238
|
+
const redactedDecoded = redactFragment(decoded, depth + 1);
|
|
239
|
+
return redactedDecoded === decoded ? fallback : encodeURIComponent(redactedDecoded);
|
|
240
|
+
}
|
|
241
|
+
function redactEncodedNestedUrlPath(value, depth) {
|
|
242
|
+
if (!looksLikeNestedUrlValue(value)) return value;
|
|
243
|
+
if (depth > MAX_NESTED_URL_REDACTION_DEPTH) return "***";
|
|
244
|
+
let decoded;
|
|
245
|
+
try {
|
|
246
|
+
decoded = decodeURIComponent(value);
|
|
247
|
+
} catch {
|
|
248
|
+
return "***";
|
|
249
|
+
}
|
|
250
|
+
if (decoded === value) return value;
|
|
251
|
+
const direct = redactSensitiveUrlLikeStringAtDepth(decoded, depth);
|
|
252
|
+
if (direct.value !== decoded || hasUnresolvedEmbeddedUrlUserInfo(decoded)) return direct.value !== decoded ? direct.value : "***";
|
|
253
|
+
if (direct.parsedWholeUrl) return value;
|
|
254
|
+
const nested = redactEncodedNestedUrlPath(decoded, depth + 1);
|
|
255
|
+
return nested === decoded ? value : nested;
|
|
256
|
+
}
|
|
257
|
+
function redactSensitiveUrlAtDepth(value, depth) {
|
|
258
|
+
try {
|
|
259
|
+
const directRedaction = redactDirectSensitiveUrl(value);
|
|
260
|
+
const parsed = new URL(directRedaction);
|
|
261
|
+
if (depth > MAX_NESTED_URL_REDACTION_DEPTH) return {
|
|
262
|
+
value: "***",
|
|
263
|
+
parsedWholeUrl: true
|
|
438
264
|
};
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
for (i = 0; i < this.parts.length; i++) {
|
|
448
|
-
part = this.parts[i];
|
|
449
|
-
if (!(0 <= part && part <= 65535)) throw new Error("ipaddr: ipv6 part should fit in 16 bits");
|
|
450
|
-
}
|
|
451
|
-
if (zoneId) this.zoneId = zoneId;
|
|
452
|
-
}
|
|
453
|
-
IPv6.prototype.SpecialRanges = {
|
|
454
|
-
unspecified: [new IPv6([
|
|
455
|
-
0,
|
|
456
|
-
0,
|
|
457
|
-
0,
|
|
458
|
-
0,
|
|
459
|
-
0,
|
|
460
|
-
0,
|
|
461
|
-
0,
|
|
462
|
-
0
|
|
463
|
-
]), 128],
|
|
464
|
-
linkLocal: [new IPv6([
|
|
465
|
-
65152,
|
|
466
|
-
0,
|
|
467
|
-
0,
|
|
468
|
-
0,
|
|
469
|
-
0,
|
|
470
|
-
0,
|
|
471
|
-
0,
|
|
472
|
-
0
|
|
473
|
-
]), 10],
|
|
474
|
-
multicast: [new IPv6([
|
|
475
|
-
65280,
|
|
476
|
-
0,
|
|
477
|
-
0,
|
|
478
|
-
0,
|
|
479
|
-
0,
|
|
480
|
-
0,
|
|
481
|
-
0,
|
|
482
|
-
0
|
|
483
|
-
]), 8],
|
|
484
|
-
loopback: [new IPv6([
|
|
485
|
-
0,
|
|
486
|
-
0,
|
|
487
|
-
0,
|
|
488
|
-
0,
|
|
489
|
-
0,
|
|
490
|
-
0,
|
|
491
|
-
0,
|
|
492
|
-
1
|
|
493
|
-
]), 128],
|
|
494
|
-
uniqueLocal: [new IPv6([
|
|
495
|
-
64512,
|
|
496
|
-
0,
|
|
497
|
-
0,
|
|
498
|
-
0,
|
|
499
|
-
0,
|
|
500
|
-
0,
|
|
501
|
-
0,
|
|
502
|
-
0
|
|
503
|
-
]), 7],
|
|
504
|
-
ipv4Mapped: [new IPv6([
|
|
505
|
-
0,
|
|
506
|
-
0,
|
|
507
|
-
0,
|
|
508
|
-
0,
|
|
509
|
-
0,
|
|
510
|
-
65535,
|
|
511
|
-
0,
|
|
512
|
-
0
|
|
513
|
-
]), 96],
|
|
514
|
-
deprecatedSiteLocal: [new IPv6([
|
|
515
|
-
65216,
|
|
516
|
-
0,
|
|
517
|
-
0,
|
|
518
|
-
0,
|
|
519
|
-
0,
|
|
520
|
-
0,
|
|
521
|
-
0,
|
|
522
|
-
0
|
|
523
|
-
]), 10],
|
|
524
|
-
discard: [new IPv6([
|
|
525
|
-
256,
|
|
526
|
-
0,
|
|
527
|
-
0,
|
|
528
|
-
0,
|
|
529
|
-
0,
|
|
530
|
-
0,
|
|
531
|
-
0,
|
|
532
|
-
0
|
|
533
|
-
]), 64],
|
|
534
|
-
rfc6145: [new IPv6([
|
|
535
|
-
0,
|
|
536
|
-
0,
|
|
537
|
-
0,
|
|
538
|
-
0,
|
|
539
|
-
65535,
|
|
540
|
-
0,
|
|
541
|
-
0,
|
|
542
|
-
0
|
|
543
|
-
]), 96],
|
|
544
|
-
rfc6052: [[new IPv6([
|
|
545
|
-
100,
|
|
546
|
-
65435,
|
|
547
|
-
0,
|
|
548
|
-
0,
|
|
549
|
-
0,
|
|
550
|
-
0,
|
|
551
|
-
0,
|
|
552
|
-
0
|
|
553
|
-
]), 96], [new IPv6([
|
|
554
|
-
100,
|
|
555
|
-
65435,
|
|
556
|
-
1,
|
|
557
|
-
0,
|
|
558
|
-
0,
|
|
559
|
-
0,
|
|
560
|
-
0,
|
|
561
|
-
0
|
|
562
|
-
]), 48]],
|
|
563
|
-
"6to4": [new IPv6([
|
|
564
|
-
8194,
|
|
565
|
-
0,
|
|
566
|
-
0,
|
|
567
|
-
0,
|
|
568
|
-
0,
|
|
569
|
-
0,
|
|
570
|
-
0,
|
|
571
|
-
0
|
|
572
|
-
]), 16],
|
|
573
|
-
teredo: [new IPv6([
|
|
574
|
-
8193,
|
|
575
|
-
0,
|
|
576
|
-
0,
|
|
577
|
-
0,
|
|
578
|
-
0,
|
|
579
|
-
0,
|
|
580
|
-
0,
|
|
581
|
-
0
|
|
582
|
-
]), 32],
|
|
583
|
-
benchmarking: [new IPv6([
|
|
584
|
-
8193,
|
|
585
|
-
2,
|
|
586
|
-
0,
|
|
587
|
-
0,
|
|
588
|
-
0,
|
|
589
|
-
0,
|
|
590
|
-
0,
|
|
591
|
-
0
|
|
592
|
-
]), 48],
|
|
593
|
-
amt: [new IPv6([
|
|
594
|
-
8193,
|
|
595
|
-
3,
|
|
596
|
-
0,
|
|
597
|
-
0,
|
|
598
|
-
0,
|
|
599
|
-
0,
|
|
600
|
-
0,
|
|
601
|
-
0
|
|
602
|
-
]), 32],
|
|
603
|
-
as112v6: [[new IPv6([
|
|
604
|
-
8193,
|
|
605
|
-
4,
|
|
606
|
-
274,
|
|
607
|
-
0,
|
|
608
|
-
0,
|
|
609
|
-
0,
|
|
610
|
-
0,
|
|
611
|
-
0
|
|
612
|
-
]), 48], [new IPv6([
|
|
613
|
-
9760,
|
|
614
|
-
79,
|
|
615
|
-
32768,
|
|
616
|
-
0,
|
|
617
|
-
0,
|
|
618
|
-
0,
|
|
619
|
-
0,
|
|
620
|
-
0
|
|
621
|
-
]), 48]],
|
|
622
|
-
deprecatedOrchid: [new IPv6([
|
|
623
|
-
8193,
|
|
624
|
-
16,
|
|
625
|
-
0,
|
|
626
|
-
0,
|
|
627
|
-
0,
|
|
628
|
-
0,
|
|
629
|
-
0,
|
|
630
|
-
0
|
|
631
|
-
]), 28],
|
|
632
|
-
orchid2: [new IPv6([
|
|
633
|
-
8193,
|
|
634
|
-
32,
|
|
635
|
-
0,
|
|
636
|
-
0,
|
|
637
|
-
0,
|
|
638
|
-
0,
|
|
639
|
-
0,
|
|
640
|
-
0
|
|
641
|
-
]), 28],
|
|
642
|
-
droneRemoteIdProtocolEntityTags: [new IPv6([
|
|
643
|
-
8193,
|
|
644
|
-
48,
|
|
645
|
-
0,
|
|
646
|
-
0,
|
|
647
|
-
0,
|
|
648
|
-
0,
|
|
649
|
-
0,
|
|
650
|
-
0
|
|
651
|
-
]), 28],
|
|
652
|
-
segmentRouting: [new IPv6([
|
|
653
|
-
24320,
|
|
654
|
-
0,
|
|
655
|
-
0,
|
|
656
|
-
0,
|
|
657
|
-
0,
|
|
658
|
-
0,
|
|
659
|
-
0,
|
|
660
|
-
0
|
|
661
|
-
]), 16],
|
|
662
|
-
reserved: [
|
|
663
|
-
[new IPv6([
|
|
664
|
-
8193,
|
|
665
|
-
0,
|
|
666
|
-
0,
|
|
667
|
-
0,
|
|
668
|
-
0,
|
|
669
|
-
0,
|
|
670
|
-
0,
|
|
671
|
-
0
|
|
672
|
-
]), 23],
|
|
673
|
-
[new IPv6([
|
|
674
|
-
8193,
|
|
675
|
-
3512,
|
|
676
|
-
0,
|
|
677
|
-
0,
|
|
678
|
-
0,
|
|
679
|
-
0,
|
|
680
|
-
0,
|
|
681
|
-
0
|
|
682
|
-
]), 32],
|
|
683
|
-
[new IPv6([
|
|
684
|
-
16383,
|
|
685
|
-
0,
|
|
686
|
-
0,
|
|
687
|
-
0,
|
|
688
|
-
0,
|
|
689
|
-
0,
|
|
690
|
-
0,
|
|
691
|
-
0
|
|
692
|
-
]), 20]
|
|
693
|
-
]
|
|
694
|
-
};
|
|
695
|
-
IPv6.prototype.isIPv4MappedAddress = function() {
|
|
696
|
-
return this.range() === "ipv4Mapped";
|
|
697
|
-
};
|
|
698
|
-
IPv6.prototype.kind = function() {
|
|
699
|
-
return "ipv6";
|
|
700
|
-
};
|
|
701
|
-
IPv6.prototype.match = function(other, cidrRange) {
|
|
702
|
-
let ref;
|
|
703
|
-
if (cidrRange === void 0) {
|
|
704
|
-
ref = other;
|
|
705
|
-
other = ref[0];
|
|
706
|
-
cidrRange = ref[1];
|
|
707
|
-
}
|
|
708
|
-
if (other.kind() !== "ipv6") throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");
|
|
709
|
-
return matchCIDR(this.parts, other.parts, 16, cidrRange);
|
|
710
|
-
};
|
|
711
|
-
IPv6.prototype.prefixLengthFromSubnetMask = function() {
|
|
712
|
-
let cidr = 0;
|
|
713
|
-
let stop = false;
|
|
714
|
-
const zerotable = {
|
|
715
|
-
0: 16,
|
|
716
|
-
32768: 15,
|
|
717
|
-
49152: 14,
|
|
718
|
-
57344: 13,
|
|
719
|
-
61440: 12,
|
|
720
|
-
63488: 11,
|
|
721
|
-
64512: 10,
|
|
722
|
-
65024: 9,
|
|
723
|
-
65280: 8,
|
|
724
|
-
65408: 7,
|
|
725
|
-
65472: 6,
|
|
726
|
-
65504: 5,
|
|
727
|
-
65520: 4,
|
|
728
|
-
65528: 3,
|
|
729
|
-
65532: 2,
|
|
730
|
-
65534: 1,
|
|
731
|
-
65535: 0
|
|
732
|
-
};
|
|
733
|
-
let part, zeros;
|
|
734
|
-
for (let i = 7; i >= 0; i -= 1) {
|
|
735
|
-
part = this.parts[i];
|
|
736
|
-
if (part in zerotable) {
|
|
737
|
-
zeros = zerotable[part];
|
|
738
|
-
if (stop && zeros !== 0) return null;
|
|
739
|
-
if (zeros !== 16) stop = true;
|
|
740
|
-
cidr += zeros;
|
|
741
|
-
} else return null;
|
|
742
|
-
}
|
|
743
|
-
return 128 - cidr;
|
|
744
|
-
};
|
|
745
|
-
IPv6.prototype.range = function() {
|
|
746
|
-
return ipaddr.subnetMatch(this, this.SpecialRanges);
|
|
747
|
-
};
|
|
748
|
-
IPv6.prototype.toByteArray = function() {
|
|
749
|
-
let part;
|
|
750
|
-
const bytes = [];
|
|
751
|
-
const ref = this.parts;
|
|
752
|
-
for (let i = 0; i < ref.length; i++) {
|
|
753
|
-
part = ref[i];
|
|
754
|
-
bytes.push(part >> 8);
|
|
755
|
-
bytes.push(part & 255);
|
|
756
|
-
}
|
|
757
|
-
return bytes;
|
|
758
|
-
};
|
|
759
|
-
IPv6.prototype.toFixedLengthString = function() {
|
|
760
|
-
const addr = (function() {
|
|
761
|
-
const results = [];
|
|
762
|
-
for (let i = 0; i < this.parts.length; i++) results.push(padPart(this.parts[i].toString(16), 4));
|
|
763
|
-
return results;
|
|
764
|
-
}).call(this).join(":");
|
|
765
|
-
let suffix = "";
|
|
766
|
-
if (this.zoneId) suffix = `%${this.zoneId}`;
|
|
767
|
-
return addr + suffix;
|
|
768
|
-
};
|
|
769
|
-
IPv6.prototype.toIPv4Address = function() {
|
|
770
|
-
if (!this.isIPv4MappedAddress()) throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");
|
|
771
|
-
const ref = this.parts.slice(-2);
|
|
772
|
-
const high = ref[0];
|
|
773
|
-
const low = ref[1];
|
|
774
|
-
return new ipaddr.IPv4([
|
|
775
|
-
high >> 8,
|
|
776
|
-
high & 255,
|
|
777
|
-
low >> 8,
|
|
778
|
-
low & 255
|
|
779
|
-
]);
|
|
780
|
-
};
|
|
781
|
-
IPv6.prototype.toNormalizedString = function() {
|
|
782
|
-
const addr = (function() {
|
|
783
|
-
const results = [];
|
|
784
|
-
for (let i = 0; i < this.parts.length; i++) results.push(this.parts[i].toString(16));
|
|
785
|
-
return results;
|
|
786
|
-
}).call(this).join(":");
|
|
787
|
-
let suffix = "";
|
|
788
|
-
if (this.zoneId) suffix = `%${this.zoneId}`;
|
|
789
|
-
return addr + suffix;
|
|
790
|
-
};
|
|
791
|
-
IPv6.prototype.toRFC5952String = function() {
|
|
792
|
-
const regex = /((^|:)(0(:|$)){2,})/g;
|
|
793
|
-
const string = this.toNormalizedString();
|
|
794
|
-
let bestMatchIndex = 0;
|
|
795
|
-
let bestMatchLength = -1;
|
|
796
|
-
let match;
|
|
797
|
-
while (match = regex.exec(string)) if (match[0].length > bestMatchLength) {
|
|
798
|
-
bestMatchIndex = match.index;
|
|
799
|
-
bestMatchLength = match[0].length;
|
|
800
|
-
}
|
|
801
|
-
if (bestMatchLength < 0) return string;
|
|
802
|
-
return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`;
|
|
265
|
+
let mutated = directRedaction !== value;
|
|
266
|
+
const redactedNestedPath = redactEmbeddedUrlUserInfo(redactEncodedNestedUrlPath(parsed.pathname, depth + 1));
|
|
267
|
+
if (redactedNestedPath !== parsed.pathname) {
|
|
268
|
+
const originalPath = parsed.pathname;
|
|
269
|
+
parsed.pathname = redactedNestedPath;
|
|
270
|
+
if (parsed.pathname === originalPath) return {
|
|
271
|
+
value: directRedaction,
|
|
272
|
+
parsedWholeUrl: false
|
|
803
273
|
};
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
return new this(octets);
|
|
821
|
-
} catch (e) {
|
|
822
|
-
throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
|
|
823
|
-
}
|
|
824
|
-
};
|
|
825
|
-
ipaddr.IPv6.isIPv6 = function(string) {
|
|
826
|
-
return this.parser(string) !== null;
|
|
827
|
-
};
|
|
828
|
-
ipaddr.IPv6.isValid = function(string) {
|
|
829
|
-
if (typeof string === "string" && string.indexOf(":") === -1) return false;
|
|
830
|
-
try {
|
|
831
|
-
const addr = this.parser(string);
|
|
832
|
-
new this(addr.parts, addr.zoneId);
|
|
833
|
-
return true;
|
|
834
|
-
} catch (e) {
|
|
835
|
-
return false;
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
ipaddr.IPv6.isValidCIDR = function(string) {
|
|
839
|
-
if (typeof string === "string" && string.indexOf(":") === -1) return false;
|
|
840
|
-
try {
|
|
841
|
-
this.parseCIDR(string);
|
|
842
|
-
return true;
|
|
843
|
-
} catch (e) {
|
|
844
|
-
return false;
|
|
845
|
-
}
|
|
846
|
-
};
|
|
847
|
-
ipaddr.IPv6.networkAddressFromCIDR = function(string) {
|
|
848
|
-
let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
|
|
849
|
-
try {
|
|
850
|
-
cidr = this.parseCIDR(string);
|
|
851
|
-
ipInterfaceOctets = cidr[0].toByteArray();
|
|
852
|
-
subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
853
|
-
octets = [];
|
|
854
|
-
i = 0;
|
|
855
|
-
while (i < 16) {
|
|
856
|
-
octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
|
|
857
|
-
i++;
|
|
858
|
-
}
|
|
859
|
-
return new this(octets);
|
|
860
|
-
} catch (e) {
|
|
861
|
-
throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
|
|
862
|
-
}
|
|
863
|
-
};
|
|
864
|
-
ipaddr.IPv6.parse = function(string) {
|
|
865
|
-
const addr = this.parser(string);
|
|
866
|
-
if (addr.parts === null) throw new Error("ipaddr: string is not formatted like an IPv6 Address");
|
|
867
|
-
return new this(addr.parts, addr.zoneId);
|
|
868
|
-
};
|
|
869
|
-
ipaddr.IPv6.parseCIDR = function(string) {
|
|
870
|
-
let maskLength, match, parsed;
|
|
871
|
-
if (match = string.match(/^(.+)\/(\d+)$/)) {
|
|
872
|
-
maskLength = parseInt(match[2]);
|
|
873
|
-
if (maskLength >= 0 && maskLength <= 128) {
|
|
874
|
-
parsed = [this.parse(match[1]), maskLength];
|
|
875
|
-
Object.defineProperty(parsed, "toString", { value: function() {
|
|
876
|
-
return this.join("/");
|
|
877
|
-
} });
|
|
878
|
-
return parsed;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range");
|
|
882
|
-
};
|
|
883
|
-
ipaddr.IPv6.parser = function(string) {
|
|
884
|
-
let addr, i, match, octet, octets, zoneId;
|
|
885
|
-
if (match = string.match(ipv6Regexes.deprecatedTransitional)) return this.parser(`::ffff:${match[1]}`);
|
|
886
|
-
if (ipv6Regexes.native.test(string)) return expandIPv6(string, 8);
|
|
887
|
-
if (match = string.match(ipv6Regexes.transitional)) {
|
|
888
|
-
zoneId = match[6] || "";
|
|
889
|
-
addr = match[1];
|
|
890
|
-
if (!match[1].endsWith("::")) addr = addr.slice(0, -1);
|
|
891
|
-
addr = expandIPv6(addr + zoneId, 6);
|
|
892
|
-
if (addr.parts) {
|
|
893
|
-
octets = [
|
|
894
|
-
parseInt(match[2]),
|
|
895
|
-
parseInt(match[3]),
|
|
896
|
-
parseInt(match[4]),
|
|
897
|
-
parseInt(match[5])
|
|
898
|
-
];
|
|
899
|
-
for (i = 0; i < octets.length; i++) {
|
|
900
|
-
octet = octets[i];
|
|
901
|
-
if (!(0 <= octet && octet <= 255)) return null;
|
|
902
|
-
}
|
|
903
|
-
addr.parts.push(octets[0] << 8 | octets[1]);
|
|
904
|
-
addr.parts.push(octets[2] << 8 | octets[3]);
|
|
905
|
-
return {
|
|
906
|
-
parts: addr.parts,
|
|
907
|
-
zoneId: addr.zoneId
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
return null;
|
|
912
|
-
};
|
|
913
|
-
ipaddr.IPv6.subnetMaskFromPrefixLength = function(prefix) {
|
|
914
|
-
prefix = parseInt(prefix);
|
|
915
|
-
if (prefix < 0 || prefix > 128) throw new Error("ipaddr: invalid IPv6 prefix length");
|
|
916
|
-
const octets = [
|
|
917
|
-
0,
|
|
918
|
-
0,
|
|
919
|
-
0,
|
|
920
|
-
0,
|
|
921
|
-
0,
|
|
922
|
-
0,
|
|
923
|
-
0,
|
|
924
|
-
0,
|
|
925
|
-
0,
|
|
926
|
-
0,
|
|
927
|
-
0,
|
|
928
|
-
0,
|
|
929
|
-
0,
|
|
930
|
-
0,
|
|
931
|
-
0,
|
|
932
|
-
0
|
|
933
|
-
];
|
|
934
|
-
let j = 0;
|
|
935
|
-
const filledOctetCount = Math.floor(prefix / 8);
|
|
936
|
-
while (j < filledOctetCount) {
|
|
937
|
-
octets[j] = 255;
|
|
938
|
-
j++;
|
|
939
|
-
}
|
|
940
|
-
if (filledOctetCount < 16) octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - prefix % 8;
|
|
941
|
-
return new this(octets);
|
|
942
|
-
};
|
|
943
|
-
ipaddr.fromByteArray = function(bytes) {
|
|
944
|
-
const length = bytes.length;
|
|
945
|
-
if (length === 4) return new ipaddr.IPv4(bytes);
|
|
946
|
-
else if (length === 16) return new ipaddr.IPv6(bytes);
|
|
947
|
-
else throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address");
|
|
948
|
-
};
|
|
949
|
-
ipaddr.isValid = function(string) {
|
|
950
|
-
return ipaddr.IPv6.isValid(string) || ipaddr.IPv4.isValid(string);
|
|
951
|
-
};
|
|
952
|
-
ipaddr.isValidCIDR = function(string) {
|
|
953
|
-
return ipaddr.IPv6.isValidCIDR(string) || ipaddr.IPv4.isValidCIDR(string);
|
|
954
|
-
};
|
|
955
|
-
ipaddr.parse = function(string) {
|
|
956
|
-
if (ipaddr.IPv6.isValid(string)) return ipaddr.IPv6.parse(string);
|
|
957
|
-
else if (ipaddr.IPv4.isValid(string)) return ipaddr.IPv4.parse(string);
|
|
958
|
-
else throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format");
|
|
959
|
-
};
|
|
960
|
-
ipaddr.parseCIDR = function(string) {
|
|
961
|
-
try {
|
|
962
|
-
return ipaddr.IPv6.parseCIDR(string);
|
|
963
|
-
} catch (e) {
|
|
964
|
-
try {
|
|
965
|
-
return ipaddr.IPv4.parseCIDR(string);
|
|
966
|
-
} catch (e2) {
|
|
967
|
-
throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format");
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
};
|
|
971
|
-
ipaddr.process = function(string) {
|
|
972
|
-
const addr = this.parse(string);
|
|
973
|
-
if (addr.kind() === "ipv6" && addr.isIPv4MappedAddress()) return addr.toIPv4Address();
|
|
974
|
-
else return addr;
|
|
274
|
+
mutated = true;
|
|
275
|
+
}
|
|
276
|
+
const redactedQuery = redactQueryString(parsed.search.slice(1), depth);
|
|
277
|
+
if (redactedQuery !== parsed.search.slice(1)) {
|
|
278
|
+
parsed.search = redactedQuery;
|
|
279
|
+
mutated = true;
|
|
280
|
+
}
|
|
281
|
+
const fragment = parsed.hash.slice(1);
|
|
282
|
+
const redactedHash = redactFragment(fragment, depth + 1);
|
|
283
|
+
if (redactedHash !== fragment) {
|
|
284
|
+
parsed.hash = redactedHash;
|
|
285
|
+
mutated = true;
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
value: mutated ? parsed.toString() : value,
|
|
289
|
+
parsedWholeUrl: true
|
|
975
290
|
};
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
rangeSubnets = rangeList[rangeName];
|
|
981
|
-
if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) rangeSubnets = [rangeSubnets];
|
|
982
|
-
for (i = 0; i < rangeSubnets.length; i++) {
|
|
983
|
-
subnet = rangeSubnets[i];
|
|
984
|
-
if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) return rangeName;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
return defaultName;
|
|
291
|
+
} catch {
|
|
292
|
+
return {
|
|
293
|
+
value,
|
|
294
|
+
parsedWholeUrl: false
|
|
988
295
|
};
|
|
989
|
-
|
|
990
|
-
else root.ipaddr = ipaddr;
|
|
991
|
-
})(exports);
|
|
992
|
-
})))(), 1);
|
|
993
|
-
function normalizeOptionalString(value) {
|
|
994
|
-
if (typeof value !== "string") return;
|
|
995
|
-
return value.trim() || void 0;
|
|
296
|
+
}
|
|
996
297
|
}
|
|
997
|
-
function
|
|
998
|
-
|
|
298
|
+
function redactSensitiveUrlLikeStringAtDepth(value, depth) {
|
|
299
|
+
const redactedUrl = redactSensitiveUrlAtDepth(value, depth);
|
|
300
|
+
if (redactedUrl.parsedWholeUrl) return redactedUrl;
|
|
301
|
+
return {
|
|
302
|
+
value: redactEncodedUrlLikeString(redactRelativeUrlFragment(redactUrlLikeFallback(redactedUrl.value), depth), depth + 1),
|
|
303
|
+
parsedWholeUrl: false
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function redactEncodedUrlLikeString(value, depth) {
|
|
307
|
+
if (!looksLikeNestedUrlValue(value)) return value;
|
|
308
|
+
if (depth > MAX_NESTED_URL_REDACTION_DEPTH) return "***";
|
|
309
|
+
let decoded;
|
|
310
|
+
try {
|
|
311
|
+
decoded = decodeURIComponent(value);
|
|
312
|
+
} catch {
|
|
313
|
+
return "***";
|
|
314
|
+
}
|
|
315
|
+
if (decoded === value) return value;
|
|
316
|
+
const redactedDecoded = redactSensitiveUrlLikeStringAtDepth(decoded, depth + 1);
|
|
317
|
+
if (redactedDecoded.value !== decoded || redactedDecoded.parsedWholeUrl) return redactedDecoded.value === decoded ? value : redactedDecoded.value;
|
|
318
|
+
return hasUnresolvedEmbeddedUrlUserInfo(decoded) ? "***" : value;
|
|
319
|
+
}
|
|
320
|
+
function redactNestedUrlValue(value, depth) {
|
|
321
|
+
if (!looksLikeNestedUrlValue(value)) return value;
|
|
322
|
+
if (depth > MAX_NESTED_URL_REDACTION_DEPTH) return "***";
|
|
323
|
+
const direct = redactSensitiveUrlLikeStringAtDepth(value, depth);
|
|
324
|
+
if (direct.value !== value) return direct.value;
|
|
325
|
+
if (hasUnresolvedEmbeddedUrlUserInfo(value)) return "***";
|
|
326
|
+
if (direct.parsedWholeUrl) return value;
|
|
327
|
+
let decoded;
|
|
328
|
+
try {
|
|
329
|
+
decoded = decodeURIComponent(value);
|
|
330
|
+
} catch {
|
|
331
|
+
return "***";
|
|
332
|
+
}
|
|
333
|
+
if (decoded === value || !looksLikeNestedUrlValue(decoded)) return value;
|
|
334
|
+
const redactedDecoded = redactNestedUrlValue(decoded, depth + 1);
|
|
335
|
+
return redactedDecoded === decoded ? value : encodeURIComponent(redactedDecoded);
|
|
336
|
+
}
|
|
337
|
+
/** Redacts sensitive URL-looking substrings even when the full value is not a valid URL. */
|
|
338
|
+
function redactSensitiveUrlLikeString(value) {
|
|
339
|
+
return redactSensitiveUrlLikeStringAtDepth(value, 0).value;
|
|
999
340
|
}
|
|
1000
|
-
|
|
341
|
+
ipaddr.IPv4.parse("198.18.0.0");
|
|
1001
342
|
function stripIpv6Brackets(value) {
|
|
1002
343
|
if (value.startsWith("[") && value.endsWith("]")) return value.slice(1, -1);
|
|
1003
344
|
return value;
|
|
@@ -1020,13 +361,13 @@ function normalizeIpParseInput(raw) {
|
|
|
1020
361
|
function parseCanonicalIpAddress(raw) {
|
|
1021
362
|
const normalized = normalizeIpParseInput(raw);
|
|
1022
363
|
if (!normalized) return;
|
|
1023
|
-
return
|
|
364
|
+
return ipaddr.IPv4.isValidFourPartDecimal(normalized) || ipaddr.IPv6.isValid(normalized) ? ipaddr.parse(normalized) : void 0;
|
|
1024
365
|
}
|
|
1025
366
|
/** Normalizes canonical IP literals and maps IPv4-mapped IPv6 addresses to IPv4 text. */
|
|
1026
367
|
function normalizeIpAddress(raw) {
|
|
1027
368
|
const parsed = parseCanonicalIpAddress(raw);
|
|
1028
369
|
if (!parsed) return;
|
|
1029
|
-
return normalizeLowercaseStringOrEmpty
|
|
370
|
+
return normalizeLowercaseStringOrEmpty(normalizeIpv4MappedAddress(parsed).toString());
|
|
1030
371
|
}
|
|
1031
372
|
/** True when a canonical IP literal is loopback, including IPv4-mapped IPv6. */
|
|
1032
373
|
function isLoopbackIpAddress(raw) {
|
|
@@ -1035,28 +376,19 @@ function isLoopbackIpAddress(raw) {
|
|
|
1035
376
|
return normalizeIpv4MappedAddress(parsed).range() === "loopback";
|
|
1036
377
|
}
|
|
1037
378
|
//#endregion
|
|
1038
|
-
//#region packages/net-policy/src/url-protocol.ts
|
|
1039
|
-
function parseUrl(value) {
|
|
1040
|
-
if (value instanceof URL) return value;
|
|
1041
|
-
try {
|
|
1042
|
-
return new URL(value);
|
|
1043
|
-
} catch {
|
|
1044
|
-
return null;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
function isWssUrl(value) {
|
|
1048
|
-
return parseUrl(value)?.protocol === "wss:";
|
|
1049
|
-
}
|
|
1050
|
-
//#endregion
|
|
1051
379
|
//#region packages/gateway-client/src/client-address-utils.ts
|
|
1052
|
-
function
|
|
380
|
+
function normalizeGatewayErrorText(value) {
|
|
1053
381
|
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
1054
382
|
}
|
|
1055
383
|
function isSensitiveUrlQueryParamName(key) {
|
|
1056
384
|
return /(?:token|password|secret|key|auth|credential)/iu.test(key);
|
|
1057
385
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
386
|
+
const SHA256_HEX_FINGERPRINT = /^[a-fA-F0-9]{64}$/u;
|
|
387
|
+
const SHA256_COLON_FINGERPRINT = /^(?:[a-fA-F0-9]{2}:){31}[a-fA-F0-9]{2}$/u;
|
|
388
|
+
function normalizeTlsFingerprint(fingerprint) {
|
|
389
|
+
const value = (fingerprint ?? "").trim().replace(/^sha256:/iu, "");
|
|
390
|
+
if (SHA256_HEX_FINGERPRINT.test(value)) return value.toLowerCase();
|
|
391
|
+
return SHA256_COLON_FINGERPRINT.test(value) ? value.replaceAll(":", "").toLowerCase() : "";
|
|
1060
392
|
}
|
|
1061
393
|
function parseHostForAddressChecks(host) {
|
|
1062
394
|
if (!host) return null;
|
|
@@ -1076,34 +408,34 @@ function parseGatewayIpAddress(host) {
|
|
|
1076
408
|
return normalized ? parseCanonicalIpAddress(normalized) : void 0;
|
|
1077
409
|
}
|
|
1078
410
|
//#endregion
|
|
1079
|
-
//#region packages/gateway-client/src/
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
}
|
|
1091
|
-
publicKeyRawBase64UrlFromPem: () => {
|
|
1092
|
-
throw new Error("GatewayClient public key dependency is not configured");
|
|
1093
|
-
},
|
|
1094
|
-
loadDeviceAuthToken: () => null,
|
|
1095
|
-
storeDeviceAuthToken: () => {},
|
|
1096
|
-
clearDeviceAuthToken: () => {},
|
|
1097
|
-
beforeConnect: () => {},
|
|
1098
|
-
registerGatewayLoopbackBypass: () => void 0,
|
|
1099
|
-
logDebug: () => {},
|
|
1100
|
-
logError: () => {},
|
|
1101
|
-
redactForLog: (message) => message,
|
|
1102
|
-
normalizeTlsFingerprint: normalizeFingerprint
|
|
411
|
+
//#region packages/gateway-client/src/request-error.ts
|
|
412
|
+
var GatewayClientRequestError = class extends GatewayProtocolRequestError {
|
|
413
|
+
constructor(error) {
|
|
414
|
+
super({
|
|
415
|
+
...error,
|
|
416
|
+
message: formatConnectErrorMessage({
|
|
417
|
+
message: error.message,
|
|
418
|
+
details: error.details
|
|
419
|
+
})
|
|
420
|
+
});
|
|
421
|
+
this.name = "GatewayClientRequestError";
|
|
422
|
+
}
|
|
1103
423
|
};
|
|
1104
|
-
|
|
1105
|
-
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region packages/net-policy/src/url-protocol.ts
|
|
426
|
+
function parseUrl(value) {
|
|
427
|
+
if (value instanceof URL) return value;
|
|
428
|
+
try {
|
|
429
|
+
return new URL(value);
|
|
430
|
+
} catch {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
1106
433
|
}
|
|
434
|
+
function isWssUrl(value) {
|
|
435
|
+
return parseUrl(value)?.protocol === "wss:";
|
|
436
|
+
}
|
|
437
|
+
//#endregion
|
|
438
|
+
//#region packages/gateway-client/src/websocket-transport.ts
|
|
1107
439
|
const PRIVATE_OR_LOOPBACK_IPV4_RANGES = /* @__PURE__ */ new Set([
|
|
1108
440
|
"loopback",
|
|
1109
441
|
"private",
|
|
@@ -1119,19 +451,16 @@ const PRIVATE_OR_LOOPBACK_IPV6_RANGES = /* @__PURE__ */ new Set([
|
|
|
1119
451
|
function isPrivateOrLoopbackIpAddress(address) {
|
|
1120
452
|
return (address.kind() === "ipv4" ? PRIVATE_OR_LOOPBACK_IPV4_RANGES : PRIVATE_OR_LOOPBACK_IPV6_RANGES).has(address.range());
|
|
1121
453
|
}
|
|
1122
|
-
function
|
|
454
|
+
function isGatewayLoopbackHost(host) {
|
|
1123
455
|
const parsed = parseHostForAddressChecks(host);
|
|
1124
|
-
|
|
1125
|
-
if (parsed.isLocalhost) return true;
|
|
1126
|
-
return isLoopbackIpAddress(parsed.unbracketedHost);
|
|
456
|
+
return Boolean(parsed && (parsed.isLocalhost || isLoopbackIpAddress(parsed.unbracketedHost)));
|
|
1127
457
|
}
|
|
1128
458
|
function isPrivateOrLoopbackHost(host) {
|
|
1129
459
|
const parsed = parseHostForAddressChecks(host);
|
|
1130
460
|
if (!parsed) return false;
|
|
1131
461
|
if (parsed.isLocalhost) return true;
|
|
1132
462
|
const address = parseGatewayIpAddress(parsed.unbracketedHost);
|
|
1133
|
-
|
|
1134
|
-
return isPrivateOrLoopbackIpAddress(address);
|
|
463
|
+
return Boolean(address && isPrivateOrLoopbackIpAddress(address));
|
|
1135
464
|
}
|
|
1136
465
|
function isTrustedPlaintextWebSocketHost(hostname) {
|
|
1137
466
|
if (isPrivateOrLoopbackHost(hostname)) return true;
|
|
@@ -1144,7 +473,7 @@ function isSecureWebSocketUrl(rawUrl, options) {
|
|
|
1144
473
|
const protocol = url.protocol === "https:" ? "wss:" : url.protocol === "http:" ? "ws:" : url.protocol;
|
|
1145
474
|
if (protocol === "wss:") return true;
|
|
1146
475
|
if (protocol !== "ws:") return false;
|
|
1147
|
-
if (
|
|
476
|
+
if (isGatewayLoopbackHost(url.hostname) || isTrustedPlaintextWebSocketHost(url.hostname)) return true;
|
|
1148
477
|
if (options?.allowPrivateWs === true) {
|
|
1149
478
|
const hostForIpCheck = url.hostname.startsWith("[") && url.hostname.endsWith("]") ? url.hostname.slice(1, -1) : url.hostname;
|
|
1150
479
|
return isPrivateOrLoopbackHost(url.hostname) || parseGatewayIpAddress(hostForIpCheck) === void 0;
|
|
@@ -1154,37 +483,119 @@ function isSecureWebSocketUrl(rawUrl, options) {
|
|
|
1154
483
|
return false;
|
|
1155
484
|
}
|
|
1156
485
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
});
|
|
1168
|
-
this.name = "GatewayClientRequestError";
|
|
486
|
+
var GatewayWebSocketTransportConfigurationError = class extends Error {};
|
|
487
|
+
function resolveGatewayWebSocketTransport(params) {
|
|
488
|
+
const usesTls = isWssUrl(params.url);
|
|
489
|
+
if (params.tlsFingerprint && !usesTls) throw new GatewayWebSocketTransportConfigurationError("gateway tls fingerprint requires wss:// gateway url");
|
|
490
|
+
const allowPrivateWs = (params.env ?? process.env).OPENCLAW_ALLOW_INSECURE_PRIVATE_WS === "1";
|
|
491
|
+
if (!isSecureWebSocketUrl(params.url, { allowPrivateWs })) {
|
|
492
|
+
let displayHost = params.url;
|
|
493
|
+
try {
|
|
494
|
+
displayHost = new URL(params.url).hostname || params.url;
|
|
495
|
+
} catch {}
|
|
496
|
+
throw new GatewayWebSocketTransportConfigurationError(`SECURITY ERROR: Cannot connect to "${displayHost}" over plaintext ws://. Both credentials and chat data would be exposed to network interception. Use wss:// for remote URLs. Safe defaults: keep gateway.bind=loopback and connect via SSH tunnel (ssh -N -L 18789:127.0.0.1:18789 user@gateway-host), or use Tailscale Serve/Funnel. ` + (allowPrivateWs ? "" : "Break-glass (trusted private networks only): set OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1. ") + "Run `openclaw doctor --fix` for guidance.");
|
|
1169
497
|
}
|
|
498
|
+
const normalize = params.normalizeTlsFingerprint ?? normalizeTlsFingerprint;
|
|
499
|
+
const expectedFingerprint = params.tlsFingerprint ? normalizeTlsFingerprint(params.tlsFingerprint) : void 0;
|
|
500
|
+
if (params.tlsFingerprint && !expectedFingerprint) throw new GatewayWebSocketTransportConfigurationError("gateway tls fingerprint must be a SHA-256 fingerprint");
|
|
501
|
+
const options = { ...params.options };
|
|
502
|
+
if (usesTls && expectedFingerprint) {
|
|
503
|
+
options.rejectUnauthorized = false;
|
|
504
|
+
options.checkServerIdentity = (_hostValue, cert) => {
|
|
505
|
+
const fingerprintValue = typeof cert === "object" && cert && "fingerprint256" in cert ? cert.fingerprint256 ?? "" : "";
|
|
506
|
+
const canonicalFingerprint = normalizeTlsFingerprint(typeof fingerprintValue === "string" ? fingerprintValue : "");
|
|
507
|
+
const fingerprint = canonicalFingerprint ? normalize(canonicalFingerprint) : "";
|
|
508
|
+
const expected = normalize(expectedFingerprint);
|
|
509
|
+
if (!fingerprint) return /* @__PURE__ */ new Error("Missing server TLS fingerprint");
|
|
510
|
+
if (fingerprint !== expected) return /* @__PURE__ */ new Error("Server TLS fingerprint mismatch");
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
options,
|
|
515
|
+
validateSocket: (socket) => {
|
|
516
|
+
if (!params.tlsFingerprint) return null;
|
|
517
|
+
const expected = expectedFingerprint ? normalize(expectedFingerprint) : "";
|
|
518
|
+
if (!expected) return /* @__PURE__ */ new Error("gateway tls fingerprint missing");
|
|
519
|
+
const rawSocket = socket["_socket"];
|
|
520
|
+
if (!rawSocket || typeof rawSocket.getPeerCertificate !== "function") return /* @__PURE__ */ new Error("gateway tls fingerprint unavailable");
|
|
521
|
+
const canonicalFingerprint = normalizeTlsFingerprint(rawSocket.getPeerCertificate()?.fingerprint256 ?? "");
|
|
522
|
+
const fingerprint = canonicalFingerprint ? normalize(canonicalFingerprint) : "";
|
|
523
|
+
if (!fingerprint) return /* @__PURE__ */ new Error("gateway tls fingerprint unavailable");
|
|
524
|
+
return fingerprint === expected ? null : /* @__PURE__ */ new Error("gateway tls fingerprint mismatch");
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region packages/gateway-client/src/client.ts
|
|
530
|
+
const DEFAULT_HOST_DEPS = {
|
|
531
|
+
loadOrCreateDeviceIdentity: () => void 0,
|
|
532
|
+
signDevicePayload: () => {
|
|
533
|
+
throw new Error("GatewayClient device signature dependency is not configured");
|
|
534
|
+
},
|
|
535
|
+
publicKeyRawBase64UrlFromPem: () => {
|
|
536
|
+
throw new Error("GatewayClient public key dependency is not configured");
|
|
537
|
+
},
|
|
538
|
+
loadDeviceAuthToken: () => null,
|
|
539
|
+
storeDeviceAuthToken: () => {},
|
|
540
|
+
clearDeviceAuthToken: () => {},
|
|
541
|
+
beforeConnect: () => {},
|
|
542
|
+
registerGatewayLoopbackBypass: () => void 0,
|
|
543
|
+
logDebug: () => {},
|
|
544
|
+
logError: () => {},
|
|
545
|
+
redactForLog: (message) => message,
|
|
546
|
+
normalizeTlsFingerprint
|
|
1170
547
|
};
|
|
1171
|
-
|
|
548
|
+
function resolveHostDeps(overrides) {
|
|
549
|
+
return Object.fromEntries(Object.entries(DEFAULT_HOST_DEPS).map(([key, fallback]) => [key, overrides?.[key] ?? fallback]));
|
|
550
|
+
}
|
|
551
|
+
const DEFAULT_GATEWAY_CLIENT_URL = "ws://127.0.0.1:18789";
|
|
552
|
+
const DEFAULT_CLIENT_VERSION = "0.0.0";
|
|
553
|
+
const MAX_UPGRADE_ERROR_BODY_BYTES = 2 * 1024;
|
|
554
|
+
const UPGRADE_ERROR_BODY_TIMEOUT_MS = 1e3;
|
|
555
|
+
async function readUpgradeErrorBody(response) {
|
|
556
|
+
return await new Promise((resolve) => {
|
|
557
|
+
const chunks = [];
|
|
558
|
+
let totalBytes = 0;
|
|
559
|
+
let settled = false;
|
|
560
|
+
const finish = () => {
|
|
561
|
+
if (settled) return;
|
|
562
|
+
settled = true;
|
|
563
|
+
clearTimeout(timer);
|
|
564
|
+
response.off("data", onData);
|
|
565
|
+
response.off("end", finish);
|
|
566
|
+
response.off("error", finish);
|
|
567
|
+
response.off("aborted", finish);
|
|
568
|
+
resolve(Buffer.concat(chunks, totalBytes).toString("utf8").replace(/\s+/gu, " ").trim());
|
|
569
|
+
};
|
|
570
|
+
const stop = () => {
|
|
571
|
+
finish();
|
|
572
|
+
response.destroy();
|
|
573
|
+
};
|
|
574
|
+
const onData = (chunk) => {
|
|
575
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
576
|
+
const remaining = MAX_UPGRADE_ERROR_BODY_BYTES - totalBytes;
|
|
577
|
+
if (remaining > 0) {
|
|
578
|
+
const prefix = buffer.subarray(0, remaining);
|
|
579
|
+
chunks.push(prefix);
|
|
580
|
+
totalBytes += prefix.byteLength;
|
|
581
|
+
}
|
|
582
|
+
if (buffer.byteLength >= remaining) stop();
|
|
583
|
+
};
|
|
584
|
+
const timer = setTimeout(stop, UPGRADE_ERROR_BODY_TIMEOUT_MS);
|
|
585
|
+
timer.unref?.();
|
|
586
|
+
response.on("data", onData);
|
|
587
|
+
response.once("end", finish);
|
|
588
|
+
response.once("error", finish);
|
|
589
|
+
response.once("aborted", finish);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
var GatewayClientRequestTimeoutError = class extends GatewayProtocolRequestTimeoutError {
|
|
1172
593
|
constructor(params) {
|
|
1173
|
-
super(`gateway request timeout for ${params.method}`);
|
|
594
|
+
super(params, `gateway request timeout for ${params.method}`);
|
|
1174
595
|
this.name = "GatewayClientRequestTimeoutError";
|
|
1175
|
-
this.method = params.method;
|
|
1176
|
-
this.timeoutMs = params.timeoutMs;
|
|
1177
|
-
this.requestSent = params.requestSent;
|
|
1178
|
-
}
|
|
1179
|
-
};
|
|
1180
|
-
var GatewayClientTransientPreHelloCloseError = class extends Error {
|
|
1181
|
-
constructor() {
|
|
1182
|
-
super("gateway transient pre-hello clean close");
|
|
1183
|
-
this.name = "GatewayClientTransientPreHelloCloseError";
|
|
1184
596
|
}
|
|
1185
597
|
};
|
|
1186
|
-
var
|
|
1187
|
-
var GatewayClientTransportPolicyError = class extends GatewayClientSocketFactoryConfigurationError {};
|
|
598
|
+
var GatewayClientTransportPolicyError = class extends GatewayWebSocketTransportConfigurationError {};
|
|
1188
599
|
const GATEWAY_CONNECT_ASSEMBLY_ERROR = Symbol("gateway.connectAssemblyError");
|
|
1189
600
|
function markGatewayConnectAssemblyError(error) {
|
|
1190
601
|
Object.defineProperty(error, GATEWAY_CONNECT_ASSEMBLY_ERROR, {
|
|
@@ -1206,10 +617,20 @@ function formatGatewayClientErrorForLog(err) {
|
|
|
1206
617
|
const FORCE_STOP_TERMINATE_GRACE_MS = 250;
|
|
1207
618
|
const STOP_AND_WAIT_TIMEOUT_MS = 1e3;
|
|
1208
619
|
const MAX_SUPPRESSED_TRANSIENT_PRE_HELLO_CLEAN_CLOSES = 1;
|
|
620
|
+
function resolveLegacyNodePlatform(platform) {
|
|
621
|
+
switch (platform) {
|
|
622
|
+
case "macos": return "darwin";
|
|
623
|
+
case "windows": return "win32";
|
|
624
|
+
default: return;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
1209
627
|
var GatewayClient = class {
|
|
1210
628
|
constructor(opts) {
|
|
1211
629
|
this.ws = null;
|
|
1212
630
|
this.stopped = false;
|
|
631
|
+
this.useLegacyNodeProtocolEnvelope = false;
|
|
632
|
+
this.nodeProtocolTransitionPending = false;
|
|
633
|
+
this.suppressNextHelloCallback = false;
|
|
1213
634
|
this.pendingDeviceTokenRetry = false;
|
|
1214
635
|
this.deviceTokenRetryBudgetUsed = false;
|
|
1215
636
|
this.approvalRuntimeTokenCompatibilityDisabled = false;
|
|
@@ -1225,7 +646,7 @@ var GatewayClient = class {
|
|
|
1225
646
|
...opts,
|
|
1226
647
|
deviceIdentity: opts.deviceIdentity === null ? void 0 : opts.deviceIdentity ?? this.deps.loadOrCreateDeviceIdentity()
|
|
1227
648
|
};
|
|
1228
|
-
this.requestTimeoutMs = typeof opts.requestTimeoutMs === "number" && Number.isFinite(opts.requestTimeoutMs) ?
|
|
649
|
+
this.requestTimeoutMs = typeof opts.requestTimeoutMs === "number" && Number.isFinite(opts.requestTimeoutMs) ? opts.requestTimeoutMs : DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS;
|
|
1229
650
|
const connectChallengeTimeoutMs = resolveConnectChallengeTimeoutMs(this.opts.connectChallengeTimeoutMs, {
|
|
1230
651
|
env: this.opts.env,
|
|
1231
652
|
configuredTimeoutMs: this.opts.preauthHandshakeTimeoutMs
|
|
@@ -1264,7 +685,13 @@ var GatewayClient = class {
|
|
|
1264
685
|
};
|
|
1265
686
|
},
|
|
1266
687
|
onConnectHello: (hello, context) => this.handleConnectHello(hello, context.plan),
|
|
1267
|
-
onHello: (hello) =>
|
|
688
|
+
onHello: (hello) => {
|
|
689
|
+
if (this.suppressNextHelloCallback) {
|
|
690
|
+
this.suppressNextHelloCallback = false;
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
this.opts.onHelloOk?.(hello);
|
|
694
|
+
},
|
|
1268
695
|
onConnectFailure: (error, context) => this.handleConnectRequestFailure(error, context.plan),
|
|
1269
696
|
resolveClose: (context) => this.resolveClose(context),
|
|
1270
697
|
onClose: (context, decision) => {
|
|
@@ -1294,7 +721,7 @@ var GatewayClient = class {
|
|
|
1294
721
|
maxMs: 3e4
|
|
1295
722
|
},
|
|
1296
723
|
requestTimeoutMs: this.requestTimeoutMs,
|
|
1297
|
-
shouldRetrySocketFactoryError: (error) => !(error instanceof
|
|
724
|
+
shouldRetrySocketFactoryError: (error) => !(error instanceof GatewayWebSocketTransportConfigurationError) && !(error instanceof SyntaxError) && !(error instanceof TypeError) && !(error instanceof RangeError),
|
|
1298
725
|
rethrowSocketFactoryError: (error) => error instanceof GatewayClientTransportPolicyError
|
|
1299
726
|
});
|
|
1300
727
|
}
|
|
@@ -1310,7 +737,9 @@ var GatewayClient = class {
|
|
|
1310
737
|
this.opts = {
|
|
1311
738
|
...this.opts,
|
|
1312
739
|
caps: [...manifest.caps],
|
|
1313
|
-
commands: [...manifest.commands]
|
|
740
|
+
commands: [...manifest.commands],
|
|
741
|
+
computerUse: manifest.computerUse === void 0 ? void 0 : structuredClone(manifest.computerUse),
|
|
742
|
+
workerRuns: manifest.workerRuns ? structuredClone(manifest.workerRuns) : void 0
|
|
1314
743
|
};
|
|
1315
744
|
if (!this.stopped) this.protocol.closeSocket(1012, "node manifest changed");
|
|
1316
745
|
}
|
|
@@ -1320,36 +749,29 @@ var GatewayClient = class {
|
|
|
1320
749
|
}
|
|
1321
750
|
createSocket(handlers) {
|
|
1322
751
|
const url = this.opts.url ?? DEFAULT_GATEWAY_CLIENT_URL;
|
|
1323
|
-
const
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
752
|
+
const configuredEdgeAuthHeaders = this.opts.edgeAuthHeaders;
|
|
753
|
+
const edgeAuthHeaders = configuredEdgeAuthHeaders && Object.keys(configuredEdgeAuthHeaders).length > 0 ? configuredEdgeAuthHeaders : void 0;
|
|
754
|
+
if (edgeAuthHeaders && new URL(url).protocol !== "wss:") throw new GatewayWebSocketTransportConfigurationError("edge auth headers require a wss:// Gateway URL");
|
|
755
|
+
const handshakeTimeoutMs = resolvePreauthHandshakeTimeoutMs({
|
|
756
|
+
env: this.opts.env,
|
|
757
|
+
configuredTimeoutMs: this.opts.preauthHandshakeTimeoutMs
|
|
758
|
+
});
|
|
759
|
+
const transport = resolveGatewayWebSocketTransport({
|
|
760
|
+
url,
|
|
761
|
+
tlsFingerprint: this.opts.tlsFingerprint,
|
|
762
|
+
env: this.opts.env,
|
|
763
|
+
normalizeTlsFingerprint: this.deps.normalizeTlsFingerprint,
|
|
764
|
+
options: {
|
|
765
|
+
maxPayload: 25 * 1024 * 1024,
|
|
766
|
+
handshakeTimeout: handshakeTimeoutMs,
|
|
767
|
+
...this.opts.origin ? { origin: this.opts.origin } : {},
|
|
768
|
+
...edgeAuthHeaders ? {
|
|
769
|
+
followRedirects: false,
|
|
770
|
+
headers: edgeAuthHeaders
|
|
771
|
+
} : {}
|
|
772
|
+
}
|
|
773
|
+
});
|
|
1333
774
|
this.deps.beforeConnect();
|
|
1334
|
-
const wsOptions = {
|
|
1335
|
-
maxPayload: 25 * 1024 * 1024,
|
|
1336
|
-
handshakeTimeout: resolvePreauthHandshakeTimeoutMs({
|
|
1337
|
-
env: this.opts.env,
|
|
1338
|
-
configuredTimeoutMs: this.opts.preauthHandshakeTimeoutMs
|
|
1339
|
-
}),
|
|
1340
|
-
...this.opts.origin ? { origin: this.opts.origin } : {}
|
|
1341
|
-
};
|
|
1342
|
-
if (usesTls && this.opts.tlsFingerprint) {
|
|
1343
|
-
wsOptions.rejectUnauthorized = false;
|
|
1344
|
-
wsOptions.checkServerIdentity = (_hostValue, cert) => {
|
|
1345
|
-
const fingerprintValue = typeof cert === "object" && cert && "fingerprint256" in cert ? cert.fingerprint256 ?? "" : "";
|
|
1346
|
-
const fingerprint = this.deps.normalizeTlsFingerprint(typeof fingerprintValue === "string" ? fingerprintValue : "");
|
|
1347
|
-
const expected = this.deps.normalizeTlsFingerprint(this.opts.tlsFingerprint ?? "");
|
|
1348
|
-
if (!expected) return;
|
|
1349
|
-
if (!fingerprint) return /* @__PURE__ */ new Error("Missing server TLS fingerprint");
|
|
1350
|
-
if (fingerprint !== expected) return /* @__PURE__ */ new Error("Server TLS fingerprint mismatch");
|
|
1351
|
-
};
|
|
1352
|
-
}
|
|
1353
775
|
let ws;
|
|
1354
776
|
let unregisterGatewayLoopbackBypass;
|
|
1355
777
|
try {
|
|
@@ -1358,7 +780,7 @@ var GatewayClient = class {
|
|
|
1358
780
|
throw new GatewayClientTransportPolicyError(error instanceof Error ? error.message : String(error));
|
|
1359
781
|
}
|
|
1360
782
|
try {
|
|
1361
|
-
ws = new WebSocket(url,
|
|
783
|
+
ws = new WebSocket(url, transport.options);
|
|
1362
784
|
ws.binaryType = "nodebuffer";
|
|
1363
785
|
} catch (error) {
|
|
1364
786
|
throw error instanceof Error ? error : new Error(String(error));
|
|
@@ -1367,15 +789,14 @@ var GatewayClient = class {
|
|
|
1367
789
|
}
|
|
1368
790
|
this.ws = ws;
|
|
1369
791
|
this.transportValidated = false;
|
|
792
|
+
let upgradeError;
|
|
1370
793
|
ws.on("open", () => {
|
|
1371
794
|
handlers.open();
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
return;
|
|
1378
|
-
}
|
|
795
|
+
const tlsError = transport.validateSocket(ws);
|
|
796
|
+
if (tlsError) {
|
|
797
|
+
handlers.error(tlsError);
|
|
798
|
+
ws.close(1008, tlsError.message);
|
|
799
|
+
return;
|
|
1379
800
|
}
|
|
1380
801
|
this.transportValidated = true;
|
|
1381
802
|
});
|
|
@@ -1386,7 +807,28 @@ var GatewayClient = class {
|
|
|
1386
807
|
this.resolvePendingStop(ws);
|
|
1387
808
|
handlers.close(code, reasonText);
|
|
1388
809
|
});
|
|
810
|
+
ws.on("unexpected-response", (request, response) => {
|
|
811
|
+
readUpgradeErrorBody(response).then((body) => {
|
|
812
|
+
const statusCode = response.statusCode;
|
|
813
|
+
const rawLocation = response.headers.location;
|
|
814
|
+
const location = rawLocation ? redactSensitiveUrlLikeString(Array.isArray(rawLocation) ? rawLocation[0] ?? "" : rawLocation) : void 0;
|
|
815
|
+
upgradeError = new GatewayClientRequestError({
|
|
816
|
+
code: "UNAVAILABLE",
|
|
817
|
+
message: `gateway rejected websocket upgrade (HTTP ${statusCode ?? "unknown"})${body ? `: ${body}` : ""}`,
|
|
818
|
+
retryable: true,
|
|
819
|
+
details: {
|
|
820
|
+
reason: "websocket-upgrade-rejected",
|
|
821
|
+
...statusCode === void 0 ? {} : { httpStatus: statusCode },
|
|
822
|
+
...location ? { location } : {}
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
handlers.error(upgradeError);
|
|
826
|
+
request.destroy();
|
|
827
|
+
ws.close();
|
|
828
|
+
});
|
|
829
|
+
});
|
|
1389
830
|
ws.on("error", (err) => {
|
|
831
|
+
if (upgradeError) return;
|
|
1390
832
|
this.logDebug(`gateway client error: ${formatGatewayClientErrorForLog(err)}`);
|
|
1391
833
|
handlers.error(err instanceof Error ? err : new Error(String(err)));
|
|
1392
834
|
});
|
|
@@ -1486,22 +928,34 @@ var GatewayClient = class {
|
|
|
1486
928
|
storedScopes,
|
|
1487
929
|
defaultScopes: ["operator.admin"]
|
|
1488
930
|
});
|
|
1489
|
-
const
|
|
931
|
+
const clientMode = this.opts.mode ?? GATEWAY_CLIENT_MODES.BACKEND;
|
|
932
|
+
const clientId = this.opts.clientName ?? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT;
|
|
933
|
+
const isBuiltInNodeHost = role === "node" && clientMode === GATEWAY_CLIENT_MODES.NODE && clientId === GATEWAY_CLIENT_NAMES.NODE_HOST;
|
|
934
|
+
const negotiatesNodeProtocol = this.shouldNegotiateLegacyNodeProtocol();
|
|
935
|
+
const useLegacyNodeProtocolEnvelope = isBuiltInNodeHost && (this.useLegacyNodeProtocolEnvelope || this.opts.maxProtocol === MIN_NODE_PROTOCOL_VERSION && (this.opts.minProtocol ?? MIN_NODE_PROTOCOL_VERSION) <= MIN_NODE_PROTOCOL_VERSION);
|
|
936
|
+
const minProtocol = useLegacyNodeProtocolEnvelope ? MIN_NODE_PROTOCOL_VERSION : negotiatesNodeProtocol ? PROTOCOL_VERSION : this.opts.minProtocol ?? (clientMode === GATEWAY_CLIENT_MODES.PROBE ? MIN_PROBE_PROTOCOL_VERSION : role === "node" && clientMode === GATEWAY_CLIENT_MODES.NODE ? MIN_NODE_PROTOCOL_VERSION : MIN_CLIENT_PROTOCOL_VERSION);
|
|
937
|
+
const maxProtocol = useLegacyNodeProtocolEnvelope ? MIN_NODE_PROTOCOL_VERSION : negotiatesNodeProtocol ? PROTOCOL_VERSION : this.opts.maxProtocol ?? PROTOCOL_VERSION;
|
|
938
|
+
const configuredPlatform = this.opts.platform ?? process.platform;
|
|
939
|
+
const platform = useLegacyNodeProtocolEnvelope ? resolveLegacyNodePlatform(configuredPlatform) ?? configuredPlatform : configuredPlatform;
|
|
940
|
+
const deviceFamily = useLegacyNodeProtocolEnvelope ? void 0 : this.opts.deviceFamily;
|
|
1490
941
|
return {
|
|
1491
942
|
params: {
|
|
1492
|
-
minProtocol
|
|
1493
|
-
maxProtocol
|
|
943
|
+
minProtocol,
|
|
944
|
+
maxProtocol,
|
|
1494
945
|
client: {
|
|
1495
|
-
id:
|
|
946
|
+
id: clientId,
|
|
1496
947
|
displayName: this.opts.clientDisplayName,
|
|
1497
948
|
version: this.opts.clientVersion ?? DEFAULT_CLIENT_VERSION,
|
|
949
|
+
buildId: this.opts.clientBuildId,
|
|
1498
950
|
platform,
|
|
1499
|
-
deviceFamily
|
|
1500
|
-
mode:
|
|
951
|
+
deviceFamily,
|
|
952
|
+
mode: clientMode,
|
|
1501
953
|
instanceId: this.opts.instanceId
|
|
1502
954
|
},
|
|
1503
955
|
caps: Array.isArray(this.opts.caps) ? this.opts.caps : [],
|
|
1504
956
|
commands: Array.isArray(this.opts.commands) ? this.opts.commands : void 0,
|
|
957
|
+
computerUse: useLegacyNodeProtocolEnvelope ? void 0 : this.opts.computerUse,
|
|
958
|
+
workerRuns: useLegacyNodeProtocolEnvelope ? void 0 : this.opts.workerRuns,
|
|
1505
959
|
permissions: this.opts.permissions && typeof this.opts.permissions === "object" ? this.opts.permissions : void 0,
|
|
1506
960
|
pathEnv: this.opts.pathEnv,
|
|
1507
961
|
auth,
|
|
@@ -1513,30 +967,47 @@ var GatewayClient = class {
|
|
|
1513
967
|
scopes,
|
|
1514
968
|
signatureToken,
|
|
1515
969
|
signedAtMs,
|
|
1516
|
-
platform
|
|
970
|
+
platform,
|
|
971
|
+
deviceFamily,
|
|
972
|
+
clientMode
|
|
1517
973
|
})
|
|
1518
974
|
},
|
|
1519
975
|
authApprovalRuntimeToken,
|
|
1520
976
|
authAgentRuntimeIdentityToken,
|
|
1521
977
|
resolvedDeviceToken,
|
|
978
|
+
storedScopes,
|
|
1522
979
|
storedToken,
|
|
1523
980
|
usingStoredDeviceToken
|
|
1524
981
|
};
|
|
1525
982
|
}
|
|
983
|
+
shouldNegotiateLegacyNodeProtocol() {
|
|
984
|
+
if (this.opts.role !== "node" || this.opts.mode !== GATEWAY_CLIENT_MODES.NODE || this.opts.clientName !== GATEWAY_CLIENT_NAMES.NODE_HOST) return false;
|
|
985
|
+
return (this.opts.minProtocol ?? MIN_NODE_PROTOCOL_VERSION) === MIN_NODE_PROTOCOL_VERSION && (this.opts.maxProtocol ?? PROTOCOL_VERSION) === PROTOCOL_VERSION;
|
|
986
|
+
}
|
|
987
|
+
shouldRetryWithLegacyNodeProtocol(error) {
|
|
988
|
+
if (this.useLegacyNodeProtocolEnvelope || !this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
|
|
989
|
+
const detailCode = readConnectErrorDetailCode(error.details);
|
|
990
|
+
return error.details?.expectedProtocol === MIN_NODE_PROTOCOL_VERSION && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeGatewayErrorText(error.message).includes("protocol mismatch"));
|
|
991
|
+
}
|
|
992
|
+
shouldRetryWithCurrentNodeProtocol(error) {
|
|
993
|
+
if (!this.useLegacyNodeProtocolEnvelope || !this.shouldNegotiateLegacyNodeProtocol() || !(error instanceof GatewayClientRequestError)) return false;
|
|
994
|
+
const detailCode = readConnectErrorDetailCode(error.details);
|
|
995
|
+
return error.details?.expectedProtocol === PROTOCOL_VERSION && (detailCode === ConnectErrorDetailCodes.PROTOCOL_MISMATCH || normalizeGatewayErrorText(error.message).includes("protocol mismatch"));
|
|
996
|
+
}
|
|
1526
997
|
buildDeviceConnectParams(params) {
|
|
1527
998
|
if (!this.opts.deviceIdentity) return;
|
|
1528
|
-
const { nonce, role, scopes, signatureToken, signedAtMs, platform } = params;
|
|
999
|
+
const { nonce, role, scopes, signatureToken, signedAtMs, platform, deviceFamily, clientMode } = params;
|
|
1529
1000
|
const payload = buildDeviceAuthPayloadV3({
|
|
1530
1001
|
deviceId: this.opts.deviceIdentity.deviceId,
|
|
1531
1002
|
clientId: this.opts.clientName ?? GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
|
|
1532
|
-
clientMode
|
|
1003
|
+
clientMode,
|
|
1533
1004
|
role,
|
|
1534
1005
|
scopes,
|
|
1535
1006
|
signedAtMs,
|
|
1536
1007
|
token: signatureToken ?? null,
|
|
1537
1008
|
nonce,
|
|
1538
1009
|
platform,
|
|
1539
|
-
deviceFamily
|
|
1010
|
+
deviceFamily
|
|
1540
1011
|
});
|
|
1541
1012
|
const signature = this.deps.signDevicePayload(this.opts.deviceIdentity.privateKeyPem, payload);
|
|
1542
1013
|
return {
|
|
@@ -1548,24 +1019,67 @@ var GatewayClient = class {
|
|
|
1548
1019
|
};
|
|
1549
1020
|
}
|
|
1550
1021
|
handleConnectHello(helloOk, assembled) {
|
|
1022
|
+
const reconnectWithCurrentNodeProtocol = this.useLegacyNodeProtocolEnvelope && this.shouldNegotiateLegacyNodeProtocol() && helloOk.protocol > MIN_NODE_PROTOCOL_VERSION;
|
|
1023
|
+
if (reconnectWithCurrentNodeProtocol) this.useLegacyNodeProtocolEnvelope = false;
|
|
1024
|
+
this.nodeProtocolTransitionPending = false;
|
|
1551
1025
|
this.pendingDeviceTokenRetry = false;
|
|
1552
1026
|
this.deviceTokenRetryBudgetUsed = false;
|
|
1553
1027
|
this.suppressedTransientPreHelloCleanCloses = 0;
|
|
1554
1028
|
const role = this.opts.role ?? "operator";
|
|
1555
1029
|
const authInfo = helloOk.auth;
|
|
1556
|
-
if (authInfo?.deviceToken && this.opts.deviceIdentity)
|
|
1557
|
-
|
|
1558
|
-
role: authInfo.
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1030
|
+
if (authInfo?.deviceToken && this.opts.deviceIdentity) {
|
|
1031
|
+
const tokenRole = authInfo.role ?? role;
|
|
1032
|
+
const scopes = tokenRole === role && authInfo.deviceToken === assembled.storedToken ? assembled.storedScopes ?? authInfo.scopes ?? [] : authInfo.scopes ?? [];
|
|
1033
|
+
this.deps.storeDeviceAuthToken({
|
|
1034
|
+
deviceId: this.opts.deviceIdentity.deviceId,
|
|
1035
|
+
role: tokenRole,
|
|
1036
|
+
token: authInfo.deviceToken,
|
|
1037
|
+
scopes,
|
|
1038
|
+
env: this.opts.env
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
if (this.opts.preferBootstrapToken) {
|
|
1042
|
+
this.opts.token = void 0;
|
|
1043
|
+
this.opts.bootstrapToken = void 0;
|
|
1044
|
+
this.opts.password = void 0;
|
|
1045
|
+
this.opts.preferBootstrapToken = false;
|
|
1046
|
+
}
|
|
1563
1047
|
this.tickIntervalMs = typeof helloOk.policy?.tickIntervalMs === "number" ? helloOk.policy.tickIntervalMs : 3e4;
|
|
1048
|
+
if (reconnectWithCurrentNodeProtocol) {
|
|
1049
|
+
this.suppressNextHelloCallback = true;
|
|
1050
|
+
this.protocol.resetReconnectBackoff(250);
|
|
1051
|
+
this.protocol.closeSocket(1012, "gateway protocol upgraded");
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1564
1054
|
this.lastTick = Date.now();
|
|
1565
1055
|
this.startTickWatch();
|
|
1566
1056
|
}
|
|
1567
1057
|
handleConnectRequestFailure(error, assembled) {
|
|
1058
|
+
if (this.shouldRetryWithCurrentNodeProtocol(error)) {
|
|
1059
|
+
const resetBackoff = !this.nodeProtocolTransitionPending;
|
|
1060
|
+
this.useLegacyNodeProtocolEnvelope = false;
|
|
1061
|
+
this.nodeProtocolTransitionPending = true;
|
|
1062
|
+
if (resetBackoff) this.protocol.resetReconnectBackoff(250);
|
|
1063
|
+
this.logDebug("gateway rejected protocol v3; retrying node host with protocol v4");
|
|
1064
|
+
return {
|
|
1065
|
+
closeCode: 1008,
|
|
1066
|
+
closeReason: "connect retry"
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
if (this.shouldRetryWithLegacyNodeProtocol(error)) {
|
|
1070
|
+
const resetBackoff = !this.nodeProtocolTransitionPending;
|
|
1071
|
+
this.useLegacyNodeProtocolEnvelope = true;
|
|
1072
|
+
this.nodeProtocolTransitionPending = true;
|
|
1073
|
+
if (resetBackoff) this.protocol.resetReconnectBackoff(250);
|
|
1074
|
+
this.logDebug("gateway rejected protocol v4; retrying node host with protocol v3");
|
|
1075
|
+
return {
|
|
1076
|
+
closeCode: 1008,
|
|
1077
|
+
closeReason: "connect retry"
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
this.nodeProtocolTransitionPending = false;
|
|
1568
1081
|
const role = this.opts.role ?? "operator";
|
|
1082
|
+
const detailCode = error instanceof GatewayClientRequestError ? readConnectErrorDetailCode(error.details) : null;
|
|
1569
1083
|
const shouldRetryWithDeviceToken = shouldRetryGatewayWithDeviceToken({
|
|
1570
1084
|
retryBudgetUsed: this.deviceTokenRetryBudgetUsed,
|
|
1571
1085
|
currentDeviceToken: assembled.resolvedDeviceToken,
|
|
@@ -1574,7 +1088,7 @@ var GatewayClient = class {
|
|
|
1574
1088
|
trustedEndpoint: this.isTrustedDeviceRetryEndpoint(),
|
|
1575
1089
|
errorDetails: error instanceof GatewayClientRequestError ? error.details : void 0
|
|
1576
1090
|
});
|
|
1577
|
-
if (this.opts.deviceIdentity && assembled.usingStoredDeviceToken &&
|
|
1091
|
+
if (this.opts.deviceIdentity && assembled.usingStoredDeviceToken && detailCode === ConnectErrorDetailCodes.AUTH_DEVICE_TOKEN_MISMATCH) {
|
|
1578
1092
|
const deviceId = this.opts.deviceIdentity.deviceId;
|
|
1579
1093
|
try {
|
|
1580
1094
|
this.deps.clearDeviceAuthToken({
|
|
@@ -1630,7 +1144,7 @@ var GatewayClient = class {
|
|
|
1630
1144
|
}
|
|
1631
1145
|
this.notifyConnectError(error);
|
|
1632
1146
|
const message = `gateway connect failed: ${formatGatewayClientErrorForLog(error)}`;
|
|
1633
|
-
if (this.opts.mode === GATEWAY_CLIENT_MODES.PROBE || isGatewayClientStoppedError(error)) this.logDebug(message);
|
|
1147
|
+
if (this.opts.mode === GATEWAY_CLIENT_MODES.PROBE || isGatewayClientStoppedError(error) || detailCode === ConnectErrorDetailCodes.AUTH_RATE_LIMITED) this.logDebug(message);
|
|
1634
1148
|
else this.logError(message);
|
|
1635
1149
|
return {
|
|
1636
1150
|
closeCode: 1008,
|
|
@@ -1643,7 +1157,7 @@ var GatewayClient = class {
|
|
|
1643
1157
|
const details = context.connectFailure?.error instanceof GatewayClientRequestError ? context.connectFailure.error.details : void 0;
|
|
1644
1158
|
if (context.code === 1013 && context.connectFailure?.reconnectDelayMs !== void 0) return {
|
|
1645
1159
|
retry: true,
|
|
1646
|
-
notify:
|
|
1160
|
+
notify: this.opts.notifyOnStartupRetry === true,
|
|
1647
1161
|
reconnectDelayMs: context.connectFailure.reconnectDelayMs
|
|
1648
1162
|
};
|
|
1649
1163
|
if (info.transientPreHelloCleanClose && this.suppressedTransientPreHelloCleanCloses < MAX_SUPPRESSED_TRANSIENT_PRE_HELLO_CLEAN_CLOSES) {
|
|
@@ -1651,7 +1165,7 @@ var GatewayClient = class {
|
|
|
1651
1165
|
return {
|
|
1652
1166
|
retry: true,
|
|
1653
1167
|
notify: true,
|
|
1654
|
-
pendingError: new
|
|
1168
|
+
pendingError: /* @__PURE__ */ new Error("gateway transient pre-hello clean close")
|
|
1655
1169
|
};
|
|
1656
1170
|
}
|
|
1657
1171
|
if (info.transientPreHelloCleanClose || context.connectRequestSent && !context.helloReceived && !context.connectFailure) {
|
|
@@ -1664,6 +1178,7 @@ var GatewayClient = class {
|
|
|
1664
1178
|
details,
|
|
1665
1179
|
deviceTokenRetryPending: this.pendingDeviceTokenRetry,
|
|
1666
1180
|
tokenMismatchIsTerminal: true,
|
|
1181
|
+
protocolMismatchIsTerminal: !this.nodeProtocolTransitionPending,
|
|
1667
1182
|
clientVersionMismatchIsTerminal: true
|
|
1668
1183
|
})) {
|
|
1669
1184
|
this.notifyReconnectPaused({
|
|
@@ -1687,11 +1202,13 @@ var GatewayClient = class {
|
|
|
1687
1202
|
phase: context.helloReceived ? "post-hello" : "pre-hello",
|
|
1688
1203
|
socketOpened: context.socketOpened,
|
|
1689
1204
|
transportValidated: this.transportValidated,
|
|
1690
|
-
|
|
1205
|
+
connectRequestSent: context.connectRequestSent,
|
|
1206
|
+
transientPreHelloCleanClose: !context.helloReceived && context.code === 1e3 && context.reason === "",
|
|
1207
|
+
...context.connectFailure?.error ? { connectError: context.connectFailure.error } : {}
|
|
1691
1208
|
};
|
|
1692
1209
|
}
|
|
1693
1210
|
clearStaleDeviceTokenForClose(code, reason) {
|
|
1694
|
-
if (code !== 1008 || !
|
|
1211
|
+
if (code !== 1008 || !normalizeGatewayErrorText(reason).includes("device token mismatch") || this.opts.token || this.opts.password || !this.opts.deviceIdentity) return;
|
|
1695
1212
|
const deviceId = this.opts.deviceIdentity.deviceId;
|
|
1696
1213
|
const role = this.opts.role ?? "operator";
|
|
1697
1214
|
try {
|
|
@@ -1724,14 +1241,14 @@ var GatewayClient = class {
|
|
|
1724
1241
|
if (!params.authApprovalRuntimeToken) return false;
|
|
1725
1242
|
if (!(params.error instanceof GatewayClientRequestError)) return false;
|
|
1726
1243
|
if (params.error.gatewayCode !== "INVALID_REQUEST") return false;
|
|
1727
|
-
const message =
|
|
1244
|
+
const message = normalizeGatewayErrorText(params.error.message);
|
|
1728
1245
|
return message.includes("invalid connect params") && message.includes("approvalruntimetoken");
|
|
1729
1246
|
}
|
|
1730
1247
|
shouldFailClosedForUnsupportedAgentRuntimeIdentity(params) {
|
|
1731
1248
|
if (!params.authAgentRuntimeIdentityToken) return false;
|
|
1732
1249
|
if (!(params.error instanceof GatewayClientRequestError)) return false;
|
|
1733
1250
|
if (params.error.gatewayCode !== "INVALID_REQUEST") return false;
|
|
1734
|
-
const message =
|
|
1251
|
+
const message = normalizeGatewayErrorText(params.error.message);
|
|
1735
1252
|
return message.includes("invalid connect params") && message.includes("agentruntimeidentitytoken");
|
|
1736
1253
|
}
|
|
1737
1254
|
isTrustedDeviceRetryEndpoint() {
|
|
@@ -1739,7 +1256,7 @@ var GatewayClient = class {
|
|
|
1739
1256
|
try {
|
|
1740
1257
|
const parsed = new URL(rawUrl);
|
|
1741
1258
|
const protocol = parsed.protocol === "https:" ? "wss:" : parsed.protocol === "http:" ? "ws:" : parsed.protocol;
|
|
1742
|
-
if (
|
|
1259
|
+
if (isGatewayLoopbackHost(parsed.hostname)) return true;
|
|
1743
1260
|
return protocol === "wss:" && Boolean(this.opts.tlsFingerprint?.trim());
|
|
1744
1261
|
} catch {
|
|
1745
1262
|
return false;
|
|
@@ -1754,6 +1271,7 @@ var GatewayClient = class {
|
|
|
1754
1271
|
return selectGatewayConnectAuth({
|
|
1755
1272
|
token: this.opts.token,
|
|
1756
1273
|
bootstrapToken: this.opts.bootstrapToken,
|
|
1274
|
+
preferBootstrapToken: this.opts.preferBootstrapToken,
|
|
1757
1275
|
deviceToken: this.opts.deviceToken,
|
|
1758
1276
|
password: this.opts.password,
|
|
1759
1277
|
approvalRuntimeToken: this.approvalRuntimeTokenCompatibilityDisabled ? void 0 : this.opts.approvalRuntimeToken,
|
|
@@ -1778,21 +1296,9 @@ var GatewayClient = class {
|
|
|
1778
1296
|
if (gap > (typeof rawTimeoutMs === "number" && Number.isFinite(rawTimeoutMs) ? Math.max(1, rawTimeoutMs) : this.tickIntervalMs * 2)) this.protocol.closeSocket(4e3, "tick timeout");
|
|
1779
1297
|
}, interval);
|
|
1780
1298
|
}
|
|
1781
|
-
validateTlsFingerprint() {
|
|
1782
|
-
if (!this.opts.tlsFingerprint || !this.ws) return null;
|
|
1783
|
-
const expected = this.deps.normalizeTlsFingerprint(this.opts.tlsFingerprint);
|
|
1784
|
-
if (!expected) return /* @__PURE__ */ new Error("gateway tls fingerprint missing");
|
|
1785
|
-
const socket = this.ws["_socket"];
|
|
1786
|
-
if (!socket || typeof socket.getPeerCertificate !== "function") return /* @__PURE__ */ new Error("gateway tls fingerprint unavailable");
|
|
1787
|
-
const cert = socket.getPeerCertificate();
|
|
1788
|
-
const fingerprint = this.deps.normalizeTlsFingerprint(cert?.fingerprint256 ?? "");
|
|
1789
|
-
if (!fingerprint) return /* @__PURE__ */ new Error("gateway tls fingerprint unavailable");
|
|
1790
|
-
if (fingerprint !== expected) return /* @__PURE__ */ new Error("gateway tls fingerprint mismatch");
|
|
1791
|
-
return null;
|
|
1792
|
-
}
|
|
1793
1299
|
async request(method, params, opts) {
|
|
1794
1300
|
const expectFinal = opts?.expectFinal === true;
|
|
1795
|
-
const timeoutMs = opts?.timeoutMs === null ? null : typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) ?
|
|
1301
|
+
const timeoutMs = opts?.timeoutMs === null ? null : typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : expectFinal ? null : this.requestTimeoutMs;
|
|
1796
1302
|
return this.protocol.request(method, params, {
|
|
1797
1303
|
expectFinal,
|
|
1798
1304
|
timeoutMs,
|
|
@@ -1808,4 +1314,15 @@ function createGatewayRequestAbortError(method) {
|
|
|
1808
1314
|
return err;
|
|
1809
1315
|
}
|
|
1810
1316
|
//#endregion
|
|
1811
|
-
|
|
1317
|
+
//#region packages/gateway-client/src/cloudflare-access.ts
|
|
1318
|
+
const CF_ACCESS_CLIENT_ID_HEADER = "CF-Access-Client-Id";
|
|
1319
|
+
const CF_ACCESS_CLIENT_SECRET_HEADER = "CF-Access-Client-Secret";
|
|
1320
|
+
/** Build only the two headers Cloudflare Access defines for service-token auth. */
|
|
1321
|
+
function buildCloudflareAccessHeaders(credentials) {
|
|
1322
|
+
return {
|
|
1323
|
+
[CF_ACCESS_CLIENT_ID_HEADER]: credentials.clientId,
|
|
1324
|
+
[CF_ACCESS_CLIENT_SECRET_HEADER]: credentials.clientSecret
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
//#endregion
|
|
1328
|
+
export { CF_ACCESS_CLIENT_ID_HEADER, CF_ACCESS_CLIENT_SECRET_HEADER, DEFAULT_GATEWAY_REQUEST_TIMEOUT_MS, DEFAULT_PREAUTH_HANDSHAKE_TIMEOUT_MS, GatewayBrowserDeviceAuthLifecycle, GatewayClient, GatewayClientRequestError, GatewayClientRequestTimeoutError, GatewayScopeUpgrade, GatewaySessionMessageSubscriptionCoordinator, MAX_CONNECT_CHALLENGE_TIMEOUT_MS, MAX_SAFE_TIMEOUT_DELAY_MS, MIN_CONNECT_CHALLENGE_TIMEOUT_MS, addSafeTimeoutDelayGraceMs, buildCloudflareAccessHeaders, buildDeviceAuthPayload, buildDeviceAuthPayloadV3, buildGatewayConnectAuth, clampConnectChallengeTimeoutMs, clearGatewayConnectTimeout, createSessionProjection, gatewayCredentialScope, gatewayOriginScope, getConnectChallengeTimeoutMsFromEnv, getGatewaySessionMessageSubscriptionCoordinator, hasSessionProjectionAcceptedFinal, isGatewayConnectAssemblyError, isLocallyOptimisticSessionMessage, normalizeDeviceMetadataForAuth, normalizeSessionProjectionRunId, projectLiveSessionMessage, readSessionMessageIdentity, readSessionMessageSequence, reconcileSessionProjectionSnapshot, reduceSessionProjection, reduceSessionProjectionRunEvent, releaseGatewaySessionMessageSubscription, resetGatewaySessionMessageSubscriptionCoordinator, resolveConnectChallengeTimeoutMs, resolveFiniteTimeoutDelayMs, resolveGatewayConnectScopes, resolvePreauthHandshakeTimeoutMs, resolveSafeTimeoutDelayMs, selectGatewayConnectAuth, shouldRetryGatewayWithDeviceToken, startGatewayClientWhenEventLoopReady, startGatewayClientWithReadinessWait, startGatewayConnectTimeout, waitForEventLoopReady };
|