browserclaw 0.5.3 → 0.5.6
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 +6 -0
- package/dist/index.cjs +1140 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1139 -196
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -10,35 +10,826 @@ var promises = require('dns/promises');
|
|
|
10
10
|
var dns = require('dns');
|
|
11
11
|
var promises$1 = require('fs/promises');
|
|
12
12
|
var crypto = require('crypto');
|
|
13
|
-
var ipaddr = require('ipaddr.js');
|
|
14
13
|
|
|
15
14
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
15
|
|
|
17
|
-
function _interopNamespace(e) {
|
|
18
|
-
if (e && e.__esModule) return e;
|
|
19
|
-
var n = Object.create(null);
|
|
20
|
-
if (e) {
|
|
21
|
-
Object.keys(e).forEach(function (k) {
|
|
22
|
-
if (k !== 'default') {
|
|
23
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
24
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
25
|
-
enumerable: true,
|
|
26
|
-
get: function () { return e[k]; }
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
n.default = e;
|
|
32
|
-
return Object.freeze(n);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
16
|
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
36
17
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
37
18
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
38
19
|
var net__default = /*#__PURE__*/_interopDefault(net);
|
|
39
|
-
var ipaddr__namespace = /*#__PURE__*/_interopNamespace(ipaddr);
|
|
40
20
|
|
|
41
|
-
|
|
21
|
+
var __create = Object.create;
|
|
22
|
+
var __defProp = Object.defineProperty;
|
|
23
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
24
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
25
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
26
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
27
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
28
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
29
|
+
};
|
|
30
|
+
var __copyProps = (to, from, except, desc) => {
|
|
31
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
32
|
+
for (let key of __getOwnPropNames(from))
|
|
33
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
34
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
35
|
+
}
|
|
36
|
+
return to;
|
|
37
|
+
};
|
|
38
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
39
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
40
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
41
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
42
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
43
|
+
__defProp(target, "default", { value: mod, enumerable: true }) ,
|
|
44
|
+
mod
|
|
45
|
+
));
|
|
46
|
+
|
|
47
|
+
// node_modules/ipaddr.js/lib/ipaddr.js
|
|
48
|
+
var require_ipaddr = __commonJS({
|
|
49
|
+
"node_modules/ipaddr.js/lib/ipaddr.js"(exports$1, module) {
|
|
50
|
+
(function(root) {
|
|
51
|
+
const ipv4Part = "(0?\\d+|0x[a-f0-9]+)";
|
|
52
|
+
const ipv4Regexes = {
|
|
53
|
+
fourOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, "i"),
|
|
54
|
+
threeOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}$`, "i"),
|
|
55
|
+
twoOctet: new RegExp(`^${ipv4Part}\\.${ipv4Part}$`, "i"),
|
|
56
|
+
longValue: new RegExp(`^${ipv4Part}$`, "i")
|
|
57
|
+
};
|
|
58
|
+
const octalRegex = new RegExp(`^0[0-7]+$`, "i");
|
|
59
|
+
const hexRegex = new RegExp(`^0x[a-f0-9]+$`, "i");
|
|
60
|
+
const zoneIndex = "%[0-9a-z]{1,}";
|
|
61
|
+
const ipv6Part = "(?:[0-9a-f]+::?)+";
|
|
62
|
+
const ipv6Regexes = {
|
|
63
|
+
zoneIndex: new RegExp(zoneIndex, "i"),
|
|
64
|
+
"native": new RegExp(`^(::)?(${ipv6Part})?([0-9a-f]+)?(::)?(${zoneIndex})?$`, "i"),
|
|
65
|
+
deprecatedTransitional: new RegExp(`^(?:::)(${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?)$`, "i"),
|
|
66
|
+
transitional: new RegExp(`^((?:${ipv6Part})|(?:::)(?:${ipv6Part})?)${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}\\.${ipv4Part}(${zoneIndex})?$`, "i")
|
|
67
|
+
};
|
|
68
|
+
function expandIPv6(string, parts) {
|
|
69
|
+
if (string.indexOf("::") !== string.lastIndexOf("::")) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
let colonCount = 0;
|
|
73
|
+
let lastColon = -1;
|
|
74
|
+
let zoneId = (string.match(ipv6Regexes.zoneIndex) || [])[0];
|
|
75
|
+
let replacement, replacementCount;
|
|
76
|
+
if (zoneId) {
|
|
77
|
+
zoneId = zoneId.substring(1);
|
|
78
|
+
string = string.replace(/%.+$/, "");
|
|
79
|
+
}
|
|
80
|
+
while ((lastColon = string.indexOf(":", lastColon + 1)) >= 0) {
|
|
81
|
+
colonCount++;
|
|
82
|
+
}
|
|
83
|
+
if (string.substr(0, 2) === "::") {
|
|
84
|
+
colonCount--;
|
|
85
|
+
}
|
|
86
|
+
if (string.substr(-2, 2) === "::") {
|
|
87
|
+
colonCount--;
|
|
88
|
+
}
|
|
89
|
+
if (colonCount > parts) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
replacementCount = parts - colonCount;
|
|
93
|
+
replacement = ":";
|
|
94
|
+
while (replacementCount--) {
|
|
95
|
+
replacement += "0:";
|
|
96
|
+
}
|
|
97
|
+
string = string.replace("::", replacement);
|
|
98
|
+
if (string[0] === ":") {
|
|
99
|
+
string = string.slice(1);
|
|
100
|
+
}
|
|
101
|
+
if (string[string.length - 1] === ":") {
|
|
102
|
+
string = string.slice(0, -1);
|
|
103
|
+
}
|
|
104
|
+
parts = (function() {
|
|
105
|
+
const ref = string.split(":");
|
|
106
|
+
const results = [];
|
|
107
|
+
for (let i = 0; i < ref.length; i++) {
|
|
108
|
+
results.push(parseInt(ref[i], 16));
|
|
109
|
+
}
|
|
110
|
+
return results;
|
|
111
|
+
})();
|
|
112
|
+
return {
|
|
113
|
+
parts,
|
|
114
|
+
zoneId
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function matchCIDR(first, second, partSize, cidrBits) {
|
|
118
|
+
if (first.length !== second.length) {
|
|
119
|
+
throw new Error("ipaddr: cannot match CIDR for objects with different lengths");
|
|
120
|
+
}
|
|
121
|
+
let part = 0;
|
|
122
|
+
let shift;
|
|
123
|
+
while (cidrBits > 0) {
|
|
124
|
+
shift = partSize - cidrBits;
|
|
125
|
+
if (shift < 0) {
|
|
126
|
+
shift = 0;
|
|
127
|
+
}
|
|
128
|
+
if (first[part] >> shift !== second[part] >> shift) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
cidrBits -= partSize;
|
|
132
|
+
part += 1;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
function parseIntAuto(string) {
|
|
137
|
+
if (hexRegex.test(string)) {
|
|
138
|
+
return parseInt(string, 16);
|
|
139
|
+
}
|
|
140
|
+
if (string[0] === "0" && !isNaN(parseInt(string[1], 10))) {
|
|
141
|
+
if (octalRegex.test(string)) {
|
|
142
|
+
return parseInt(string, 8);
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`ipaddr: cannot parse ${string} as octal`);
|
|
145
|
+
}
|
|
146
|
+
return parseInt(string, 10);
|
|
147
|
+
}
|
|
148
|
+
function padPart(part, length) {
|
|
149
|
+
while (part.length < length) {
|
|
150
|
+
part = `0${part}`;
|
|
151
|
+
}
|
|
152
|
+
return part;
|
|
153
|
+
}
|
|
154
|
+
const ipaddr2 = {};
|
|
155
|
+
ipaddr2.IPv4 = (function() {
|
|
156
|
+
function IPv42(octets) {
|
|
157
|
+
if (octets.length !== 4) {
|
|
158
|
+
throw new Error("ipaddr: ipv4 octet count should be 4");
|
|
159
|
+
}
|
|
160
|
+
let i, octet;
|
|
161
|
+
for (i = 0; i < octets.length; i++) {
|
|
162
|
+
octet = octets[i];
|
|
163
|
+
if (!(0 <= octet && octet <= 255)) {
|
|
164
|
+
throw new Error("ipaddr: ipv4 octet should fit in 8 bits");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
this.octets = octets;
|
|
168
|
+
}
|
|
169
|
+
IPv42.prototype.SpecialRanges = {
|
|
170
|
+
unspecified: [[new IPv42([0, 0, 0, 0]), 8]],
|
|
171
|
+
broadcast: [[new IPv42([255, 255, 255, 255]), 32]],
|
|
172
|
+
// RFC3171
|
|
173
|
+
multicast: [[new IPv42([224, 0, 0, 0]), 4]],
|
|
174
|
+
// RFC3927
|
|
175
|
+
linkLocal: [[new IPv42([169, 254, 0, 0]), 16]],
|
|
176
|
+
// RFC5735
|
|
177
|
+
loopback: [[new IPv42([127, 0, 0, 0]), 8]],
|
|
178
|
+
// RFC6598
|
|
179
|
+
carrierGradeNat: [[new IPv42([100, 64, 0, 0]), 10]],
|
|
180
|
+
// RFC1918
|
|
181
|
+
"private": [
|
|
182
|
+
[new IPv42([10, 0, 0, 0]), 8],
|
|
183
|
+
[new IPv42([172, 16, 0, 0]), 12],
|
|
184
|
+
[new IPv42([192, 168, 0, 0]), 16]
|
|
185
|
+
],
|
|
186
|
+
// Reserved and testing-only ranges; RFCs 5735, 5737, 2544, 1700
|
|
187
|
+
reserved: [
|
|
188
|
+
[new IPv42([192, 0, 0, 0]), 24],
|
|
189
|
+
[new IPv42([192, 0, 2, 0]), 24],
|
|
190
|
+
[new IPv42([192, 88, 99, 0]), 24],
|
|
191
|
+
[new IPv42([198, 18, 0, 0]), 15],
|
|
192
|
+
[new IPv42([198, 51, 100, 0]), 24],
|
|
193
|
+
[new IPv42([203, 0, 113, 0]), 24],
|
|
194
|
+
[new IPv42([240, 0, 0, 0]), 4]
|
|
195
|
+
],
|
|
196
|
+
// RFC7534, RFC7535
|
|
197
|
+
as112: [
|
|
198
|
+
[new IPv42([192, 175, 48, 0]), 24],
|
|
199
|
+
[new IPv42([192, 31, 196, 0]), 24]
|
|
200
|
+
],
|
|
201
|
+
// RFC7450
|
|
202
|
+
amt: [
|
|
203
|
+
[new IPv42([192, 52, 193, 0]), 24]
|
|
204
|
+
]
|
|
205
|
+
};
|
|
206
|
+
IPv42.prototype.kind = function() {
|
|
207
|
+
return "ipv4";
|
|
208
|
+
};
|
|
209
|
+
IPv42.prototype.match = function(other, cidrRange) {
|
|
210
|
+
let ref;
|
|
211
|
+
if (cidrRange === void 0) {
|
|
212
|
+
ref = other;
|
|
213
|
+
other = ref[0];
|
|
214
|
+
cidrRange = ref[1];
|
|
215
|
+
}
|
|
216
|
+
if (other.kind() !== "ipv4") {
|
|
217
|
+
throw new Error("ipaddr: cannot match ipv4 address with non-ipv4 one");
|
|
218
|
+
}
|
|
219
|
+
return matchCIDR(this.octets, other.octets, 8, cidrRange);
|
|
220
|
+
};
|
|
221
|
+
IPv42.prototype.prefixLengthFromSubnetMask = function() {
|
|
222
|
+
let cidr = 0;
|
|
223
|
+
let stop = false;
|
|
224
|
+
const zerotable = {
|
|
225
|
+
0: 8,
|
|
226
|
+
128: 7,
|
|
227
|
+
192: 6,
|
|
228
|
+
224: 5,
|
|
229
|
+
240: 4,
|
|
230
|
+
248: 3,
|
|
231
|
+
252: 2,
|
|
232
|
+
254: 1,
|
|
233
|
+
255: 0
|
|
234
|
+
};
|
|
235
|
+
let i, octet, zeros;
|
|
236
|
+
for (i = 3; i >= 0; i -= 1) {
|
|
237
|
+
octet = this.octets[i];
|
|
238
|
+
if (octet in zerotable) {
|
|
239
|
+
zeros = zerotable[octet];
|
|
240
|
+
if (stop && zeros !== 0) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
if (zeros !== 8) {
|
|
244
|
+
stop = true;
|
|
245
|
+
}
|
|
246
|
+
cidr += zeros;
|
|
247
|
+
} else {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return 32 - cidr;
|
|
252
|
+
};
|
|
253
|
+
IPv42.prototype.range = function() {
|
|
254
|
+
return ipaddr2.subnetMatch(this, this.SpecialRanges);
|
|
255
|
+
};
|
|
256
|
+
IPv42.prototype.toByteArray = function() {
|
|
257
|
+
return this.octets.slice(0);
|
|
258
|
+
};
|
|
259
|
+
IPv42.prototype.toIPv4MappedAddress = function() {
|
|
260
|
+
return ipaddr2.IPv6.parse(`::ffff:${this.toString()}`);
|
|
261
|
+
};
|
|
262
|
+
IPv42.prototype.toNormalizedString = function() {
|
|
263
|
+
return this.toString();
|
|
264
|
+
};
|
|
265
|
+
IPv42.prototype.toString = function() {
|
|
266
|
+
return this.octets.join(".");
|
|
267
|
+
};
|
|
268
|
+
return IPv42;
|
|
269
|
+
})();
|
|
270
|
+
ipaddr2.IPv4.broadcastAddressFromCIDR = function(string) {
|
|
271
|
+
try {
|
|
272
|
+
const cidr = this.parseCIDR(string);
|
|
273
|
+
const ipInterfaceOctets = cidr[0].toByteArray();
|
|
274
|
+
const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
275
|
+
const octets = [];
|
|
276
|
+
let i = 0;
|
|
277
|
+
while (i < 4) {
|
|
278
|
+
octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
|
|
279
|
+
i++;
|
|
280
|
+
}
|
|
281
|
+
return new this(octets);
|
|
282
|
+
} catch (e) {
|
|
283
|
+
throw new Error("ipaddr: the address does not have IPv4 CIDR format");
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
ipaddr2.IPv4.isIPv4 = function(string) {
|
|
287
|
+
return this.parser(string) !== null;
|
|
288
|
+
};
|
|
289
|
+
ipaddr2.IPv4.isValid = function(string) {
|
|
290
|
+
try {
|
|
291
|
+
new this(this.parser(string));
|
|
292
|
+
return true;
|
|
293
|
+
} catch (e) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
ipaddr2.IPv4.isValidCIDR = function(string) {
|
|
298
|
+
try {
|
|
299
|
+
this.parseCIDR(string);
|
|
300
|
+
return true;
|
|
301
|
+
} catch (e) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
ipaddr2.IPv4.isValidFourPartDecimal = function(string) {
|
|
306
|
+
if (ipaddr2.IPv4.isValid(string) && string.match(/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){3}$/)) {
|
|
307
|
+
return true;
|
|
308
|
+
} else {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
ipaddr2.IPv4.isValidCIDRFourPartDecimal = function(string) {
|
|
313
|
+
const match = string.match(/^(.+)\/(\d+)$/);
|
|
314
|
+
if (!ipaddr2.IPv4.isValidCIDR(string) || !match) {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
return ipaddr2.IPv4.isValidFourPartDecimal(match[1]);
|
|
318
|
+
};
|
|
319
|
+
ipaddr2.IPv4.networkAddressFromCIDR = function(string) {
|
|
320
|
+
let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
|
|
321
|
+
try {
|
|
322
|
+
cidr = this.parseCIDR(string);
|
|
323
|
+
ipInterfaceOctets = cidr[0].toByteArray();
|
|
324
|
+
subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
325
|
+
octets = [];
|
|
326
|
+
i = 0;
|
|
327
|
+
while (i < 4) {
|
|
328
|
+
octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
|
|
329
|
+
i++;
|
|
330
|
+
}
|
|
331
|
+
return new this(octets);
|
|
332
|
+
} catch (e) {
|
|
333
|
+
throw new Error("ipaddr: the address does not have IPv4 CIDR format");
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
ipaddr2.IPv4.parse = function(string) {
|
|
337
|
+
const parts = this.parser(string);
|
|
338
|
+
if (parts === null) {
|
|
339
|
+
throw new Error("ipaddr: string is not formatted like an IPv4 Address");
|
|
340
|
+
}
|
|
341
|
+
return new this(parts);
|
|
342
|
+
};
|
|
343
|
+
ipaddr2.IPv4.parseCIDR = function(string) {
|
|
344
|
+
let match;
|
|
345
|
+
if (match = string.match(/^(.+)\/(\d+)$/)) {
|
|
346
|
+
const maskLength = parseInt(match[2]);
|
|
347
|
+
if (maskLength >= 0 && maskLength <= 32) {
|
|
348
|
+
const parsed = [this.parse(match[1]), maskLength];
|
|
349
|
+
Object.defineProperty(parsed, "toString", {
|
|
350
|
+
value: function() {
|
|
351
|
+
return this.join("/");
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
return parsed;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
throw new Error("ipaddr: string is not formatted like an IPv4 CIDR range");
|
|
358
|
+
};
|
|
359
|
+
ipaddr2.IPv4.parser = function(string) {
|
|
360
|
+
let match, part, value;
|
|
361
|
+
if (match = string.match(ipv4Regexes.fourOctet)) {
|
|
362
|
+
return (function() {
|
|
363
|
+
const ref = match.slice(1, 6);
|
|
364
|
+
const results = [];
|
|
365
|
+
for (let i = 0; i < ref.length; i++) {
|
|
366
|
+
part = ref[i];
|
|
367
|
+
results.push(parseIntAuto(part));
|
|
368
|
+
}
|
|
369
|
+
return results;
|
|
370
|
+
})();
|
|
371
|
+
} else if (match = string.match(ipv4Regexes.longValue)) {
|
|
372
|
+
value = parseIntAuto(match[1]);
|
|
373
|
+
if (value > 4294967295 || value < 0) {
|
|
374
|
+
throw new Error("ipaddr: address outside defined range");
|
|
375
|
+
}
|
|
376
|
+
return (function() {
|
|
377
|
+
const results = [];
|
|
378
|
+
let shift;
|
|
379
|
+
for (shift = 0; shift <= 24; shift += 8) {
|
|
380
|
+
results.push(value >> shift & 255);
|
|
381
|
+
}
|
|
382
|
+
return results;
|
|
383
|
+
})().reverse();
|
|
384
|
+
} else if (match = string.match(ipv4Regexes.twoOctet)) {
|
|
385
|
+
return (function() {
|
|
386
|
+
const ref = match.slice(1, 4);
|
|
387
|
+
const results = [];
|
|
388
|
+
value = parseIntAuto(ref[1]);
|
|
389
|
+
if (value > 16777215 || value < 0) {
|
|
390
|
+
throw new Error("ipaddr: address outside defined range");
|
|
391
|
+
}
|
|
392
|
+
results.push(parseIntAuto(ref[0]));
|
|
393
|
+
results.push(value >> 16 & 255);
|
|
394
|
+
results.push(value >> 8 & 255);
|
|
395
|
+
results.push(value & 255);
|
|
396
|
+
return results;
|
|
397
|
+
})();
|
|
398
|
+
} else if (match = string.match(ipv4Regexes.threeOctet)) {
|
|
399
|
+
return (function() {
|
|
400
|
+
const ref = match.slice(1, 5);
|
|
401
|
+
const results = [];
|
|
402
|
+
value = parseIntAuto(ref[2]);
|
|
403
|
+
if (value > 65535 || value < 0) {
|
|
404
|
+
throw new Error("ipaddr: address outside defined range");
|
|
405
|
+
}
|
|
406
|
+
results.push(parseIntAuto(ref[0]));
|
|
407
|
+
results.push(parseIntAuto(ref[1]));
|
|
408
|
+
results.push(value >> 8 & 255);
|
|
409
|
+
results.push(value & 255);
|
|
410
|
+
return results;
|
|
411
|
+
})();
|
|
412
|
+
} else {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
ipaddr2.IPv4.subnetMaskFromPrefixLength = function(prefix) {
|
|
417
|
+
prefix = parseInt(prefix);
|
|
418
|
+
if (prefix < 0 || prefix > 32) {
|
|
419
|
+
throw new Error("ipaddr: invalid IPv4 prefix length");
|
|
420
|
+
}
|
|
421
|
+
const octets = [0, 0, 0, 0];
|
|
422
|
+
let j = 0;
|
|
423
|
+
const filledOctetCount = Math.floor(prefix / 8);
|
|
424
|
+
while (j < filledOctetCount) {
|
|
425
|
+
octets[j] = 255;
|
|
426
|
+
j++;
|
|
427
|
+
}
|
|
428
|
+
if (filledOctetCount < 4) {
|
|
429
|
+
octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - prefix % 8;
|
|
430
|
+
}
|
|
431
|
+
return new this(octets);
|
|
432
|
+
};
|
|
433
|
+
ipaddr2.IPv6 = (function() {
|
|
434
|
+
function IPv62(parts, zoneId) {
|
|
435
|
+
let i, part;
|
|
436
|
+
if (parts.length === 16) {
|
|
437
|
+
this.parts = [];
|
|
438
|
+
for (i = 0; i <= 14; i += 2) {
|
|
439
|
+
this.parts.push(parts[i] << 8 | parts[i + 1]);
|
|
440
|
+
}
|
|
441
|
+
} else if (parts.length === 8) {
|
|
442
|
+
this.parts = parts;
|
|
443
|
+
} else {
|
|
444
|
+
throw new Error("ipaddr: ipv6 part count should be 8 or 16");
|
|
445
|
+
}
|
|
446
|
+
for (i = 0; i < this.parts.length; i++) {
|
|
447
|
+
part = this.parts[i];
|
|
448
|
+
if (!(0 <= part && part <= 65535)) {
|
|
449
|
+
throw new Error("ipaddr: ipv6 part should fit in 16 bits");
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (zoneId) {
|
|
453
|
+
this.zoneId = zoneId;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
IPv62.prototype.SpecialRanges = {
|
|
457
|
+
// RFC4291, here and after
|
|
458
|
+
unspecified: [new IPv62([0, 0, 0, 0, 0, 0, 0, 0]), 128],
|
|
459
|
+
linkLocal: [new IPv62([65152, 0, 0, 0, 0, 0, 0, 0]), 10],
|
|
460
|
+
multicast: [new IPv62([65280, 0, 0, 0, 0, 0, 0, 0]), 8],
|
|
461
|
+
loopback: [new IPv62([0, 0, 0, 0, 0, 0, 0, 1]), 128],
|
|
462
|
+
uniqueLocal: [new IPv62([64512, 0, 0, 0, 0, 0, 0, 0]), 7],
|
|
463
|
+
ipv4Mapped: [new IPv62([0, 0, 0, 0, 0, 65535, 0, 0]), 96],
|
|
464
|
+
// RFC6666
|
|
465
|
+
discard: [new IPv62([256, 0, 0, 0, 0, 0, 0, 0]), 64],
|
|
466
|
+
// RFC6145
|
|
467
|
+
rfc6145: [new IPv62([0, 0, 0, 0, 65535, 0, 0, 0]), 96],
|
|
468
|
+
// RFC6052
|
|
469
|
+
rfc6052: [new IPv62([100, 65435, 0, 0, 0, 0, 0, 0]), 96],
|
|
470
|
+
// RFC3056
|
|
471
|
+
"6to4": [new IPv62([8194, 0, 0, 0, 0, 0, 0, 0]), 16],
|
|
472
|
+
// RFC6052, RFC6146
|
|
473
|
+
teredo: [new IPv62([8193, 0, 0, 0, 0, 0, 0, 0]), 32],
|
|
474
|
+
// RFC5180
|
|
475
|
+
benchmarking: [new IPv62([8193, 2, 0, 0, 0, 0, 0, 0]), 48],
|
|
476
|
+
// RFC7450
|
|
477
|
+
amt: [new IPv62([8193, 3, 0, 0, 0, 0, 0, 0]), 32],
|
|
478
|
+
as112v6: [
|
|
479
|
+
[new IPv62([8193, 4, 274, 0, 0, 0, 0, 0]), 48],
|
|
480
|
+
[new IPv62([9760, 79, 32768, 0, 0, 0, 0, 0]), 48]
|
|
481
|
+
],
|
|
482
|
+
deprecated: [new IPv62([8193, 16, 0, 0, 0, 0, 0, 0]), 28],
|
|
483
|
+
orchid2: [new IPv62([8193, 32, 0, 0, 0, 0, 0, 0]), 28],
|
|
484
|
+
droneRemoteIdProtocolEntityTags: [new IPv62([8193, 48, 0, 0, 0, 0, 0, 0]), 28],
|
|
485
|
+
reserved: [
|
|
486
|
+
// RFC3849
|
|
487
|
+
[new IPv62([8193, 0, 0, 0, 0, 0, 0, 0]), 23],
|
|
488
|
+
// RFC2928
|
|
489
|
+
[new IPv62([8193, 3512, 0, 0, 0, 0, 0, 0]), 32]
|
|
490
|
+
]
|
|
491
|
+
};
|
|
492
|
+
IPv62.prototype.isIPv4MappedAddress = function() {
|
|
493
|
+
return this.range() === "ipv4Mapped";
|
|
494
|
+
};
|
|
495
|
+
IPv62.prototype.kind = function() {
|
|
496
|
+
return "ipv6";
|
|
497
|
+
};
|
|
498
|
+
IPv62.prototype.match = function(other, cidrRange) {
|
|
499
|
+
let ref;
|
|
500
|
+
if (cidrRange === void 0) {
|
|
501
|
+
ref = other;
|
|
502
|
+
other = ref[0];
|
|
503
|
+
cidrRange = ref[1];
|
|
504
|
+
}
|
|
505
|
+
if (other.kind() !== "ipv6") {
|
|
506
|
+
throw new Error("ipaddr: cannot match ipv6 address with non-ipv6 one");
|
|
507
|
+
}
|
|
508
|
+
return matchCIDR(this.parts, other.parts, 16, cidrRange);
|
|
509
|
+
};
|
|
510
|
+
IPv62.prototype.prefixLengthFromSubnetMask = function() {
|
|
511
|
+
let cidr = 0;
|
|
512
|
+
let stop = false;
|
|
513
|
+
const zerotable = {
|
|
514
|
+
0: 16,
|
|
515
|
+
32768: 15,
|
|
516
|
+
49152: 14,
|
|
517
|
+
57344: 13,
|
|
518
|
+
61440: 12,
|
|
519
|
+
63488: 11,
|
|
520
|
+
64512: 10,
|
|
521
|
+
65024: 9,
|
|
522
|
+
65280: 8,
|
|
523
|
+
65408: 7,
|
|
524
|
+
65472: 6,
|
|
525
|
+
65504: 5,
|
|
526
|
+
65520: 4,
|
|
527
|
+
65528: 3,
|
|
528
|
+
65532: 2,
|
|
529
|
+
65534: 1,
|
|
530
|
+
65535: 0
|
|
531
|
+
};
|
|
532
|
+
let part, zeros;
|
|
533
|
+
for (let i = 7; i >= 0; i -= 1) {
|
|
534
|
+
part = this.parts[i];
|
|
535
|
+
if (part in zerotable) {
|
|
536
|
+
zeros = zerotable[part];
|
|
537
|
+
if (stop && zeros !== 0) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
if (zeros !== 16) {
|
|
541
|
+
stop = true;
|
|
542
|
+
}
|
|
543
|
+
cidr += zeros;
|
|
544
|
+
} else {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return 128 - cidr;
|
|
549
|
+
};
|
|
550
|
+
IPv62.prototype.range = function() {
|
|
551
|
+
return ipaddr2.subnetMatch(this, this.SpecialRanges);
|
|
552
|
+
};
|
|
553
|
+
IPv62.prototype.toByteArray = function() {
|
|
554
|
+
let part;
|
|
555
|
+
const bytes = [];
|
|
556
|
+
const ref = this.parts;
|
|
557
|
+
for (let i = 0; i < ref.length; i++) {
|
|
558
|
+
part = ref[i];
|
|
559
|
+
bytes.push(part >> 8);
|
|
560
|
+
bytes.push(part & 255);
|
|
561
|
+
}
|
|
562
|
+
return bytes;
|
|
563
|
+
};
|
|
564
|
+
IPv62.prototype.toFixedLengthString = function() {
|
|
565
|
+
const addr = (function() {
|
|
566
|
+
const results = [];
|
|
567
|
+
for (let i = 0; i < this.parts.length; i++) {
|
|
568
|
+
results.push(padPart(this.parts[i].toString(16), 4));
|
|
569
|
+
}
|
|
570
|
+
return results;
|
|
571
|
+
}).call(this).join(":");
|
|
572
|
+
let suffix = "";
|
|
573
|
+
if (this.zoneId) {
|
|
574
|
+
suffix = `%${this.zoneId}`;
|
|
575
|
+
}
|
|
576
|
+
return addr + suffix;
|
|
577
|
+
};
|
|
578
|
+
IPv62.prototype.toIPv4Address = function() {
|
|
579
|
+
if (!this.isIPv4MappedAddress()) {
|
|
580
|
+
throw new Error("ipaddr: trying to convert a generic ipv6 address to ipv4");
|
|
581
|
+
}
|
|
582
|
+
const ref = this.parts.slice(-2);
|
|
583
|
+
const high = ref[0];
|
|
584
|
+
const low = ref[1];
|
|
585
|
+
return new ipaddr2.IPv4([high >> 8, high & 255, low >> 8, low & 255]);
|
|
586
|
+
};
|
|
587
|
+
IPv62.prototype.toNormalizedString = function() {
|
|
588
|
+
const addr = (function() {
|
|
589
|
+
const results = [];
|
|
590
|
+
for (let i = 0; i < this.parts.length; i++) {
|
|
591
|
+
results.push(this.parts[i].toString(16));
|
|
592
|
+
}
|
|
593
|
+
return results;
|
|
594
|
+
}).call(this).join(":");
|
|
595
|
+
let suffix = "";
|
|
596
|
+
if (this.zoneId) {
|
|
597
|
+
suffix = `%${this.zoneId}`;
|
|
598
|
+
}
|
|
599
|
+
return addr + suffix;
|
|
600
|
+
};
|
|
601
|
+
IPv62.prototype.toRFC5952String = function() {
|
|
602
|
+
const regex = /((^|:)(0(:|$)){2,})/g;
|
|
603
|
+
const string = this.toNormalizedString();
|
|
604
|
+
let bestMatchIndex = 0;
|
|
605
|
+
let bestMatchLength = -1;
|
|
606
|
+
let match;
|
|
607
|
+
while (match = regex.exec(string)) {
|
|
608
|
+
if (match[0].length > bestMatchLength) {
|
|
609
|
+
bestMatchIndex = match.index;
|
|
610
|
+
bestMatchLength = match[0].length;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (bestMatchLength < 0) {
|
|
614
|
+
return string;
|
|
615
|
+
}
|
|
616
|
+
return `${string.substring(0, bestMatchIndex)}::${string.substring(bestMatchIndex + bestMatchLength)}`;
|
|
617
|
+
};
|
|
618
|
+
IPv62.prototype.toString = function() {
|
|
619
|
+
return this.toRFC5952String();
|
|
620
|
+
};
|
|
621
|
+
return IPv62;
|
|
622
|
+
})();
|
|
623
|
+
ipaddr2.IPv6.broadcastAddressFromCIDR = function(string) {
|
|
624
|
+
try {
|
|
625
|
+
const cidr = this.parseCIDR(string);
|
|
626
|
+
const ipInterfaceOctets = cidr[0].toByteArray();
|
|
627
|
+
const subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
628
|
+
const octets = [];
|
|
629
|
+
let i = 0;
|
|
630
|
+
while (i < 16) {
|
|
631
|
+
octets.push(parseInt(ipInterfaceOctets[i], 10) | parseInt(subnetMaskOctets[i], 10) ^ 255);
|
|
632
|
+
i++;
|
|
633
|
+
}
|
|
634
|
+
return new this(octets);
|
|
635
|
+
} catch (e) {
|
|
636
|
+
throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
ipaddr2.IPv6.isIPv6 = function(string) {
|
|
640
|
+
return this.parser(string) !== null;
|
|
641
|
+
};
|
|
642
|
+
ipaddr2.IPv6.isValid = function(string) {
|
|
643
|
+
if (typeof string === "string" && string.indexOf(":") === -1) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
const addr = this.parser(string);
|
|
648
|
+
new this(addr.parts, addr.zoneId);
|
|
649
|
+
return true;
|
|
650
|
+
} catch (e) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
ipaddr2.IPv6.isValidCIDR = function(string) {
|
|
655
|
+
if (typeof string === "string" && string.indexOf(":") === -1) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
try {
|
|
659
|
+
this.parseCIDR(string);
|
|
660
|
+
return true;
|
|
661
|
+
} catch (e) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
ipaddr2.IPv6.networkAddressFromCIDR = function(string) {
|
|
666
|
+
let cidr, i, ipInterfaceOctets, octets, subnetMaskOctets;
|
|
667
|
+
try {
|
|
668
|
+
cidr = this.parseCIDR(string);
|
|
669
|
+
ipInterfaceOctets = cidr[0].toByteArray();
|
|
670
|
+
subnetMaskOctets = this.subnetMaskFromPrefixLength(cidr[1]).toByteArray();
|
|
671
|
+
octets = [];
|
|
672
|
+
i = 0;
|
|
673
|
+
while (i < 16) {
|
|
674
|
+
octets.push(parseInt(ipInterfaceOctets[i], 10) & parseInt(subnetMaskOctets[i], 10));
|
|
675
|
+
i++;
|
|
676
|
+
}
|
|
677
|
+
return new this(octets);
|
|
678
|
+
} catch (e) {
|
|
679
|
+
throw new Error(`ipaddr: the address does not have IPv6 CIDR format (${e})`);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
ipaddr2.IPv6.parse = function(string) {
|
|
683
|
+
const addr = this.parser(string);
|
|
684
|
+
if (addr.parts === null) {
|
|
685
|
+
throw new Error("ipaddr: string is not formatted like an IPv6 Address");
|
|
686
|
+
}
|
|
687
|
+
return new this(addr.parts, addr.zoneId);
|
|
688
|
+
};
|
|
689
|
+
ipaddr2.IPv6.parseCIDR = function(string) {
|
|
690
|
+
let maskLength, match, parsed;
|
|
691
|
+
if (match = string.match(/^(.+)\/(\d+)$/)) {
|
|
692
|
+
maskLength = parseInt(match[2]);
|
|
693
|
+
if (maskLength >= 0 && maskLength <= 128) {
|
|
694
|
+
parsed = [this.parse(match[1]), maskLength];
|
|
695
|
+
Object.defineProperty(parsed, "toString", {
|
|
696
|
+
value: function() {
|
|
697
|
+
return this.join("/");
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
return parsed;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
throw new Error("ipaddr: string is not formatted like an IPv6 CIDR range");
|
|
704
|
+
};
|
|
705
|
+
ipaddr2.IPv6.parser = function(string) {
|
|
706
|
+
let addr, i, match, octet, octets, zoneId;
|
|
707
|
+
if (match = string.match(ipv6Regexes.deprecatedTransitional)) {
|
|
708
|
+
return this.parser(`::ffff:${match[1]}`);
|
|
709
|
+
}
|
|
710
|
+
if (ipv6Regexes.native.test(string)) {
|
|
711
|
+
return expandIPv6(string, 8);
|
|
712
|
+
}
|
|
713
|
+
if (match = string.match(ipv6Regexes.transitional)) {
|
|
714
|
+
zoneId = match[6] || "";
|
|
715
|
+
addr = match[1];
|
|
716
|
+
if (!match[1].endsWith("::")) {
|
|
717
|
+
addr = addr.slice(0, -1);
|
|
718
|
+
}
|
|
719
|
+
addr = expandIPv6(addr + zoneId, 6);
|
|
720
|
+
if (addr.parts) {
|
|
721
|
+
octets = [
|
|
722
|
+
parseInt(match[2]),
|
|
723
|
+
parseInt(match[3]),
|
|
724
|
+
parseInt(match[4]),
|
|
725
|
+
parseInt(match[5])
|
|
726
|
+
];
|
|
727
|
+
for (i = 0; i < octets.length; i++) {
|
|
728
|
+
octet = octets[i];
|
|
729
|
+
if (!(0 <= octet && octet <= 255)) {
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
addr.parts.push(octets[0] << 8 | octets[1]);
|
|
734
|
+
addr.parts.push(octets[2] << 8 | octets[3]);
|
|
735
|
+
return {
|
|
736
|
+
parts: addr.parts,
|
|
737
|
+
zoneId: addr.zoneId
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return null;
|
|
742
|
+
};
|
|
743
|
+
ipaddr2.IPv6.subnetMaskFromPrefixLength = function(prefix) {
|
|
744
|
+
prefix = parseInt(prefix);
|
|
745
|
+
if (prefix < 0 || prefix > 128) {
|
|
746
|
+
throw new Error("ipaddr: invalid IPv6 prefix length");
|
|
747
|
+
}
|
|
748
|
+
const octets = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
749
|
+
let j = 0;
|
|
750
|
+
const filledOctetCount = Math.floor(prefix / 8);
|
|
751
|
+
while (j < filledOctetCount) {
|
|
752
|
+
octets[j] = 255;
|
|
753
|
+
j++;
|
|
754
|
+
}
|
|
755
|
+
if (filledOctetCount < 16) {
|
|
756
|
+
octets[filledOctetCount] = Math.pow(2, prefix % 8) - 1 << 8 - prefix % 8;
|
|
757
|
+
}
|
|
758
|
+
return new this(octets);
|
|
759
|
+
};
|
|
760
|
+
ipaddr2.fromByteArray = function(bytes) {
|
|
761
|
+
const length = bytes.length;
|
|
762
|
+
if (length === 4) {
|
|
763
|
+
return new ipaddr2.IPv4(bytes);
|
|
764
|
+
} else if (length === 16) {
|
|
765
|
+
return new ipaddr2.IPv6(bytes);
|
|
766
|
+
} else {
|
|
767
|
+
throw new Error("ipaddr: the binary input is neither an IPv6 nor IPv4 address");
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
ipaddr2.isValid = function(string) {
|
|
771
|
+
return ipaddr2.IPv6.isValid(string) || ipaddr2.IPv4.isValid(string);
|
|
772
|
+
};
|
|
773
|
+
ipaddr2.isValidCIDR = function(string) {
|
|
774
|
+
return ipaddr2.IPv6.isValidCIDR(string) || ipaddr2.IPv4.isValidCIDR(string);
|
|
775
|
+
};
|
|
776
|
+
ipaddr2.parse = function(string) {
|
|
777
|
+
if (ipaddr2.IPv6.isValid(string)) {
|
|
778
|
+
return ipaddr2.IPv6.parse(string);
|
|
779
|
+
} else if (ipaddr2.IPv4.isValid(string)) {
|
|
780
|
+
return ipaddr2.IPv4.parse(string);
|
|
781
|
+
} else {
|
|
782
|
+
throw new Error("ipaddr: the address has neither IPv6 nor IPv4 format");
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
ipaddr2.parseCIDR = function(string) {
|
|
786
|
+
try {
|
|
787
|
+
return ipaddr2.IPv6.parseCIDR(string);
|
|
788
|
+
} catch (e) {
|
|
789
|
+
try {
|
|
790
|
+
return ipaddr2.IPv4.parseCIDR(string);
|
|
791
|
+
} catch (e2) {
|
|
792
|
+
throw new Error("ipaddr: the address has neither IPv6 nor IPv4 CIDR format");
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
ipaddr2.process = function(string) {
|
|
797
|
+
const addr = this.parse(string);
|
|
798
|
+
if (addr.kind() === "ipv6" && addr.isIPv4MappedAddress()) {
|
|
799
|
+
return addr.toIPv4Address();
|
|
800
|
+
} else {
|
|
801
|
+
return addr;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
ipaddr2.subnetMatch = function(address, rangeList, defaultName) {
|
|
805
|
+
let i, rangeName, rangeSubnets, subnet;
|
|
806
|
+
if (defaultName === void 0 || defaultName === null) {
|
|
807
|
+
defaultName = "unicast";
|
|
808
|
+
}
|
|
809
|
+
for (rangeName in rangeList) {
|
|
810
|
+
if (Object.prototype.hasOwnProperty.call(rangeList, rangeName)) {
|
|
811
|
+
rangeSubnets = rangeList[rangeName];
|
|
812
|
+
if (rangeSubnets[0] && !(rangeSubnets[0] instanceof Array)) {
|
|
813
|
+
rangeSubnets = [rangeSubnets];
|
|
814
|
+
}
|
|
815
|
+
for (i = 0; i < rangeSubnets.length; i++) {
|
|
816
|
+
subnet = rangeSubnets[i];
|
|
817
|
+
if (address.kind() === subnet[0].kind() && address.match.apply(address, subnet)) {
|
|
818
|
+
return rangeName;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return defaultName;
|
|
824
|
+
};
|
|
825
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
826
|
+
module.exports = ipaddr2;
|
|
827
|
+
} else {
|
|
828
|
+
root.ipaddr = ipaddr2;
|
|
829
|
+
}
|
|
830
|
+
})(exports$1);
|
|
831
|
+
}
|
|
832
|
+
});
|
|
42
833
|
var CHROMIUM_BUNDLE_IDS = /* @__PURE__ */ new Set([
|
|
43
834
|
"com.google.Chrome",
|
|
44
835
|
"com.google.Chrome.beta",
|
|
@@ -829,15 +1620,16 @@ async function connectBrowser(cdpUrl, authToken) {
|
|
|
829
1620
|
const headers = {};
|
|
830
1621
|
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
831
1622
|
const browser = await playwrightCore.chromium.connectOverCDP(endpoint, { timeout, headers });
|
|
832
|
-
const
|
|
833
|
-
cached = connected;
|
|
834
|
-
observeBrowser(browser);
|
|
835
|
-
browser.on("disconnected", () => {
|
|
1623
|
+
const onDisconnected = () => {
|
|
836
1624
|
if (cached?.browser === browser) cached = null;
|
|
837
1625
|
for (const key of roleRefsByTarget.keys()) {
|
|
838
1626
|
if (key.startsWith(normalized + "::")) roleRefsByTarget.delete(key);
|
|
839
1627
|
}
|
|
840
|
-
}
|
|
1628
|
+
};
|
|
1629
|
+
const connected = { browser, cdpUrl: normalized, authToken, onDisconnected };
|
|
1630
|
+
cached = connected;
|
|
1631
|
+
observeBrowser(browser);
|
|
1632
|
+
browser.on("disconnected", onDisconnected);
|
|
841
1633
|
return connected;
|
|
842
1634
|
} catch (err) {
|
|
843
1635
|
lastErr = err;
|
|
@@ -867,6 +1659,14 @@ async function disconnectBrowser() {
|
|
|
867
1659
|
if (cur) await cur.browser.close().catch(() => {
|
|
868
1660
|
});
|
|
869
1661
|
}
|
|
1662
|
+
function cdpSocketNeedsAttach(wsUrl) {
|
|
1663
|
+
try {
|
|
1664
|
+
const pathname = new URL(wsUrl).pathname;
|
|
1665
|
+
return pathname === "/cdp" || pathname.endsWith("/cdp") || pathname.includes("/devtools/browser/") || pathname === "/";
|
|
1666
|
+
} catch {
|
|
1667
|
+
return false;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
870
1670
|
async function tryTerminateExecutionViaCdp(cdpUrl, targetId) {
|
|
871
1671
|
const httpBase = normalizeCdpHttpBaseForJsonEndpoints(cdpUrl);
|
|
872
1672
|
const ctrl = new AbortController();
|
|
@@ -883,8 +1683,10 @@ async function tryTerminateExecutionViaCdp(cdpUrl, targetId) {
|
|
|
883
1683
|
}
|
|
884
1684
|
if (!Array.isArray(targets)) return;
|
|
885
1685
|
const target = targets.find((entry) => String(entry?.id ?? "").trim() === targetId);
|
|
886
|
-
const
|
|
887
|
-
if (!
|
|
1686
|
+
const wsUrlRaw = String(target?.webSocketDebuggerUrl ?? "").trim();
|
|
1687
|
+
if (!wsUrlRaw) return;
|
|
1688
|
+
const wsUrl = normalizeCdpWsUrl(wsUrlRaw, httpBase);
|
|
1689
|
+
const needsAttach = cdpSocketNeedsAttach(wsUrl);
|
|
888
1690
|
await new Promise((resolve2) => {
|
|
889
1691
|
let done = false;
|
|
890
1692
|
const finish = () => {
|
|
@@ -897,8 +1699,9 @@ async function tryTerminateExecutionViaCdp(cdpUrl, targetId) {
|
|
|
897
1699
|
}
|
|
898
1700
|
resolve2();
|
|
899
1701
|
};
|
|
900
|
-
const timer = setTimeout(finish,
|
|
1702
|
+
const timer = setTimeout(finish, 3e3);
|
|
901
1703
|
let ws;
|
|
1704
|
+
let nextId = 1;
|
|
902
1705
|
try {
|
|
903
1706
|
ws = new WebSocket(wsUrl);
|
|
904
1707
|
} catch {
|
|
@@ -906,11 +1709,27 @@ async function tryTerminateExecutionViaCdp(cdpUrl, targetId) {
|
|
|
906
1709
|
return;
|
|
907
1710
|
}
|
|
908
1711
|
ws.onopen = () => {
|
|
1712
|
+
if (needsAttach) {
|
|
1713
|
+
ws.send(JSON.stringify({ id: nextId++, method: "Target.attachToTarget", params: { targetId, flatten: true } }));
|
|
1714
|
+
} else {
|
|
1715
|
+
ws.send(JSON.stringify({ id: nextId++, method: "Runtime.terminateExecution" }));
|
|
1716
|
+
setTimeout(finish, 300);
|
|
1717
|
+
}
|
|
1718
|
+
};
|
|
1719
|
+
ws.onmessage = (event) => {
|
|
1720
|
+
if (!needsAttach) return;
|
|
909
1721
|
try {
|
|
910
|
-
|
|
1722
|
+
const msg = JSON.parse(String(event.data));
|
|
1723
|
+
if (msg.id && msg.result?.sessionId) {
|
|
1724
|
+
ws.send(JSON.stringify({ id: nextId++, sessionId: msg.result.sessionId, method: "Runtime.terminateExecution" }));
|
|
1725
|
+
try {
|
|
1726
|
+
ws.send(JSON.stringify({ id: nextId++, method: "Target.detachFromTarget", params: { sessionId: msg.result.sessionId } }));
|
|
1727
|
+
} catch {
|
|
1728
|
+
}
|
|
1729
|
+
setTimeout(finish, 300);
|
|
1730
|
+
}
|
|
911
1731
|
} catch {
|
|
912
1732
|
}
|
|
913
|
-
setTimeout(finish, 300);
|
|
914
1733
|
};
|
|
915
1734
|
ws.onerror = () => finish();
|
|
916
1735
|
ws.onclose = () => finish();
|
|
@@ -922,6 +1741,9 @@ async function forceDisconnectPlaywrightForTarget(opts) {
|
|
|
922
1741
|
if (!cur || cur.cdpUrl !== normalized) return;
|
|
923
1742
|
cached = null;
|
|
924
1743
|
connectingByUrl.delete(normalized);
|
|
1744
|
+
if (cur.onDisconnected && typeof cur.browser.off === "function") {
|
|
1745
|
+
cur.browser.off("disconnected", cur.onDisconnected);
|
|
1746
|
+
}
|
|
925
1747
|
const targetId = opts.targetId?.trim() || "";
|
|
926
1748
|
if (targetId) {
|
|
927
1749
|
await tryTerminateExecutionViaCdp(normalized, targetId).catch(() => {
|
|
@@ -1472,6 +2294,9 @@ function formatAriaNodes(nodes, limit) {
|
|
|
1472
2294
|
}
|
|
1473
2295
|
return out;
|
|
1474
2296
|
}
|
|
2297
|
+
|
|
2298
|
+
// src/security.ts
|
|
2299
|
+
var ipaddr = __toESM(require_ipaddr());
|
|
1475
2300
|
var InvalidBrowserNavigationUrlError = class extends Error {
|
|
1476
2301
|
constructor(message) {
|
|
1477
2302
|
super(message);
|
|
@@ -1512,20 +2337,6 @@ function isBlockedHostnameNormalized(normalized) {
|
|
|
1512
2337
|
if (BLOCKED_HOSTNAMES.has(normalized)) return true;
|
|
1513
2338
|
return normalized.endsWith(".localhost") || normalized.endsWith(".local") || normalized.endsWith(".internal");
|
|
1514
2339
|
}
|
|
1515
|
-
function isStrictDecimalOctet(part) {
|
|
1516
|
-
if (!/^[0-9]+$/.test(part)) return false;
|
|
1517
|
-
const n = parseInt(part, 10);
|
|
1518
|
-
if (n < 0 || n > 255) return false;
|
|
1519
|
-
if (String(n) !== part) return false;
|
|
1520
|
-
return true;
|
|
1521
|
-
}
|
|
1522
|
-
function isUnsupportedIPv4Literal(ip) {
|
|
1523
|
-
if (/^[0-9]+$/.test(ip)) return true;
|
|
1524
|
-
const parts = ip.split(".");
|
|
1525
|
-
if (parts.length !== 4) return true;
|
|
1526
|
-
if (!parts.every(isStrictDecimalOctet)) return true;
|
|
1527
|
-
return false;
|
|
1528
|
-
}
|
|
1529
2340
|
var BLOCKED_IPV4_RANGES = /* @__PURE__ */ new Set([
|
|
1530
2341
|
"unspecified",
|
|
1531
2342
|
"broadcast",
|
|
@@ -1543,7 +2354,98 @@ var BLOCKED_IPV6_RANGES = /* @__PURE__ */ new Set([
|
|
|
1543
2354
|
"uniqueLocal",
|
|
1544
2355
|
"multicast"
|
|
1545
2356
|
]);
|
|
1546
|
-
var RFC2544_BENCHMARK_PREFIX = [
|
|
2357
|
+
var RFC2544_BENCHMARK_PREFIX = [ipaddr.IPv4.parse("198.18.0.0"), 15];
|
|
2358
|
+
var EMBEDDED_IPV4_SENTINEL_RULES = [
|
|
2359
|
+
// IPv4-compatible (::a.b.c.d)
|
|
2360
|
+
{
|
|
2361
|
+
matches: (parts) => parts[0] === 0 && parts[1] === 0 && parts[2] === 0 && parts[3] === 0 && parts[4] === 0 && parts[5] === 0,
|
|
2362
|
+
toHextets: (parts) => [parts[6], parts[7]]
|
|
2363
|
+
},
|
|
2364
|
+
// NAT64 local-use (64:ff9b:1::/48)
|
|
2365
|
+
{
|
|
2366
|
+
matches: (parts) => parts[0] === 100 && parts[1] === 65435 && parts[2] === 1 && parts[3] === 0 && parts[4] === 0 && parts[5] === 0,
|
|
2367
|
+
toHextets: (parts) => [parts[6], parts[7]]
|
|
2368
|
+
},
|
|
2369
|
+
// 6to4 (2002::/16)
|
|
2370
|
+
{
|
|
2371
|
+
matches: (parts) => parts[0] === 8194,
|
|
2372
|
+
toHextets: (parts) => [parts[1], parts[2]]
|
|
2373
|
+
},
|
|
2374
|
+
// Teredo (2001:0000::/32) — IPv4 XOR'd
|
|
2375
|
+
{
|
|
2376
|
+
matches: (parts) => parts[0] === 8193 && parts[1] === 0,
|
|
2377
|
+
toHextets: (parts) => [parts[6] ^ 65535, parts[7] ^ 65535]
|
|
2378
|
+
},
|
|
2379
|
+
// ISATAP — sentinel in parts[4-5]: 0x0000:0x5efe or 0x0200:0x5efe
|
|
2380
|
+
{
|
|
2381
|
+
matches: (parts) => (parts[4] & 64767) === 0 && parts[5] === 24318,
|
|
2382
|
+
toHextets: (parts) => [parts[6], parts[7]]
|
|
2383
|
+
}
|
|
2384
|
+
];
|
|
2385
|
+
function stripIpv6Brackets(value) {
|
|
2386
|
+
if (value.startsWith("[") && value.endsWith("]")) return value.slice(1, -1);
|
|
2387
|
+
return value;
|
|
2388
|
+
}
|
|
2389
|
+
function isNumericIpv4LiteralPart(value) {
|
|
2390
|
+
return /^[0-9]+$/.test(value) || /^0x[0-9a-f]+$/i.test(value);
|
|
2391
|
+
}
|
|
2392
|
+
function parseIpv6WithEmbeddedIpv4(raw) {
|
|
2393
|
+
if (!raw.includes(":") || !raw.includes(".")) return;
|
|
2394
|
+
const match = /^(.*:)([^:%]+(?:\.[^:%]+){3})(%[0-9A-Za-z]+)?$/i.exec(raw);
|
|
2395
|
+
if (!match) return;
|
|
2396
|
+
const [, prefix, embeddedIpv4, zoneSuffix = ""] = match;
|
|
2397
|
+
if (!ipaddr.IPv4.isValidFourPartDecimal(embeddedIpv4)) return;
|
|
2398
|
+
const octets = embeddedIpv4.split(".").map((part) => Number.parseInt(part, 10));
|
|
2399
|
+
const normalizedIpv6 = `${prefix}${(octets[0] << 8 | octets[1]).toString(16)}:${(octets[2] << 8 | octets[3]).toString(16)}${zoneSuffix}`;
|
|
2400
|
+
if (!ipaddr.IPv6.isValid(normalizedIpv6)) return;
|
|
2401
|
+
return ipaddr.IPv6.parse(normalizedIpv6);
|
|
2402
|
+
}
|
|
2403
|
+
function normalizeIpParseInput(raw) {
|
|
2404
|
+
const trimmed = raw?.trim();
|
|
2405
|
+
if (!trimmed) return;
|
|
2406
|
+
return stripIpv6Brackets(trimmed);
|
|
2407
|
+
}
|
|
2408
|
+
function parseCanonicalIpAddress(raw) {
|
|
2409
|
+
const normalized = normalizeIpParseInput(raw);
|
|
2410
|
+
if (!normalized) return;
|
|
2411
|
+
if (ipaddr.IPv4.isValid(normalized)) {
|
|
2412
|
+
if (!ipaddr.IPv4.isValidFourPartDecimal(normalized)) return;
|
|
2413
|
+
return ipaddr.IPv4.parse(normalized);
|
|
2414
|
+
}
|
|
2415
|
+
if (ipaddr.IPv6.isValid(normalized)) return ipaddr.IPv6.parse(normalized);
|
|
2416
|
+
return parseIpv6WithEmbeddedIpv4(normalized);
|
|
2417
|
+
}
|
|
2418
|
+
function parseLooseIpAddress(raw) {
|
|
2419
|
+
const normalized = normalizeIpParseInput(raw);
|
|
2420
|
+
if (!normalized) return;
|
|
2421
|
+
if (ipaddr.isValid(normalized)) return ipaddr.parse(normalized);
|
|
2422
|
+
return parseIpv6WithEmbeddedIpv4(normalized);
|
|
2423
|
+
}
|
|
2424
|
+
function isCanonicalDottedDecimalIPv4(raw) {
|
|
2425
|
+
const trimmed = raw?.trim();
|
|
2426
|
+
if (!trimmed) return false;
|
|
2427
|
+
const normalized = stripIpv6Brackets(trimmed);
|
|
2428
|
+
if (!normalized) return false;
|
|
2429
|
+
return ipaddr.IPv4.isValidFourPartDecimal(normalized);
|
|
2430
|
+
}
|
|
2431
|
+
function isLegacyIpv4Literal(raw) {
|
|
2432
|
+
const trimmed = raw?.trim();
|
|
2433
|
+
if (!trimmed) return false;
|
|
2434
|
+
const normalized = stripIpv6Brackets(trimmed);
|
|
2435
|
+
if (!normalized || normalized.includes(":")) return false;
|
|
2436
|
+
if (isCanonicalDottedDecimalIPv4(normalized)) return false;
|
|
2437
|
+
const parts = normalized.split(".");
|
|
2438
|
+
if (parts.length === 0 || parts.length > 4) return false;
|
|
2439
|
+
if (parts.some((part) => part.length === 0)) return false;
|
|
2440
|
+
if (!parts.every((part) => isNumericIpv4LiteralPart(part))) return false;
|
|
2441
|
+
return true;
|
|
2442
|
+
}
|
|
2443
|
+
function looksLikeUnsupportedIpv4Literal(address) {
|
|
2444
|
+
const parts = address.split(".");
|
|
2445
|
+
if (parts.length === 0 || parts.length > 4) return false;
|
|
2446
|
+
if (parts.some((part) => part.length === 0)) return true;
|
|
2447
|
+
return parts.every((part) => /^[0-9]+$/.test(part) || /^0x/i.test(part));
|
|
2448
|
+
}
|
|
1547
2449
|
function isBlockedSpecialUseIpv4Address(address, opts) {
|
|
1548
2450
|
const inRfc2544 = address.match(RFC2544_BENCHMARK_PREFIX);
|
|
1549
2451
|
if (inRfc2544 && opts?.allowRfc2544BenchmarkRange === true) return false;
|
|
@@ -1553,89 +2455,50 @@ function isBlockedSpecialUseIpv6Address(address) {
|
|
|
1553
2455
|
if (BLOCKED_IPV6_RANGES.has(address.range())) return true;
|
|
1554
2456
|
return (address.parts[0] & 65472) === 65216;
|
|
1555
2457
|
}
|
|
1556
|
-
function
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
} catch {
|
|
1574
|
-
return true;
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
if (parts[0] === 8194) {
|
|
1578
|
-
const ip4str = `${parts[1] >> 8 & 255}.${parts[1] & 255}.${parts[2] >> 8 & 255}.${parts[2] & 255}`;
|
|
1579
|
-
try {
|
|
1580
|
-
return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
|
|
1581
|
-
} catch {
|
|
1582
|
-
return true;
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
if (parts[0] === 8193 && parts[1] === 0) {
|
|
1586
|
-
const hiXored = parts[6] ^ 65535;
|
|
1587
|
-
const loXored = parts[7] ^ 65535;
|
|
1588
|
-
const ip4str = `${hiXored >> 8 & 255}.${hiXored & 255}.${loXored >> 8 & 255}.${loXored & 255}`;
|
|
1589
|
-
try {
|
|
1590
|
-
return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
|
|
1591
|
-
} catch {
|
|
1592
|
-
return true;
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
if (parts[0] === 0 && parts[1] === 0 && parts[2] === 0 && parts[3] === 0 && parts[4] === 65535 && parts[5] === 0) {
|
|
1596
|
-
const ip4str = `${parts[6] >> 8 & 255}.${parts[6] & 255}.${parts[7] >> 8 & 255}.${parts[7] & 255}`;
|
|
1597
|
-
try {
|
|
1598
|
-
return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
|
|
1599
|
-
} catch {
|
|
1600
|
-
return true;
|
|
1601
|
-
}
|
|
1602
|
-
}
|
|
1603
|
-
if (parts[0] === 0 && parts[1] === 0 && parts[2] === 0 && parts[3] === 0 && parts[4] === 0 && parts[5] === 0) {
|
|
1604
|
-
const ip4str = `${parts[6] >> 8 & 255}.${parts[6] & 255}.${parts[7] >> 8 & 255}.${parts[7] & 255}`;
|
|
1605
|
-
try {
|
|
1606
|
-
return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
|
|
1607
|
-
} catch {
|
|
1608
|
-
return true;
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
if ((parts[4] & 65023) === 0 && parts[5] === 24318) {
|
|
1612
|
-
const ip4str = `${parts[6] >> 8 & 255}.${parts[6] & 255}.${parts[7] >> 8 & 255}.${parts[7] & 255}`;
|
|
1613
|
-
try {
|
|
1614
|
-
return isBlockedSpecialUseIpv4Address(ipaddr__namespace.IPv4.parse(ip4str), opts);
|
|
1615
|
-
} catch {
|
|
1616
|
-
return true;
|
|
1617
|
-
}
|
|
2458
|
+
function decodeIpv4FromHextets(high, low) {
|
|
2459
|
+
const octets = [
|
|
2460
|
+
high >>> 8 & 255,
|
|
2461
|
+
high & 255,
|
|
2462
|
+
low >>> 8 & 255,
|
|
2463
|
+
low & 255
|
|
2464
|
+
];
|
|
2465
|
+
return ipaddr.IPv4.parse(octets.join("."));
|
|
2466
|
+
}
|
|
2467
|
+
function extractEmbeddedIpv4FromIpv6(address) {
|
|
2468
|
+
if (address.isIPv4MappedAddress()) return address.toIPv4Address();
|
|
2469
|
+
if (address.range() === "rfc6145") return decodeIpv4FromHextets(address.parts[6], address.parts[7]);
|
|
2470
|
+
if (address.range() === "rfc6052") return decodeIpv4FromHextets(address.parts[6], address.parts[7]);
|
|
2471
|
+
for (const rule of EMBEDDED_IPV4_SENTINEL_RULES) {
|
|
2472
|
+
if (!rule.matches(address.parts)) continue;
|
|
2473
|
+
const [high, low] = rule.toHextets(address.parts);
|
|
2474
|
+
return decodeIpv4FromHextets(high, low);
|
|
1618
2475
|
}
|
|
1619
|
-
return null;
|
|
1620
2476
|
}
|
|
1621
|
-
function
|
|
2477
|
+
function resolveIpv4SpecialUseBlockOptions(policy) {
|
|
2478
|
+
return { allowRfc2544BenchmarkRange: policy?.allowRfc2544BenchmarkRange === true };
|
|
2479
|
+
}
|
|
2480
|
+
function isBlockedHostnameOrIp(hostname, policy) {
|
|
2481
|
+
const normalized = normalizeHostname(hostname);
|
|
2482
|
+
if (!normalized) return false;
|
|
2483
|
+
return isBlockedHostnameNormalized(normalized) || isPrivateIpAddress(normalized, policy);
|
|
2484
|
+
}
|
|
2485
|
+
function isPrivateIpAddress(address, policy) {
|
|
1622
2486
|
let normalized = address.trim().toLowerCase();
|
|
1623
2487
|
if (normalized.startsWith("[") && normalized.endsWith("]")) normalized = normalized.slice(1, -1);
|
|
1624
2488
|
if (!normalized) return false;
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
const v6 = parsed;
|
|
2489
|
+
const blockOptions = resolveIpv4SpecialUseBlockOptions(policy);
|
|
2490
|
+
const strictIp = parseCanonicalIpAddress(normalized);
|
|
2491
|
+
if (strictIp) {
|
|
2492
|
+
if (strictIp.kind() === "ipv4") return isBlockedSpecialUseIpv4Address(strictIp, blockOptions);
|
|
2493
|
+
const v6 = strictIp;
|
|
1631
2494
|
if (isBlockedSpecialUseIpv6Address(v6)) return true;
|
|
1632
|
-
const
|
|
1633
|
-
if (
|
|
2495
|
+
const embeddedIpv4 = extractEmbeddedIpv4FromIpv6(v6);
|
|
2496
|
+
if (embeddedIpv4) return isBlockedSpecialUseIpv4Address(embeddedIpv4, blockOptions);
|
|
1634
2497
|
return false;
|
|
1635
|
-
} catch {
|
|
1636
2498
|
}
|
|
1637
|
-
if (
|
|
1638
|
-
if (normalized
|
|
2499
|
+
if (normalized.includes(":") && !parseLooseIpAddress(normalized)) return true;
|
|
2500
|
+
if (!isCanonicalDottedDecimalIPv4(normalized) && isLegacyIpv4Literal(normalized)) return true;
|
|
2501
|
+
if (looksLikeUnsupportedIpv4Literal(normalized)) return true;
|
|
1639
2502
|
return false;
|
|
1640
2503
|
}
|
|
1641
2504
|
function normalizeHostnameSet(values) {
|
|
@@ -1717,13 +2580,7 @@ async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
|
|
|
1717
2580
|
);
|
|
1718
2581
|
}
|
|
1719
2582
|
if (!skipPrivateNetworkChecks) {
|
|
1720
|
-
if (
|
|
1721
|
-
throw new InvalidBrowserNavigationUrlError(
|
|
1722
|
-
`Navigation to internal/loopback address blocked: "${hostname}". ssrfPolicy.dangerouslyAllowPrivateNetwork is false (strict mode).`
|
|
1723
|
-
);
|
|
1724
|
-
}
|
|
1725
|
-
const ipOpts = { allowRfc2544BenchmarkRange: params.policy?.allowRfc2544BenchmarkRange };
|
|
1726
|
-
if (isPrivateIpAddress(normalized, ipOpts)) {
|
|
2583
|
+
if (isBlockedHostnameOrIp(normalized, params.policy)) {
|
|
1727
2584
|
throw new InvalidBrowserNavigationUrlError(
|
|
1728
2585
|
`Navigation to internal/loopback address blocked: "${hostname}". ssrfPolicy.dangerouslyAllowPrivateNetwork is false (strict mode).`
|
|
1729
2586
|
);
|
|
@@ -1744,9 +2601,8 @@ async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
|
|
|
1744
2601
|
);
|
|
1745
2602
|
}
|
|
1746
2603
|
if (!skipPrivateNetworkChecks) {
|
|
1747
|
-
const ipOpts = { allowRfc2544BenchmarkRange: params.policy?.allowRfc2544BenchmarkRange };
|
|
1748
2604
|
for (const r of results) {
|
|
1749
|
-
if (
|
|
2605
|
+
if (isBlockedHostnameOrIp(r.address, params.policy)) {
|
|
1750
2606
|
throw new InvalidBrowserNavigationUrlError(
|
|
1751
2607
|
`Navigation to internal/loopback address blocked: "${hostname}" resolves to "${r.address}". ssrfPolicy.dangerouslyAllowPrivateNetwork is false (strict mode).`
|
|
1752
2608
|
);
|
|
@@ -1767,6 +2623,7 @@ async function resolvePinnedHostnameWithPolicy(hostname, params = {}) {
|
|
|
1767
2623
|
}
|
|
1768
2624
|
async function assertBrowserNavigationAllowed(opts) {
|
|
1769
2625
|
const rawUrl = String(opts.url ?? "").trim();
|
|
2626
|
+
if (!rawUrl) throw new InvalidBrowserNavigationUrlError("url is required");
|
|
1770
2627
|
let parsed;
|
|
1771
2628
|
try {
|
|
1772
2629
|
parsed = new URL(rawUrl);
|
|
@@ -1923,12 +2780,30 @@ function resolveBoundedDelayMs(value, label, maxMs) {
|
|
|
1923
2780
|
if (normalized > maxMs) throw new Error(`${label} exceeds maximum of ${maxMs}ms`);
|
|
1924
2781
|
return normalized;
|
|
1925
2782
|
}
|
|
1926
|
-
|
|
2783
|
+
function resolveInteractionTimeoutMs(timeoutMs) {
|
|
2784
|
+
return Math.max(500, Math.min(6e4, Math.floor(timeoutMs ?? 8e3)));
|
|
2785
|
+
}
|
|
2786
|
+
function requireRefOrSelector(ref, selector) {
|
|
2787
|
+
const trimmedRef = typeof ref === "string" ? ref.trim() : "";
|
|
2788
|
+
const trimmedSelector = typeof selector === "string" ? selector.trim() : "";
|
|
2789
|
+
if (!trimmedRef && !trimmedSelector) throw new Error("ref or selector is required");
|
|
2790
|
+
return { ref: trimmedRef || void 0, selector: trimmedSelector || void 0 };
|
|
2791
|
+
}
|
|
2792
|
+
function resolveLocator(page, resolved) {
|
|
2793
|
+
return resolved.ref ? refLocator(page, resolved.ref) : page.locator(resolved.selector);
|
|
2794
|
+
}
|
|
2795
|
+
async function getRestoredPageForTarget(opts) {
|
|
1927
2796
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
1928
2797
|
ensurePageState(page);
|
|
1929
2798
|
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
1930
|
-
|
|
1931
|
-
|
|
2799
|
+
return page;
|
|
2800
|
+
}
|
|
2801
|
+
async function clickViaPlaywright(opts) {
|
|
2802
|
+
const resolved = requireRefOrSelector(opts.ref, opts.selector);
|
|
2803
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2804
|
+
const label = resolved.ref ?? resolved.selector;
|
|
2805
|
+
const locator = resolveLocator(page, resolved);
|
|
2806
|
+
const timeout = resolveInteractionTimeoutMs(opts.timeoutMs);
|
|
1932
2807
|
try {
|
|
1933
2808
|
const delayMs = resolveBoundedDelayMs(opts.delayMs, "click delayMs", MAX_CLICK_DELAY_MS);
|
|
1934
2809
|
if (delayMs > 0) {
|
|
@@ -1941,28 +2816,27 @@ async function clickViaPlaywright(opts) {
|
|
|
1941
2816
|
await locator.click({ timeout, button: opts.button, modifiers: opts.modifiers });
|
|
1942
2817
|
}
|
|
1943
2818
|
} catch (err) {
|
|
1944
|
-
throw toAIFriendlyError(err,
|
|
2819
|
+
throw toAIFriendlyError(err, label);
|
|
1945
2820
|
}
|
|
1946
2821
|
}
|
|
1947
2822
|
async function hoverViaPlaywright(opts) {
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
2823
|
+
const resolved = requireRefOrSelector(opts.ref, opts.selector);
|
|
2824
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2825
|
+
const label = resolved.ref ?? resolved.selector;
|
|
2826
|
+
const locator = resolveLocator(page, resolved);
|
|
1951
2827
|
try {
|
|
1952
|
-
await
|
|
1953
|
-
timeout: normalizeTimeoutMs(opts.timeoutMs, 8e3, 6e4)
|
|
1954
|
-
});
|
|
2828
|
+
await locator.hover({ timeout: resolveInteractionTimeoutMs(opts.timeoutMs) });
|
|
1955
2829
|
} catch (err) {
|
|
1956
|
-
throw toAIFriendlyError(err,
|
|
2830
|
+
throw toAIFriendlyError(err, label);
|
|
1957
2831
|
}
|
|
1958
2832
|
}
|
|
1959
2833
|
async function typeViaPlaywright(opts) {
|
|
2834
|
+
const resolved = requireRefOrSelector(opts.ref, opts.selector);
|
|
1960
2835
|
const text = String(opts.text ?? "");
|
|
1961
|
-
const page = await
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
const
|
|
1965
|
-
const timeout = normalizeTimeoutMs(opts.timeoutMs, 8e3, 6e4);
|
|
2836
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2837
|
+
const label = resolved.ref ?? resolved.selector;
|
|
2838
|
+
const locator = resolveLocator(page, resolved);
|
|
2839
|
+
const timeout = resolveInteractionTimeoutMs(opts.timeoutMs);
|
|
1966
2840
|
try {
|
|
1967
2841
|
if (opts.slowly) {
|
|
1968
2842
|
await locator.click({ timeout });
|
|
@@ -1972,39 +2846,38 @@ async function typeViaPlaywright(opts) {
|
|
|
1972
2846
|
}
|
|
1973
2847
|
if (opts.submit) await locator.press("Enter", { timeout });
|
|
1974
2848
|
} catch (err) {
|
|
1975
|
-
throw toAIFriendlyError(err,
|
|
2849
|
+
throw toAIFriendlyError(err, label);
|
|
1976
2850
|
}
|
|
1977
2851
|
}
|
|
1978
2852
|
async function selectOptionViaPlaywright(opts) {
|
|
2853
|
+
const resolved = requireRefOrSelector(opts.ref, opts.selector);
|
|
1979
2854
|
if (!opts.values?.length) throw new Error("values are required");
|
|
1980
|
-
const page = await
|
|
1981
|
-
|
|
1982
|
-
|
|
2855
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2856
|
+
const label = resolved.ref ?? resolved.selector;
|
|
2857
|
+
const locator = resolveLocator(page, resolved);
|
|
1983
2858
|
try {
|
|
1984
|
-
await
|
|
1985
|
-
timeout: normalizeTimeoutMs(opts.timeoutMs, 8e3, 6e4)
|
|
1986
|
-
});
|
|
2859
|
+
await locator.selectOption(opts.values, { timeout: resolveInteractionTimeoutMs(opts.timeoutMs) });
|
|
1987
2860
|
} catch (err) {
|
|
1988
|
-
throw toAIFriendlyError(err,
|
|
2861
|
+
throw toAIFriendlyError(err, label);
|
|
1989
2862
|
}
|
|
1990
2863
|
}
|
|
1991
2864
|
async function dragViaPlaywright(opts) {
|
|
1992
|
-
const
|
|
1993
|
-
|
|
1994
|
-
|
|
2865
|
+
const resolvedStart = requireRefOrSelector(opts.startRef, opts.startSelector);
|
|
2866
|
+
const resolvedEnd = requireRefOrSelector(opts.endRef, opts.endSelector);
|
|
2867
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2868
|
+
const startLocator = resolveLocator(page, resolvedStart);
|
|
2869
|
+
const endLocator = resolveLocator(page, resolvedEnd);
|
|
2870
|
+
const startLabel = resolvedStart.ref ?? resolvedStart.selector;
|
|
2871
|
+
const endLabel = resolvedEnd.ref ?? resolvedEnd.selector;
|
|
1995
2872
|
try {
|
|
1996
|
-
await
|
|
1997
|
-
timeout: normalizeTimeoutMs(opts.timeoutMs, 8e3, 6e4)
|
|
1998
|
-
});
|
|
2873
|
+
await startLocator.dragTo(endLocator, { timeout: resolveInteractionTimeoutMs(opts.timeoutMs) });
|
|
1999
2874
|
} catch (err) {
|
|
2000
|
-
throw toAIFriendlyError(err, `${
|
|
2875
|
+
throw toAIFriendlyError(err, `${startLabel} -> ${endLabel}`);
|
|
2001
2876
|
}
|
|
2002
2877
|
}
|
|
2003
2878
|
async function fillFormViaPlaywright(opts) {
|
|
2004
|
-
const page = await
|
|
2005
|
-
|
|
2006
|
-
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
2007
|
-
const timeout = normalizeTimeoutMs(opts.timeoutMs, 8e3, 6e4);
|
|
2879
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2880
|
+
const timeout = resolveInteractionTimeoutMs(opts.timeoutMs);
|
|
2008
2881
|
for (const field of opts.fields) {
|
|
2009
2882
|
const ref = field.ref.trim();
|
|
2010
2883
|
const type = (typeof field.type === "string" ? field.type.trim() : "") || "text";
|
|
@@ -2029,21 +2902,18 @@ async function fillFormViaPlaywright(opts) {
|
|
|
2029
2902
|
}
|
|
2030
2903
|
}
|
|
2031
2904
|
async function scrollIntoViewViaPlaywright(opts) {
|
|
2032
|
-
const
|
|
2033
|
-
|
|
2034
|
-
|
|
2905
|
+
const resolved = requireRefOrSelector(opts.ref, opts.selector);
|
|
2906
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2907
|
+
const label = resolved.ref ?? resolved.selector;
|
|
2908
|
+
const locator = resolveLocator(page, resolved);
|
|
2035
2909
|
try {
|
|
2036
|
-
await
|
|
2037
|
-
timeout: normalizeTimeoutMs(opts.timeoutMs, 2e4)
|
|
2038
|
-
});
|
|
2910
|
+
await locator.scrollIntoViewIfNeeded({ timeout: normalizeTimeoutMs(opts.timeoutMs, 2e4) });
|
|
2039
2911
|
} catch (err) {
|
|
2040
|
-
throw toAIFriendlyError(err,
|
|
2912
|
+
throw toAIFriendlyError(err, label);
|
|
2041
2913
|
}
|
|
2042
2914
|
}
|
|
2043
2915
|
async function highlightViaPlaywright(opts) {
|
|
2044
|
-
const page = await
|
|
2045
|
-
ensurePageState(page);
|
|
2046
|
-
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
2916
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2047
2917
|
try {
|
|
2048
2918
|
await refLocator(page, opts.ref).highlight();
|
|
2049
2919
|
} catch (err) {
|
|
@@ -2051,9 +2921,7 @@ async function highlightViaPlaywright(opts) {
|
|
|
2051
2921
|
}
|
|
2052
2922
|
}
|
|
2053
2923
|
async function setInputFilesViaPlaywright(opts) {
|
|
2054
|
-
const page = await
|
|
2055
|
-
ensurePageState(page);
|
|
2056
|
-
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
2924
|
+
const page = await getRestoredPageForTarget(opts);
|
|
2057
2925
|
if (!opts.paths.length) throw new Error("paths are required");
|
|
2058
2926
|
const inputRef = typeof opts.ref === "string" ? opts.ref.trim() : "";
|
|
2059
2927
|
const element = typeof opts.element === "string" ? opts.element.trim() : "";
|
|
@@ -2246,13 +3114,18 @@ async function resizeViewportViaPlaywright(opts) {
|
|
|
2246
3114
|
|
|
2247
3115
|
// src/actions/wait.ts
|
|
2248
3116
|
var MAX_WAIT_TIME_MS = 3e4;
|
|
3117
|
+
function resolveBoundedDelayMs2(value, label, maxMs) {
|
|
3118
|
+
const normalized = Math.floor(value ?? 0);
|
|
3119
|
+
if (!Number.isFinite(normalized) || normalized < 0) throw new Error(`${label} must be >= 0`);
|
|
3120
|
+
if (normalized > maxMs) throw new Error(`${label} exceeds maximum of ${maxMs}ms`);
|
|
3121
|
+
return normalized;
|
|
3122
|
+
}
|
|
2249
3123
|
async function waitForViaPlaywright(opts) {
|
|
2250
3124
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
2251
3125
|
ensurePageState(page);
|
|
2252
3126
|
const timeout = normalizeTimeoutMs(opts.timeoutMs, 2e4);
|
|
2253
3127
|
if (typeof opts.timeMs === "number" && Number.isFinite(opts.timeMs)) {
|
|
2254
|
-
|
|
2255
|
-
await page.waitForTimeout(bounded);
|
|
3128
|
+
await page.waitForTimeout(resolveBoundedDelayMs2(opts.timeMs, "wait timeMs", MAX_WAIT_TIME_MS));
|
|
2256
3129
|
}
|
|
2257
3130
|
if (opts.text) {
|
|
2258
3131
|
await page.getByText(opts.text).first().waitFor({ state: "visible", timeout });
|
|
@@ -2414,55 +3287,106 @@ async function evaluateViaPlaywright(opts) {
|
|
|
2414
3287
|
if (signal && abortListener) signal.removeEventListener("abort", abortListener);
|
|
2415
3288
|
}
|
|
2416
3289
|
}
|
|
3290
|
+
function createPageDownloadWaiter(page, timeoutMs) {
|
|
3291
|
+
let done = false;
|
|
3292
|
+
let timer;
|
|
3293
|
+
let handler;
|
|
3294
|
+
const cleanup = () => {
|
|
3295
|
+
if (timer) clearTimeout(timer);
|
|
3296
|
+
timer = void 0;
|
|
3297
|
+
if (handler) {
|
|
3298
|
+
page.off("download", handler);
|
|
3299
|
+
handler = void 0;
|
|
3300
|
+
}
|
|
3301
|
+
};
|
|
3302
|
+
return {
|
|
3303
|
+
promise: new Promise((resolve2, reject) => {
|
|
3304
|
+
handler = (download) => {
|
|
3305
|
+
if (done) return;
|
|
3306
|
+
done = true;
|
|
3307
|
+
cleanup();
|
|
3308
|
+
resolve2(download);
|
|
3309
|
+
};
|
|
3310
|
+
page.on("download", handler);
|
|
3311
|
+
timer = setTimeout(() => {
|
|
3312
|
+
if (done) return;
|
|
3313
|
+
done = true;
|
|
3314
|
+
cleanup();
|
|
3315
|
+
reject(new Error("Timeout waiting for download"));
|
|
3316
|
+
}, timeoutMs);
|
|
3317
|
+
}),
|
|
3318
|
+
cancel: () => {
|
|
3319
|
+
if (done) return;
|
|
3320
|
+
done = true;
|
|
3321
|
+
cleanup();
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
}
|
|
3325
|
+
async function saveDownloadPayload(download, outPath) {
|
|
3326
|
+
await writeViaSiblingTempPath({
|
|
3327
|
+
rootDir: path.dirname(outPath),
|
|
3328
|
+
targetPath: outPath,
|
|
3329
|
+
writeTemp: async (tempPath) => {
|
|
3330
|
+
await download.saveAs(tempPath);
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
return {
|
|
3334
|
+
url: download.url(),
|
|
3335
|
+
suggestedFilename: download.suggestedFilename(),
|
|
3336
|
+
path: outPath
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
async function awaitDownloadPayload(params) {
|
|
3340
|
+
try {
|
|
3341
|
+
const download = await params.waiter.promise;
|
|
3342
|
+
if (params.state.armIdDownload !== params.armId) throw new Error("Download was superseded by another waiter");
|
|
3343
|
+
return await saveDownloadPayload(download, params.outPath);
|
|
3344
|
+
} catch (err) {
|
|
3345
|
+
params.waiter.cancel();
|
|
3346
|
+
throw err;
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
2417
3349
|
async function downloadViaPlaywright(opts) {
|
|
2418
3350
|
await assertSafeOutputPath(opts.path, opts.allowedOutputRoots);
|
|
2419
3351
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
2420
3352
|
const state = ensurePageState(page);
|
|
2421
3353
|
restoreRoleRefsForTarget({ cdpUrl: opts.cdpUrl, targetId: opts.targetId, page });
|
|
2422
3354
|
const timeout = normalizeTimeoutMs(opts.timeoutMs, 12e4);
|
|
2423
|
-
const
|
|
3355
|
+
const outPath = String(opts.path ?? "").trim();
|
|
3356
|
+
if (!outPath) throw new Error("path is required");
|
|
2424
3357
|
state.armIdDownload = bumpDownloadArmId();
|
|
3358
|
+
const armId = state.armIdDownload;
|
|
3359
|
+
const waiter = createPageDownloadWaiter(page, timeout);
|
|
2425
3360
|
try {
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2428
|
-
locator.click({ timeout })
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
targetPath: outPath,
|
|
2434
|
-
writeTemp: async (tempPath) => {
|
|
2435
|
-
await download.saveAs(tempPath);
|
|
2436
|
-
}
|
|
2437
|
-
});
|
|
2438
|
-
return {
|
|
2439
|
-
url: download.url(),
|
|
2440
|
-
suggestedFilename: download.suggestedFilename(),
|
|
2441
|
-
path: outPath
|
|
2442
|
-
};
|
|
3361
|
+
const locator = refLocator(page, opts.ref);
|
|
3362
|
+
try {
|
|
3363
|
+
await locator.click({ timeout });
|
|
3364
|
+
} catch (err) {
|
|
3365
|
+
throw toAIFriendlyError(err, opts.ref);
|
|
3366
|
+
}
|
|
3367
|
+
return await awaitDownloadPayload({ waiter, state, armId, outPath });
|
|
2443
3368
|
} catch (err) {
|
|
2444
|
-
|
|
3369
|
+
waiter.cancel();
|
|
3370
|
+
throw err;
|
|
2445
3371
|
}
|
|
2446
3372
|
}
|
|
2447
3373
|
async function waitForDownloadViaPlaywright(opts) {
|
|
2448
3374
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
2449
|
-
ensurePageState(page);
|
|
3375
|
+
const state = ensurePageState(page);
|
|
2450
3376
|
const timeout = normalizeTimeoutMs(opts.timeoutMs, 12e4);
|
|
2451
|
-
|
|
2452
|
-
const
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
})
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
path: savePath
|
|
2465
|
-
};
|
|
3377
|
+
state.armIdDownload = bumpDownloadArmId();
|
|
3378
|
+
const armId = state.armIdDownload;
|
|
3379
|
+
const waiter = createPageDownloadWaiter(page, timeout);
|
|
3380
|
+
try {
|
|
3381
|
+
const download = await waiter.promise;
|
|
3382
|
+
if (state.armIdDownload !== armId) throw new Error("Download was superseded by another waiter");
|
|
3383
|
+
const savePath = opts.path ?? download.suggestedFilename();
|
|
3384
|
+
await assertSafeOutputPath(savePath, opts.allowedOutputRoots);
|
|
3385
|
+
return await saveDownloadPayload(download, savePath);
|
|
3386
|
+
} catch (err) {
|
|
3387
|
+
waiter.cancel();
|
|
3388
|
+
throw err;
|
|
3389
|
+
}
|
|
2466
3390
|
}
|
|
2467
3391
|
async function emulateMediaViaPlaywright(opts) {
|
|
2468
3392
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
@@ -2577,7 +3501,7 @@ async function setLocaleViaPlaywright(opts) {
|
|
|
2577
3501
|
async function setOfflineViaPlaywright(opts) {
|
|
2578
3502
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|
|
2579
3503
|
ensurePageState(page);
|
|
2580
|
-
await page.context().setOffline(opts.offline);
|
|
3504
|
+
await page.context().setOffline(Boolean(opts.offline));
|
|
2581
3505
|
}
|
|
2582
3506
|
async function setTimezoneViaPlaywright(opts) {
|
|
2583
3507
|
const page = await getPageForTargetId({ cdpUrl: opts.cdpUrl, targetId: opts.targetId });
|