@wenathlan/extension 1.1.44 → 1.1.46
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 -4
- package/dist/cdpbus.d.ts +104 -0
- package/dist/cdpbus.d.ts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +937 -6
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +75 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +39 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +63 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/runtimeline.d.ts +122 -0
- package/dist/runtimeline.d.ts.map +1 -0
- package/dist/types.d.ts +271 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +1252 -9
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +439 -7
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +18 -3
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +281 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +6 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -196,6 +196,143 @@ function annotationplanof(input) {
|
|
|
196
196
|
return plan;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
// cdpbus.ts
|
|
200
|
+
var cdpkinds = ["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"];
|
|
201
|
+
var cdpdomains = ["Runtime", "Log", "Debugger", "DOM", "Network", "Page"];
|
|
202
|
+
function methoddomain(method) {
|
|
203
|
+
const match = /^([A-Z][A-Za-z]*)\.([a-zA-Z][A-Za-z0-9]*)$/.exec(method.trim());
|
|
204
|
+
return match?.[1];
|
|
205
|
+
}
|
|
206
|
+
function cdpallowlistof(value) {
|
|
207
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
208
|
+
const entry = value;
|
|
209
|
+
const domains = Array.isArray(entry.domains) ? entry.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
210
|
+
if (domains.length === 0) return void 0;
|
|
211
|
+
if (entry.methods === void 0) return { domains };
|
|
212
|
+
const methods = Array.isArray(entry.methods) ? entry.methods.filter((method) => typeof method === "string" && methoddomain(method) !== void 0 && domains.includes(methoddomain(method))) : [];
|
|
213
|
+
if (methods.length === 0) return void 0;
|
|
214
|
+
return { domains, methods };
|
|
215
|
+
}
|
|
216
|
+
function allowlistcovers(allowlist, method) {
|
|
217
|
+
const domain = methoddomain(method);
|
|
218
|
+
if (domain === void 0) return false;
|
|
219
|
+
if (!allowlist.domains.includes(domain)) return false;
|
|
220
|
+
if (allowlist.methods !== void 0 && !allowlist.methods.includes(method)) return false;
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
function attachcdpsession(input) {
|
|
224
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, tabid: input.tabid, origin: input.origin, attachedat: input.now, domains: [...new Set(input.domains)], debuggerversion: input.debuggerversion };
|
|
225
|
+
}
|
|
226
|
+
function detachcdpsession(session, at, userdetached = false) {
|
|
227
|
+
return { ...session, detachedat: at, ...userdetached ? { userdetached: true } : {} };
|
|
228
|
+
}
|
|
229
|
+
function sendcdpcommand(input) {
|
|
230
|
+
const domain = methoddomain(input.method);
|
|
231
|
+
if (domain === void 0) return { ...input, method: input.method.trim(), domain: "", duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : { errorclass: "malformedmethod" }, at: input.at };
|
|
232
|
+
return { ...input, method: input.method.trim(), domain, duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : {}, at: input.at };
|
|
233
|
+
}
|
|
234
|
+
function serializecdpcommand(queue, command) {
|
|
235
|
+
return [...queue, command];
|
|
236
|
+
}
|
|
237
|
+
function cdpeventruleof(value) {
|
|
238
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
239
|
+
const entry = value;
|
|
240
|
+
const domain = typeof entry.domain === "string" && cdpdomains.includes(entry.domain) ? entry.domain : void 0;
|
|
241
|
+
const event = typeof entry.event === "string" && entry.event.trim() ? entry.event.trim() : void 0;
|
|
242
|
+
if (domain === void 0 || event === void 0) return void 0;
|
|
243
|
+
const match = typeof entry.match === "string" && entry.match.trim() ? entry.match.trim() : void 0;
|
|
244
|
+
return { domain, event, ...match !== void 0 ? { match } : {} };
|
|
245
|
+
}
|
|
246
|
+
function watchcdpevents(rules, events) {
|
|
247
|
+
const matched = [];
|
|
248
|
+
const counts = {};
|
|
249
|
+
for (const domain of cdpdomains) counts[domain] = 0;
|
|
250
|
+
for (const event of events) {
|
|
251
|
+
for (const rule of rules) {
|
|
252
|
+
if (rule.domain !== event.domain || rule.event !== event.event) continue;
|
|
253
|
+
if (rule.match !== void 0 && !(event.payload ?? "").includes(rule.match)) continue;
|
|
254
|
+
matched.push({ ruleid: rule.id, domain: event.domain, event: event.event, ...event.payload !== void 0 ? { payload: event.payload } : {} });
|
|
255
|
+
counts[event.domain] = (counts[event.domain] ?? 0) + 1;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return { matched, counts };
|
|
259
|
+
}
|
|
260
|
+
function breakpointinputof(value) {
|
|
261
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
262
|
+
const entry = value;
|
|
263
|
+
const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : void 0;
|
|
264
|
+
const line = typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : void 0;
|
|
265
|
+
if (url === void 0 || line === void 0) return void 0;
|
|
266
|
+
const column = typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : void 0;
|
|
267
|
+
const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : void 0;
|
|
268
|
+
return { url, line, ...column !== void 0 ? { column } : {}, ...condition !== void 0 ? { condition } : {} };
|
|
269
|
+
}
|
|
270
|
+
function capturepause(input) {
|
|
271
|
+
return { id: input.id, runid: input.runid, stepid: input.stepid, reason: input.reason, callframes: [...input.callframes], ...input.hitbreakpoint !== void 0 ? { hitbreakpoint: input.hitbreakpoint } : {}, ...input.domsnapshotid !== void 0 ? { domsnapshotid: input.domsnapshotid } : {}, at: input.at };
|
|
272
|
+
}
|
|
273
|
+
function stepmodeof(value) {
|
|
274
|
+
const modes = ["stepover", "stepinto", "stepout", "resume"];
|
|
275
|
+
return typeof value === "string" && modes.includes(value) ? value : void 0;
|
|
276
|
+
}
|
|
277
|
+
function watchexpressionof(value) {
|
|
278
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
279
|
+
const entry = value;
|
|
280
|
+
const expression = typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : void 0;
|
|
281
|
+
if (expression === void 0) return void 0;
|
|
282
|
+
const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
|
|
283
|
+
return { expression, scope };
|
|
284
|
+
}
|
|
285
|
+
function recordwatchvalue(expression, pauseid, value, at) {
|
|
286
|
+
return { ...expression, values: [...expression.values, { pauseid, value, at }] };
|
|
287
|
+
}
|
|
288
|
+
function overrideinputof(value) {
|
|
289
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
290
|
+
const entry = value;
|
|
291
|
+
const urlpattern = typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : void 0;
|
|
292
|
+
const source = typeof entry.source === "string" ? entry.source : void 0;
|
|
293
|
+
if (urlpattern === void 0 || source === void 0 || source.trim().length === 0) return void 0;
|
|
294
|
+
return { urlpattern, source };
|
|
295
|
+
}
|
|
296
|
+
function overridematches(urlpattern, url) {
|
|
297
|
+
const patternmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(urlpattern);
|
|
298
|
+
const urlmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(url);
|
|
299
|
+
if (!patternmatch || !urlmatch) return false;
|
|
300
|
+
if (patternmatch[1] !== urlmatch[1]) return false;
|
|
301
|
+
const patternpath = (patternmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
|
|
302
|
+
const urlpath = (urlmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
|
|
303
|
+
const walk = (patternindex, urlindex) => {
|
|
304
|
+
if (patternindex >= patternpath.length) return urlindex >= urlpath.length;
|
|
305
|
+
const segment = patternpath[patternindex];
|
|
306
|
+
if (segment === "**") return walk(patternindex + 1, urlindex) || urlindex < urlpath.length && walk(patternindex, urlindex + 1);
|
|
307
|
+
if (urlindex >= urlpath.length) return false;
|
|
308
|
+
if (segment !== "*" && segment !== urlpath[urlindex]) return false;
|
|
309
|
+
return walk(patternindex + 1, urlindex + 1);
|
|
310
|
+
};
|
|
311
|
+
return walk(0, 0);
|
|
312
|
+
}
|
|
313
|
+
function teardownplanof(value) {
|
|
314
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
315
|
+
const entry = value;
|
|
316
|
+
const revertsteps = Array.isArray(entry.revertsteps) ? entry.revertsteps.filter((step) => typeof step === "string" && step.trim().length > 0) : [];
|
|
317
|
+
const policy = entry.resumepolicy;
|
|
318
|
+
if (revertsteps.length === 0) return void 0;
|
|
319
|
+
if (policy !== void 0 && policy !== "resume" && policy !== "pause" && policy !== "ask") return void 0;
|
|
320
|
+
return { revertsteps, resumepolicy: policy ?? "ask" };
|
|
321
|
+
}
|
|
322
|
+
function teardowncdpsession(input) {
|
|
323
|
+
const revertedbreakpoints = input.breakpoints.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
324
|
+
const revertedoverrides = input.overrides.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
325
|
+
const resumepolicy = input.userdetached ? "pause" : input.plan?.resumepolicy ?? "ask";
|
|
326
|
+
return {
|
|
327
|
+
session: detachcdpsession(input.session, input.at, input.userdetached),
|
|
328
|
+
revertedbreakpoints,
|
|
329
|
+
revertedoverrides,
|
|
330
|
+
resumepolicy,
|
|
331
|
+
paused: input.userdetached || resumepolicy === "pause",
|
|
332
|
+
keepsalive: input.userdetached
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
199
336
|
// httpclient.ts
|
|
200
337
|
var httpkinds = ["fetchurl", "parsejson", "parsehtml", "callrest", "callgraphql"];
|
|
201
338
|
var redirectstatuses = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
@@ -1828,6 +1965,205 @@ var sessionmemory = class {
|
|
|
1828
1965
|
if (live.length !== records.length) await this.adapter.set("ratelimits", live);
|
|
1829
1966
|
return live;
|
|
1830
1967
|
}
|
|
1968
|
+
/** Stores one run timeline entry; the user configured timeline retention window expires the oldest entries while their level counts survive in the per run level summaries. */
|
|
1969
|
+
async addtimelineentry(entry) {
|
|
1970
|
+
const records = await this.gettimeline();
|
|
1971
|
+
const combined = [entry, ...records];
|
|
1972
|
+
const retention = (await this.getsettings())?.timelineretention;
|
|
1973
|
+
if (retention === void 0) {
|
|
1974
|
+
await this.adapter.set("timelineentries", combined);
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
const kept = combined.slice(0, retention);
|
|
1978
|
+
const expired = combined.slice(retention);
|
|
1979
|
+
if (expired.length > 0) {
|
|
1980
|
+
const expiredcounts = /* @__PURE__ */ new Map();
|
|
1981
|
+
for (const item of expired) {
|
|
1982
|
+
const counts = expiredcounts.get(item.runid) ?? {};
|
|
1983
|
+
counts[item.level] = (counts[item.level] ?? 0) + 1;
|
|
1984
|
+
expiredcounts.set(item.runid, counts);
|
|
1985
|
+
}
|
|
1986
|
+
for (const [runid, counts] of expiredcounts) await this.mergelevelsummary(runid, counts, Date.now());
|
|
1987
|
+
}
|
|
1988
|
+
await this.adapter.set("timelineentries", kept);
|
|
1989
|
+
}
|
|
1990
|
+
/** Returns every stored run timeline entry, newest first. */
|
|
1991
|
+
async gettimeline() {
|
|
1992
|
+
return await this.adapter.get("timelineentries") ?? [];
|
|
1993
|
+
}
|
|
1994
|
+
/** Returns the run timeline entries filtered by run, level and step id. */
|
|
1995
|
+
async listtimeline(filter) {
|
|
1996
|
+
const records = await this.gettimeline();
|
|
1997
|
+
return records.filter((item) => (filter.runid === void 0 || item.runid === filter.runid) && (filter.level === void 0 || item.level === filter.level) && (filter.stepid === void 0 || item.stepid === filter.stepid));
|
|
1998
|
+
}
|
|
1999
|
+
/** Stores one captured javascript error record with its stack frames, source url and line. */
|
|
2000
|
+
async adderrorrecord(record2) {
|
|
2001
|
+
const records = await this.adapter.get("errorrecords") ?? [];
|
|
2002
|
+
await this.adapter.set("errorrecords", [record2, ...records]);
|
|
2003
|
+
}
|
|
2004
|
+
/** Returns every stored error record, newest first. */
|
|
2005
|
+
async geterrorrecords() {
|
|
2006
|
+
return await this.adapter.get("errorrecords") ?? [];
|
|
2007
|
+
}
|
|
2008
|
+
/** Stores one captured unhandled rejection record with its reason and stack frames. */
|
|
2009
|
+
async addrejectionrecord(record2) {
|
|
2010
|
+
const records = await this.adapter.get("rejectionrecords") ?? [];
|
|
2011
|
+
await this.adapter.set("rejectionrecords", [record2, ...records]);
|
|
2012
|
+
}
|
|
2013
|
+
/** Returns every stored rejection record, newest first. */
|
|
2014
|
+
async getrejectionrecords() {
|
|
2015
|
+
return await this.adapter.get("rejectionrecords") ?? [];
|
|
2016
|
+
}
|
|
2017
|
+
/** Stores one captured long task entry with its duration, start time and attribution names. */
|
|
2018
|
+
async addlongtask(record2) {
|
|
2019
|
+
const records = await this.adapter.get("longtasks") ?? [];
|
|
2020
|
+
await this.adapter.set("longtasks", [record2, ...records]);
|
|
2021
|
+
}
|
|
2022
|
+
/** Returns every stored long task entry, newest first. */
|
|
2023
|
+
async getlongtasks() {
|
|
2024
|
+
return await this.adapter.get("longtasks") ?? [];
|
|
2025
|
+
}
|
|
2026
|
+
/** Stores one console diff result between two runs, replacing the previous one. */
|
|
2027
|
+
async addconsolediff(diff) {
|
|
2028
|
+
return this.adapter.set("consolediff", diff);
|
|
2029
|
+
}
|
|
2030
|
+
/** Returns the one stored console diff result. */
|
|
2031
|
+
async getdiff() {
|
|
2032
|
+
return this.adapter.get("consolediff");
|
|
2033
|
+
}
|
|
2034
|
+
/** Stores one log rotation target record with its overflow entry counts, replacing the previous record of that target and run. */
|
|
2035
|
+
async addrotationtarget(record2) {
|
|
2036
|
+
const records = (await this.adapter.get("rotationtargets") ?? []).filter((item) => !(item.target === record2.target && item.runid === record2.runid));
|
|
2037
|
+
await this.adapter.set("rotationtargets", [record2, ...records]);
|
|
2038
|
+
}
|
|
2039
|
+
/** Returns every stored rotation target record with its overflow entry counts, newest first. */
|
|
2040
|
+
async getrotationtargets() {
|
|
2041
|
+
return await this.adapter.get("rotationtargets") ?? [];
|
|
2042
|
+
}
|
|
2043
|
+
/** Stores one console capture consent decision per origin; the approved decision persists so console watching on that origin prompts once. */
|
|
2044
|
+
async setconsoleconsent(consent) {
|
|
2045
|
+
const records = (await this.adapter.get("consoleconsents") ?? []).filter((item) => item.id !== consent.id);
|
|
2046
|
+
await this.adapter.set("consoleconsents", [consent, ...records]);
|
|
2047
|
+
}
|
|
2048
|
+
/** Returns every console capture consent decision, newest first. */
|
|
2049
|
+
async getconsoleconsents() {
|
|
2050
|
+
return await this.adapter.get("consoleconsents") ?? [];
|
|
2051
|
+
}
|
|
2052
|
+
/** Stores one devtools session record with its enabled domains and detach state, replacing the previous record of its id. */
|
|
2053
|
+
async setcdpsession(session) {
|
|
2054
|
+
const records = (await this.adapter.get("cdpsessions") ?? []).filter((item) => item.id !== session.id);
|
|
2055
|
+
await this.adapter.set("cdpsessions", [session, ...records]);
|
|
2056
|
+
}
|
|
2057
|
+
/** Returns every stored devtools session record, newest first. */
|
|
2058
|
+
async getcdpsessions() {
|
|
2059
|
+
return await this.adapter.get("cdpsessions") ?? [];
|
|
2060
|
+
}
|
|
2061
|
+
/** Stores one raw command outcome with its duration and error class. */
|
|
2062
|
+
async addcdpcommand(command) {
|
|
2063
|
+
const records = await this.adapter.get("cdpcommands") ?? [];
|
|
2064
|
+
await this.adapter.set("cdpcommands", [command, ...records]);
|
|
2065
|
+
}
|
|
2066
|
+
/** Returns every stored raw command outcome, newest first. */
|
|
2067
|
+
async getcdpcommands() {
|
|
2068
|
+
return await this.adapter.get("cdpcommands") ?? [];
|
|
2069
|
+
}
|
|
2070
|
+
/** Stores one domain event rule with its match filter, replacing the previous rule of its id. */
|
|
2071
|
+
async setcdpeventrule(rule) {
|
|
2072
|
+
const records = (await this.adapter.get("cdpeventrules") ?? []).filter((item) => item.id !== rule.id);
|
|
2073
|
+
await this.adapter.set("cdpeventrules", [rule, ...records]);
|
|
2074
|
+
}
|
|
2075
|
+
/** Returns every stored domain event rule, newest first. */
|
|
2076
|
+
async getcdpeventrules() {
|
|
2077
|
+
return await this.adapter.get("cdpeventrules") ?? [];
|
|
2078
|
+
}
|
|
2079
|
+
/** Stores one breakpoint record with its condition and hit counter, replacing the previous record of its id. */
|
|
2080
|
+
async addbreakpoint(spec) {
|
|
2081
|
+
const records = (await this.adapter.get("breakpoints") ?? []).filter((item) => item.id !== spec.id);
|
|
2082
|
+
await this.adapter.set("breakpoints", [spec, ...records]);
|
|
2083
|
+
}
|
|
2084
|
+
/** Returns every stored breakpoint record, newest first. */
|
|
2085
|
+
async getbreakpoints() {
|
|
2086
|
+
return await this.adapter.get("breakpoints") ?? [];
|
|
2087
|
+
}
|
|
2088
|
+
/** Stores one pause state capture; the user configured pause retention window expires the call frames and the dom snapshot reference of the oldest captures while the pause reason and hit breakpoint survive. */
|
|
2089
|
+
async addpause(pause) {
|
|
2090
|
+
const records = await this.getpauses();
|
|
2091
|
+
const combined = [pause, ...records.filter((item) => item.id !== pause.id)];
|
|
2092
|
+
const retention = (await this.getsettings())?.pauseretention;
|
|
2093
|
+
if (retention === void 0) {
|
|
2094
|
+
await this.adapter.set("pauses", combined);
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
2097
|
+
const kept = combined.slice(0, retention);
|
|
2098
|
+
const expired = combined.slice(retention).map((item) => {
|
|
2099
|
+
if (item.framesexpired === true) return item;
|
|
2100
|
+
const faded = { ...item, callframes: [], framesexpired: true };
|
|
2101
|
+
delete faded.domsnapshotid;
|
|
2102
|
+
return faded;
|
|
2103
|
+
});
|
|
2104
|
+
await this.adapter.set("pauses", [...kept, ...expired]);
|
|
2105
|
+
}
|
|
2106
|
+
/** Returns every stored pause state capture, newest first. */
|
|
2107
|
+
async getpauses() {
|
|
2108
|
+
return await this.adapter.get("pauses") ?? [];
|
|
2109
|
+
}
|
|
2110
|
+
/** Returns the pause state captures of one run, newest first. */
|
|
2111
|
+
async listpauses(runid) {
|
|
2112
|
+
const records = await this.getpauses();
|
|
2113
|
+
return records.filter((item) => item.runid === runid);
|
|
2114
|
+
}
|
|
2115
|
+
/** Stores one watch expression with its per pause values, replacing the previous expression of its id. */
|
|
2116
|
+
async setwatchexpression(expression) {
|
|
2117
|
+
const records = (await this.adapter.get("watchexpressions") ?? []).filter((item) => item.id !== expression.id);
|
|
2118
|
+
await this.adapter.set("watchexpressions", [expression, ...records]);
|
|
2119
|
+
}
|
|
2120
|
+
/** Returns every stored watch expression, newest first. */
|
|
2121
|
+
async getwatchexpressions() {
|
|
2122
|
+
return await this.adapter.get("watchexpressions") ?? [];
|
|
2123
|
+
}
|
|
2124
|
+
/** Stores one script override with its review provenance, replacing the previous override of its id. */
|
|
2125
|
+
async addscriptoverride(spec) {
|
|
2126
|
+
const records = (await this.adapter.get("scriptoverrides") ?? []).filter((item) => item.id !== spec.id);
|
|
2127
|
+
await this.adapter.set("scriptoverrides", [spec, ...records]);
|
|
2128
|
+
}
|
|
2129
|
+
/** Returns every stored script override, newest first. */
|
|
2130
|
+
async getscriptoverrides() {
|
|
2131
|
+
return await this.adapter.get("scriptoverrides") ?? [];
|
|
2132
|
+
}
|
|
2133
|
+
/** Stores one debugger consent decision per origin with the consented domain list, replacing the previous decision of its id. */
|
|
2134
|
+
async setdebuggergrant(grant) {
|
|
2135
|
+
const records = (await this.adapter.get("debuggergrants") ?? []).filter((item) => item.id !== grant.id);
|
|
2136
|
+
await this.adapter.set("debuggergrants", [grant, ...records]);
|
|
2137
|
+
}
|
|
2138
|
+
/** Returns every debugger consent decision, newest first. */
|
|
2139
|
+
async getdebuggergrants() {
|
|
2140
|
+
return await this.adapter.get("debuggergrants") ?? [];
|
|
2141
|
+
}
|
|
2142
|
+
/** Revokes every approved debugger consent of one origin: the revoke time stamps the records so the next attach needs a new reviewed prompt. */
|
|
2143
|
+
async revokedebuggergrants(origin, at) {
|
|
2144
|
+
const records = await this.getdebuggergrants();
|
|
2145
|
+
let revoked = 0;
|
|
2146
|
+
const updated = records.map((grant) => {
|
|
2147
|
+
if (grant.origin !== origin || grant.revokedat !== void 0) return grant;
|
|
2148
|
+
revoked += 1;
|
|
2149
|
+
return { ...grant, revokedat: at };
|
|
2150
|
+
});
|
|
2151
|
+
await this.adapter.set("debuggergrants", updated);
|
|
2152
|
+
return revoked;
|
|
2153
|
+
}
|
|
2154
|
+
/** Merges expired entry counts into the per run level count summary that survives the retention window. */
|
|
2155
|
+
async mergelevelsummary(runid, counts, now) {
|
|
2156
|
+
const records = await this.getlevelsummaries();
|
|
2157
|
+
const existing = records.find((item) => item.runid === runid);
|
|
2158
|
+
const merged = { ...existing?.counts ?? {} };
|
|
2159
|
+
for (const [level, count] of Object.entries(counts)) merged[level] = (merged[level] ?? 0) + count;
|
|
2160
|
+
const updated = { runid, counts: merged, at: now };
|
|
2161
|
+
await this.adapter.set("levelsummaries", [updated, ...records.filter((item) => item.runid !== runid)]);
|
|
2162
|
+
}
|
|
2163
|
+
/** Returns every per run level count summary, newest first. */
|
|
2164
|
+
async getlevelsummaries() {
|
|
2165
|
+
return await this.adapter.get("levelsummaries") ?? [];
|
|
2166
|
+
}
|
|
1831
2167
|
};
|
|
1832
2168
|
function mediakindof(record2) {
|
|
1833
2169
|
if ("pages" in record2) return "pdf";
|
|
@@ -2364,6 +2700,195 @@ function extractvalues(body, paths) {
|
|
|
2364
2700
|
return fields.map((field) => ({ path: field.path, ...field.value !== void 0 ? { value: field.value } : {}, ...field.missing ? { missing: true } : {} }));
|
|
2365
2701
|
}
|
|
2366
2702
|
|
|
2703
|
+
// runtimeline.ts
|
|
2704
|
+
var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
|
|
2705
|
+
var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
|
|
2706
|
+
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network", "cdp"];
|
|
2707
|
+
function levelrank(level) {
|
|
2708
|
+
return loglevels.indexOf(level);
|
|
2709
|
+
}
|
|
2710
|
+
function redactconsoletext(text2, patterns) {
|
|
2711
|
+
let redacted = text2;
|
|
2712
|
+
for (const pattern of patterns) {
|
|
2713
|
+
if (!pattern) continue;
|
|
2714
|
+
while (redacted.includes(pattern)) redacted = redacted.replace(pattern, "[redacted]");
|
|
2715
|
+
}
|
|
2716
|
+
return redacted;
|
|
2717
|
+
}
|
|
2718
|
+
function argkind(value) {
|
|
2719
|
+
if (value === null) return "null";
|
|
2720
|
+
if (Array.isArray(value)) return "array";
|
|
2721
|
+
if (value instanceof Error) return "error";
|
|
2722
|
+
switch (typeof value) {
|
|
2723
|
+
case "string":
|
|
2724
|
+
return "string";
|
|
2725
|
+
case "number":
|
|
2726
|
+
return "number";
|
|
2727
|
+
case "boolean":
|
|
2728
|
+
return "boolean";
|
|
2729
|
+
case "bigint":
|
|
2730
|
+
return "bigint";
|
|
2731
|
+
case "symbol":
|
|
2732
|
+
return "symbol";
|
|
2733
|
+
case "function":
|
|
2734
|
+
return "function";
|
|
2735
|
+
case "undefined":
|
|
2736
|
+
return "undefined";
|
|
2737
|
+
default:
|
|
2738
|
+
return "object";
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
function serializearg(value, depth) {
|
|
2742
|
+
const render = (item, remaining) => {
|
|
2743
|
+
if (item instanceof Error) return `${item.name}: ${item.message}`;
|
|
2744
|
+
if (typeof item === "string") return item;
|
|
2745
|
+
if (typeof item === "function") return `[function ${item.name || "anonymous"}]`;
|
|
2746
|
+
if (typeof item === "bigint") return `${item}n`;
|
|
2747
|
+
if (typeof item === "symbol") return item.toString();
|
|
2748
|
+
if (item === null || item === void 0 || typeof item !== "object") return String(item);
|
|
2749
|
+
if (remaining <= 0) {
|
|
2750
|
+
const tag = Array.isArray(item) ? "Array" : item.constructor?.name ?? "Object";
|
|
2751
|
+
return `[${tag}]`;
|
|
2752
|
+
}
|
|
2753
|
+
if (Array.isArray(item)) return `[${item.map((entry) => render(entry, remaining - 1)).join(", ")}]`;
|
|
2754
|
+
const record2 = item;
|
|
2755
|
+
return `{${Object.keys(record2).map((key) => `${key}: ${render(record2[key], remaining - 1)}`).join(", ")}}`;
|
|
2756
|
+
};
|
|
2757
|
+
return render(value, Math.max(0, depth));
|
|
2758
|
+
}
|
|
2759
|
+
function consolecapture(input) {
|
|
2760
|
+
const parts = input.args.map((arg) => serializearg(arg, input.depth));
|
|
2761
|
+
return { level: input.level, text: redactconsoletext(parts.join(" "), input.redact), argkinds: input.args.map((arg) => argkind(arg)), repeat: 1 };
|
|
2762
|
+
}
|
|
2763
|
+
function stackframes(stacktext) {
|
|
2764
|
+
const frames = [];
|
|
2765
|
+
for (const row of stacktext.split("\n")) {
|
|
2766
|
+
const trimmed = row.trim();
|
|
2767
|
+
if (!trimmed.startsWith("at ")) continue;
|
|
2768
|
+
const body = trimmed.slice(3).trim();
|
|
2769
|
+
const location = body.match(/\(([^()]*:\d+:\d+)\)$/) ?? body.match(/^(.*:\d+:\d+)$/);
|
|
2770
|
+
const located = location?.[1];
|
|
2771
|
+
if (!located) continue;
|
|
2772
|
+
const segments = located.split(":");
|
|
2773
|
+
const column = Number.parseInt(segments.pop() ?? "", 10);
|
|
2774
|
+
const lineno = Number.parseInt(segments.pop() ?? "", 10);
|
|
2775
|
+
const url = segments.join(":");
|
|
2776
|
+
if (!Number.isFinite(lineno) || lineno < 0) continue;
|
|
2777
|
+
const name = body.endsWith(`(${located})`) ? body.slice(0, body.length - located.length - 2).trim() : "";
|
|
2778
|
+
frames.push({ ...name ? { functionname: name } : {}, url, line: lineno, ...Number.isFinite(column) ? { column } : {} });
|
|
2779
|
+
}
|
|
2780
|
+
return frames;
|
|
2781
|
+
}
|
|
2782
|
+
function errorcapture(input) {
|
|
2783
|
+
return { message: redactconsoletext(input.message, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [], sourceurl: input.sourceurl, line: input.line };
|
|
2784
|
+
}
|
|
2785
|
+
function rejectioncapture(input) {
|
|
2786
|
+
return { reason: redactconsoletext(input.reason, input.redact), frames: input.stacktext !== void 0 ? stackframes(input.stacktext) : [] };
|
|
2787
|
+
}
|
|
2788
|
+
function longtaskcapture(input) {
|
|
2789
|
+
return input.entries.filter((entry) => entry.duration >= input.threshold).map((entry) => ({ duration: Math.round(entry.duration), starttime: Math.round(entry.starttime), attributions: [...entry.attributions] }));
|
|
2790
|
+
}
|
|
2791
|
+
function attachtimeline(input) {
|
|
2792
|
+
return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
|
|
2793
|
+
}
|
|
2794
|
+
function filterentries(entries, levelset) {
|
|
2795
|
+
return entries.filter((entry) => {
|
|
2796
|
+
const floor = levelset.floors?.[entry.stepid] ?? levelset.floors?.["*"];
|
|
2797
|
+
if (floor !== void 0 && levelrank(entry.level) > levelrank(floor)) return false;
|
|
2798
|
+
if (levelset.sources !== void 0 && levelset.sources.length > 0 && !levelset.sources.includes(entry.source)) return false;
|
|
2799
|
+
return true;
|
|
2800
|
+
});
|
|
2801
|
+
}
|
|
2802
|
+
function spamdetect(entries, rule) {
|
|
2803
|
+
const collapsed = [];
|
|
2804
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2805
|
+
for (const entry of entries) {
|
|
2806
|
+
if (rule.pattern !== "" && !entry.message.includes(rule.pattern)) {
|
|
2807
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2808
|
+
continue;
|
|
2809
|
+
}
|
|
2810
|
+
const key = `${entry.level}|${entry.source}|${entry.message}`;
|
|
2811
|
+
const previous = collapsed[collapsed.length - 1];
|
|
2812
|
+
if (previous && previous.repeat !== void 0 && `${previous.level}|${previous.source}|${previous.message}` === key && entry.time - previous.time <= rule.windowsize) {
|
|
2813
|
+
previous.repeat += 1;
|
|
2814
|
+
continue;
|
|
2815
|
+
}
|
|
2816
|
+
collapsed.push({ ...entry, repeat: 1 });
|
|
2817
|
+
}
|
|
2818
|
+
for (const entry of collapsed) {
|
|
2819
|
+
if (entry.repeat > 1) counts.set(`${entry.level}|${entry.source}|${entry.message}`, entry.repeat);
|
|
2820
|
+
}
|
|
2821
|
+
const flagged = [...counts.entries()].filter(([, count]) => count > rule.collapse).map(([key, count]) => ({ message: key.split("|").slice(2).join("|"), count }));
|
|
2822
|
+
return { entries: collapsed, flagged };
|
|
2823
|
+
}
|
|
2824
|
+
function rotatelogs(entries, rule) {
|
|
2825
|
+
if (entries.length <= rule.maxentries) return { kept: [...entries], overflow: [] };
|
|
2826
|
+
const kept = entries.slice(entries.length - rule.maxentries);
|
|
2827
|
+
const overflow = entries.slice(0, entries.length - rule.maxentries);
|
|
2828
|
+
return { kept, overflow };
|
|
2829
|
+
}
|
|
2830
|
+
function timelinecounts(entries) {
|
|
2831
|
+
const counts = {};
|
|
2832
|
+
for (const level of loglevels) counts[level] = 0;
|
|
2833
|
+
for (const entry of entries) counts[entry.level] = (counts[entry.level] ?? 0) + 1;
|
|
2834
|
+
return counts;
|
|
2835
|
+
}
|
|
2836
|
+
function blockingduration(tasks, stepid, window) {
|
|
2837
|
+
const inside = tasks.filter((task) => task.starttime >= window.startedat && task.starttime <= window.endedat);
|
|
2838
|
+
return { stepid, blocking: inside.reduce((total, task) => total + task.duration, 0), tasks: inside.length };
|
|
2839
|
+
}
|
|
2840
|
+
function netfailureentryof(input) {
|
|
2841
|
+
const exchange = input.exchange;
|
|
2842
|
+
if (exchange.errorclass === void 0 && exchange.status < 400) return null;
|
|
2843
|
+
return { id: input.id, runid: exchange.runid, stepid: exchange.stepid, url: exchange.url, status: exchange.status, errorclass: exchange.errorclass ?? "httperror", correlationid: exchange.correlationid, at: input.at };
|
|
2844
|
+
}
|
|
2845
|
+
function watcherdetached(input) {
|
|
2846
|
+
for (const navigation of input.navigations) {
|
|
2847
|
+
if (navigation >= input.startedat && navigation <= input.startedat + input.lifetime) return { detached: true, at: navigation };
|
|
2848
|
+
}
|
|
2849
|
+
return { detached: false };
|
|
2850
|
+
}
|
|
2851
|
+
function consolediff(input) {
|
|
2852
|
+
const base = input.baselines;
|
|
2853
|
+
const target = input.targetlines;
|
|
2854
|
+
const basemap = /* @__PURE__ */ new Map();
|
|
2855
|
+
for (const line of base) basemap.set(line, (basemap.get(line) ?? 0) + 1);
|
|
2856
|
+
const targetmap = /* @__PURE__ */ new Map();
|
|
2857
|
+
for (const line of target) targetmap.set(line, (targetmap.get(line) ?? 0) + 1);
|
|
2858
|
+
const lines = [];
|
|
2859
|
+
const added = [];
|
|
2860
|
+
const removed = [];
|
|
2861
|
+
const repeated = [];
|
|
2862
|
+
for (const [line, count] of targetmap) {
|
|
2863
|
+
const basecount = basemap.get(line) ?? 0;
|
|
2864
|
+
if (basecount === 0) {
|
|
2865
|
+
for (let index = 0; index < count; index += 1) {
|
|
2866
|
+
lines.push({ kind: "added", text: line });
|
|
2867
|
+
added.push(line);
|
|
2868
|
+
}
|
|
2869
|
+
continue;
|
|
2870
|
+
}
|
|
2871
|
+
const share = Math.min(basecount, count);
|
|
2872
|
+
for (let index = 0; index < share; index += 1) {
|
|
2873
|
+
lines.push({ kind: "repeated", text: line, count: share });
|
|
2874
|
+
repeated.push(line);
|
|
2875
|
+
}
|
|
2876
|
+
for (let index = share; index < count; index += 1) {
|
|
2877
|
+
lines.push({ kind: "added", text: line });
|
|
2878
|
+
added.push(line);
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
for (const [line, count] of basemap) {
|
|
2882
|
+
const targetcount = targetmap.get(line) ?? 0;
|
|
2883
|
+
const missing = Math.max(0, count - targetcount);
|
|
2884
|
+
for (let index = 0; index < missing; index += 1) {
|
|
2885
|
+
lines.push({ kind: "removed", text: line });
|
|
2886
|
+
removed.push(line);
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
return { base: input.baseid, target: input.targetid, lines, added: added.length, removed: removed.length, repeated: repeated.length, at: input.now };
|
|
2890
|
+
}
|
|
2891
|
+
|
|
2367
2892
|
// socketbus.ts
|
|
2368
2893
|
var socketkinds = ["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"];
|
|
2369
2894
|
function channelorigin(url) {
|
|
@@ -2585,9 +3110,9 @@ function polldecision(input) {
|
|
|
2585
3110
|
}
|
|
2586
3111
|
|
|
2587
3112
|
// policy.ts
|
|
2588
|
-
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
2589
|
-
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies"]);
|
|
2590
|
-
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies"]);
|
|
3113
|
+
var sensitiveactions = /* @__PURE__ */ new Set(["click", "type", "navigate", "select", "presskey", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "reload", "back", "forward", "writestorage", "setattribute", "removeattribute", "evaluate", "tabcreate", "tabactivate", "tabclose", "tabreload", "windowcreate", "windowclose", "windowresize", "downloadfile", "clickpoint", "shiftclick", "dismissdialog", "enterframe", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "openlink", "openprivate", "reloadcache", "stopnav", "followlink", "spanav", "rewritequery", "setfragment", "navlist", "navprofile", "handleauth", "printpdf", "prefetch", "preconnect", "deeplink", "reopentab", "pausenav", "navrate", "openclipboard", "batchopen", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "restorelayout", "reopenrun", "badgetab", "fillform", "filllabel", "fillplaceholder", "submitform", "retryform", "runwizard", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcard", "fillcode", "consentpassword", "exportcsv", "exportjson", "exportexcel", "copytable", "pushsheets", "streamdisk", "paginateextract", "resumeextract", "batchdownload", "pausedownload", "resumedownload", "interceptmime", "readclipboard", "writeclipboard", "copyscreen", "quarantinedownload", "scanvirus", "cleanupartifacts", "recordscreen", "captureaudio", "downloadimages", "callrest", "callgraphql", "sendmessage", "blockrequest", "mockresponse", "rewriteheaders", "setcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles", "attachcdp", "detachcdp", "cdpcmd", "overridescript"]);
|
|
3114
|
+
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction", "capturebodies", "setbreakpoint", "stepcode", "watchexpr"]);
|
|
3115
|
+
var readactions = /* @__PURE__ */ new Set(["observe", "inspect", "extract", "wait", "waitfor", "waittext", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "readlinks", "readimages", "readmeta", "readforms", "readstorage", "highlight", "tablist", "windowlist", "tabsnapshot", "mapclicks", "verifyvisible", "verifyenabled", "resolvexpath", "a11ytree", "readvisible", "readertree", "detectlists", "detecttables", "readjson", "watchmutate", "waitquiet", "watchbanner", "detectinfinitescroll", "detectvirtual", "detectlazy", "readscrollpos", "readlang", "readoutline", "countpages", "listshadow", "listframes", "classifypage", "fingerprintsection", "diffsnapshots", "readselection", "watchfocus", "detectsticky", "detectscrolllock", "readopengraph", "detectlanguage", "deriveselector", "waitload", "waiturl", "spawait", "detecthttp", "readredirects", "readfinalurl", "trailaudit", "navintent", "checksafe", "querytabs", "watchtab", "findclones", "searchtabs", "listaudio", "snapshotsession", "savelayout", "attachmeta", "detectfields", "generatevalues", "saveprofiles", "asksubmit", "readerrors", "skiphoneypot", "detectlogin", "detecttemplate", "handoffcaptcha", "scrapetable", "importcsv", "looprows", "transformvalues", "deduperows", "mergepages", "stamplerows", "previewgrid", "logprovenance", "verifydownload", "exportnetlog", "namecaptures", "shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet", "capturepdf", "captureframe", "readmedia", "readassets", "probestream", "timelapse", "shotcanvas", "convertimage", "makethumbs", "fetchurl", "parsejson", "parsehtml", "opensocket", "waitmessage", "watchrequests", "readheaders", "mapapi", "subscribesse", "longpoll", "extractapi", "readcookies", "watchconsole", "watcherrors", "watchtasks", "watchcdp"]);
|
|
2591
3116
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
2592
3117
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
2593
3118
|
var targetactions = /* @__PURE__ */ new Set(["inspect", "focus", "click", "type", "scroll", "select", "hover", "clickdeep", "rightclick", "doubleclick", "drag", "drop", "upload", "clear", "check", "uncheck", "toggle", "submit", "readattribute", "readstyle", "readgeometry", "readvalue", "readtext", "readhtml", "countelements", "readtable", "highlight", "setattribute", "removeattribute", "waitfor", "shiftclick", "typetime", "appendtext", "setvalue", "typeedit", "submitsearch", "selectmulti", "chooseradio", "setslider", "setdate", "setcolor", "expanddetails", "verifyvisible", "verifyenabled", "pierceshadow", "deriveselector", "fingerprintsection", "submitform", "retryform", "selectchain", "picktypeahead", "pickdate", "attachfile", "fillcode", "consentpassword", "scrapetable", "paginateextract", "shotelement", "captureframe", "shotcanvas"]);
|
|
@@ -2603,6 +3128,8 @@ var httpactions = /* @__PURE__ */ new Set(["fetchurl", "parsejson", "parsehtml",
|
|
|
2603
3128
|
var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitmessage", "subscribesse", "longpoll"]);
|
|
2604
3129
|
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
2605
3130
|
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
3131
|
+
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
3132
|
+
var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
|
|
2606
3133
|
var credentialheaders = /* @__PURE__ */ new Set(["authorization", "proxy-authorization", "cookie", "cookie2", "set-cookie", "api-key", "x-api-key", "x-auth-token", "x-session-token", "proxy-authorization"]);
|
|
2607
3134
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
2608
3135
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2621,8 +3148,14 @@ function hostpattern(origin) {
|
|
|
2621
3148
|
function iswatchkind(kind) {
|
|
2622
3149
|
return watchactions.has(kind);
|
|
2623
3150
|
}
|
|
3151
|
+
function isdebugkind(kind) {
|
|
3152
|
+
return debugactions.has(kind);
|
|
3153
|
+
}
|
|
3154
|
+
function iscdpkind(kind) {
|
|
3155
|
+
return cdpactions.has(kind);
|
|
3156
|
+
}
|
|
2624
3157
|
function observationmodeof(kind) {
|
|
2625
|
-
if (watchactions.has(kind) || kind === "waitquiet") return "watching";
|
|
3158
|
+
if (watchactions.has(kind) || debugactions.has(kind) || cdpactions.has(kind) && kind === "watchcdp" || kind === "waitquiet") return "watching";
|
|
2626
3159
|
if (kind === "diffsnapshots") return "diffing";
|
|
2627
3160
|
return "passive";
|
|
2628
3161
|
}
|
|
@@ -3755,6 +4288,211 @@ function ratelimitbudgetallowed(wait, budget) {
|
|
|
3755
4288
|
if (wait !== void 0 && budget !== void 0 && wait > budget) return { allowed: false, reason: `The rate limit wait of ${wait} milliseconds exceeds the reviewed budget of ${budget} milliseconds; review a wider budget or submit later.` };
|
|
3756
4289
|
return { allowed: true };
|
|
3757
4290
|
}
|
|
4291
|
+
function timelinegate(session, tabid, origin, now) {
|
|
4292
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the timeline capture." };
|
|
4293
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot capture the timeline." };
|
|
4294
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot capture the timeline." };
|
|
4295
|
+
if (session.tabid !== tabid) return { allowed: false, reason: `The timeline capture needs the run tab ${session.tabid} and refuses tab ${tabid}.` };
|
|
4296
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The timeline capture of ${origin} needs the session origin grants first.` };
|
|
4297
|
+
return { allowed: true };
|
|
4298
|
+
}
|
|
4299
|
+
function consoleconsentcovers(origin, consents) {
|
|
4300
|
+
if (consents.some((consent) => consent.origin === origin && consent.approved === true)) return { allowed: true };
|
|
4301
|
+
return { allowed: false, reason: `Console capture on ${origin} needs the reviewed console consent first; approve the prompt in the review panel.` };
|
|
4302
|
+
}
|
|
4303
|
+
function stackgate(session, origin) {
|
|
4304
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `Stack capture of ${origin} stays outside the session origin grants.` };
|
|
4305
|
+
return { allowed: true };
|
|
4306
|
+
}
|
|
4307
|
+
function debugwaitbudgetallowed(watchwindow, wait) {
|
|
4308
|
+
if (watchwindow !== void 0 && (typeof watchwindow !== "number" || !Number.isFinite(watchwindow) || watchwindow < 0)) return { allowed: false, reason: "The debug watch window must be zero or a positive number of milliseconds." };
|
|
4309
|
+
if (wait !== void 0 && (typeof wait !== "number" || !Number.isFinite(wait) || wait < 0)) return { allowed: false, reason: "The reviewed debug wait budget must be zero or a positive number of milliseconds." };
|
|
4310
|
+
if (watchwindow !== void 0 && wait !== void 0 && watchwindow > wait) return { allowed: false, reason: `The debug watch window of ${watchwindow} milliseconds exceeds the reviewed wait budget of ${wait} milliseconds; review a wider budget or a shorter window.` };
|
|
4311
|
+
return { allowed: true };
|
|
4312
|
+
}
|
|
4313
|
+
function timelineretentionwindow(settings) {
|
|
4314
|
+
return settings?.timelineretention;
|
|
4315
|
+
}
|
|
4316
|
+
function diffreviewgrade() {
|
|
4317
|
+
return { risk: "read", mode: "diffing", evidence: "comparison" };
|
|
4318
|
+
}
|
|
4319
|
+
function validatetimelinegrammar(step, options) {
|
|
4320
|
+
const kind = step.kind;
|
|
4321
|
+
let watchwindow;
|
|
4322
|
+
if (options.watch !== void 0) {
|
|
4323
|
+
const watch = options.watch;
|
|
4324
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed debug watch window must be an object." };
|
|
4325
|
+
const reviewed = watch;
|
|
4326
|
+
if (reviewed.window !== void 0) {
|
|
4327
|
+
if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed debug watch window must be zero or a positive number of milliseconds." };
|
|
4328
|
+
watchwindow = reviewed.window;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
4332
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
4333
|
+
if (options.level !== void 0 && !loglevels.includes(options.level)) return { allowed: false, reason: `The reviewed level floor must be one of ${loglevels.join(", ")}.` };
|
|
4334
|
+
if (options.sources !== void 0) {
|
|
4335
|
+
if (!Array.isArray(options.sources) || options.sources.length === 0 || !options.sources.every((source) => timelinesources.includes(source))) return { allowed: false, reason: `The reviewed source filters must be a non-empty list of the reviewed timeline sources: ${timelinesources.join(", ")}.` };
|
|
4336
|
+
}
|
|
4337
|
+
if (kind === "watchconsole") {
|
|
4338
|
+
if (options.redact === void 0 || !Array.isArray(options.redact) || options.redact.length === 0 || !options.redact.every((pattern) => isnonempty(pattern))) return { allowed: false, reason: "Console capture requires a reviewed non-empty redaction pattern list before any console text is captured." };
|
|
4339
|
+
if (options.depth !== void 0 && (typeof options.depth !== "number" || !Number.isInteger(options.depth) || options.depth < 1)) return { allowed: false, reason: "The reviewed serialization depth bound must be a positive integer with no code ceiling." };
|
|
4340
|
+
if (options.spam !== void 0) {
|
|
4341
|
+
const rule = spamruleof(options.spam);
|
|
4342
|
+
if (!rule) return { allowed: false, reason: "The reviewed spam rule needs a pattern, a window size and a collapse threshold." };
|
|
4343
|
+
if (rule.collapse < 1) return { allowed: false, reason: "The reviewed spam collapse threshold must be a positive integer of user configured value with no code ceiling." };
|
|
4344
|
+
}
|
|
4345
|
+
if (options.rotation !== void 0) {
|
|
4346
|
+
const rule = rotationruleof(options.rotation);
|
|
4347
|
+
if (!rule) return { allowed: false, reason: "The reviewed rotation rule needs a max entry count and an overflow target." };
|
|
4348
|
+
}
|
|
4349
|
+
}
|
|
4350
|
+
if (kind === "watchtasks") {
|
|
4351
|
+
if (options.threshold !== void 0 && (typeof options.threshold !== "number" || !Number.isFinite(options.threshold) || options.threshold < 0)) return { allowed: false, reason: "The reviewed long task threshold must be zero or a positive number of milliseconds with no code ceiling." };
|
|
4352
|
+
}
|
|
4353
|
+
return { allowed: true };
|
|
4354
|
+
}
|
|
4355
|
+
function spamruleof(value) {
|
|
4356
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
4357
|
+
const entry = value;
|
|
4358
|
+
const pattern = typeof entry.pattern === "string" ? entry.pattern : "";
|
|
4359
|
+
const windowsize = typeof entry.windowsize === "number" && Number.isFinite(entry.windowsize) && entry.windowsize >= 0 ? entry.windowsize : void 0;
|
|
4360
|
+
const collapse = typeof entry.collapse === "number" && Number.isInteger(entry.collapse) ? entry.collapse : void 0;
|
|
4361
|
+
if (windowsize === void 0 || collapse === void 0) return void 0;
|
|
4362
|
+
return { pattern, windowsize, collapse };
|
|
4363
|
+
}
|
|
4364
|
+
function rotationruleof(value) {
|
|
4365
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
4366
|
+
const entry = value;
|
|
4367
|
+
const maxentries = typeof entry.maxentries === "number" && Number.isInteger(entry.maxentries) && entry.maxentries >= 1 ? entry.maxentries : void 0;
|
|
4368
|
+
const overflowtarget = typeof entry.overflowtarget === "string" && entry.overflowtarget.trim() ? entry.overflowtarget.trim() : void 0;
|
|
4369
|
+
if (maxentries === void 0 || overflowtarget === void 0) return void 0;
|
|
4370
|
+
return { maxentries, overflowtarget };
|
|
4371
|
+
}
|
|
4372
|
+
function debuggate(session, tabid, origin, now) {
|
|
4373
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the devtools protocol step." };
|
|
4374
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot run a devtools protocol step." };
|
|
4375
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot run a devtools protocol step." };
|
|
4376
|
+
if (session.tabid !== tabid) return { allowed: false, reason: `The devtools protocol step needs the run tab ${session.tabid} and refuses tab ${tabid}.` };
|
|
4377
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The devtools protocol step on ${origin} needs the session origin grants first.` };
|
|
4378
|
+
return { allowed: true };
|
|
4379
|
+
}
|
|
4380
|
+
function debuggerconsentcovers(origin, domains, grants) {
|
|
4381
|
+
const needed = [...new Set(domains)];
|
|
4382
|
+
const covering = grants.find((grant) => grant.origin === origin && grant.approved === true && grant.revokedat === void 0 && needed.every((domain) => grant.domains.includes(domain)));
|
|
4383
|
+
if (covering) return { allowed: true };
|
|
4384
|
+
if (grants.some((grant) => grant.origin === origin && grant.revokedat !== void 0)) return { allowed: false, reason: `The debugger consent on ${origin} was revoked; approve a new prompt before the devtools protocol runs again.` };
|
|
4385
|
+
return { allowed: false, reason: `The devtools protocol on ${origin} needs the reviewed debugger consent for ${needed.join(", ")} first; approve the prompt with the domain allowlist shown in the review panel.` };
|
|
4386
|
+
}
|
|
4387
|
+
function validatebreakpointcondition(condition) {
|
|
4388
|
+
const expression = condition.trim();
|
|
4389
|
+
if (expression.length === 0) return { allowed: false, reason: "The breakpoint condition must not be empty." };
|
|
4390
|
+
if (/(?<![=!<>])=(?!=)/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse assignment because the reviewed grammar is comparison only." };
|
|
4391
|
+
if (/[A-Za-z_$][\w$]*\s*\(/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse calls because the reviewed grammar is comparison only." };
|
|
4392
|
+
const literal = /^(?:-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|true|false|null)$/;
|
|
4393
|
+
const tokens = expression.match(/(?:[A-Za-z_$][\w$]*|-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|===|!==|==|!=|>=|<=|&&|\|\||[!.<>()+\-*\/%])/g);
|
|
4394
|
+
if (tokens === null || tokens.join("") !== expression.replace(/\s+/g, "")) return { allowed: false, reason: "The breakpoint condition must use the reviewed expression grammar of member chains, literals, comparisons, logic operators, negation and parentheses." };
|
|
4395
|
+
const identifierlike = /^(?:true|false|null)$/;
|
|
4396
|
+
for (const token of tokens) {
|
|
4397
|
+
if (literal.test(token) || identifierlike.test(token)) continue;
|
|
4398
|
+
if (["===", "!==", "==", "!=", ">=", "<=", "&&", "||", "!", ".", "(", ")", "<", ">", "+", "-", "*", "/", "%"].includes(token)) continue;
|
|
4399
|
+
if (/^[A-Za-z_$][\w$]*$/.test(token)) continue;
|
|
4400
|
+
return { allowed: false, reason: `The token ${token} of the breakpoint condition stays outside the reviewed expression grammar.` };
|
|
4401
|
+
}
|
|
4402
|
+
return { allowed: true };
|
|
4403
|
+
}
|
|
4404
|
+
function breakpointbudgetallowed(active, ceiling) {
|
|
4405
|
+
if (ceiling === void 0) return { allowed: true };
|
|
4406
|
+
if (typeof ceiling !== "number" || !Number.isInteger(ceiling) || ceiling < 0) return { allowed: false, reason: "The reviewed breakpoint ceiling must be zero or a positive integer of user configured value with no code ceiling." };
|
|
4407
|
+
if (active >= ceiling) return { allowed: false, reason: `The run already holds ${active} active breakpoint${active === 1 ? "" : "s"} and the reviewed breakpoint ceiling is ${ceiling}; revert one or review a wider ceiling.` };
|
|
4408
|
+
return { allowed: true };
|
|
4409
|
+
}
|
|
4410
|
+
function pauseretentionwindow(settings) {
|
|
4411
|
+
return settings?.pauseretention;
|
|
4412
|
+
}
|
|
4413
|
+
function breakpointceilingof(settings) {
|
|
4414
|
+
return settings?.breakpointceiling;
|
|
4415
|
+
}
|
|
4416
|
+
function validatecdpgrammar(step, options) {
|
|
4417
|
+
const kind = step.kind;
|
|
4418
|
+
if (kind === "attachcdp") {
|
|
4419
|
+
if (!Array.isArray(options.domains) || options.domains.length === 0 || !options.domains.every((domain) => typeof domain === "string" && cdpdomains.includes(domain))) return { allowed: false, reason: `The attach needs a non-empty enabled domain list of the reviewed domain grammar: ${cdpdomains.join(", ")}.` };
|
|
4420
|
+
if (teardownplanof(options.teardown) === void 0) return { allowed: false, reason: "Every attach needs a reviewed teardown plan with its revert steps and resume policy before approval." };
|
|
4421
|
+
if (options.allowlist !== void 0) {
|
|
4422
|
+
const allowlist = cdpallowlistof(options.allowlist);
|
|
4423
|
+
if (!allowlist || !allowlist.domains.every((domain) => options.domains.includes(domain))) return { allowed: false, reason: "The reviewed method allowlist must stay inside the enabled domains of the attach." };
|
|
4424
|
+
}
|
|
4425
|
+
const budgetcheck = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
|
|
4426
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
4427
|
+
return { allowed: true };
|
|
4428
|
+
}
|
|
4429
|
+
if (kind === "detachcdp") return { allowed: true };
|
|
4430
|
+
if (kind === "cdpcmd") {
|
|
4431
|
+
const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : void 0;
|
|
4432
|
+
if (!command || typeof command.method !== "string" || methoddomain(command.method) === void 0) return { allowed: false, reason: "The raw command needs a reviewed method of the Domain.method form." };
|
|
4433
|
+
if (command.params !== void 0 && (typeof command.params !== "object" || Array.isArray(command.params))) return { allowed: false, reason: "The raw command params must be a JSON object." };
|
|
4434
|
+
if (command.resultpath !== void 0 && typeof command.resultpath !== "string") return { allowed: false, reason: "The reviewed result path must be a dotted path string." };
|
|
4435
|
+
return { allowed: true };
|
|
4436
|
+
}
|
|
4437
|
+
if (kind === "watchcdp") {
|
|
4438
|
+
if (!Array.isArray(options.events) || options.events.length === 0 || !options.events.every((rule) => cdpeventruleof(rule) !== void 0)) return { allowed: false, reason: "The event watch needs a non-empty reviewed list of domain event rules of the reviewed domain grammar." };
|
|
4439
|
+
let watchwindow;
|
|
4440
|
+
if (options.watch !== void 0) {
|
|
4441
|
+
const watch = options.watch;
|
|
4442
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed event watch window must be an object." };
|
|
4443
|
+
const reviewed = watch;
|
|
4444
|
+
if (reviewed.window !== void 0) {
|
|
4445
|
+
if (typeof reviewed.window !== "number" || !Number.isFinite(reviewed.window) || reviewed.window < 0) return { allowed: false, reason: "The reviewed event watch window must be zero or a positive number of milliseconds." };
|
|
4446
|
+
watchwindow = reviewed.window;
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
if (watchwindow === void 0) return { allowed: false, reason: "The event watch needs a reviewed lifetime window before any domain event is observed." };
|
|
4450
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
4451
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
4452
|
+
return { allowed: true };
|
|
4453
|
+
}
|
|
4454
|
+
if (kind === "setbreakpoint") {
|
|
4455
|
+
const breakpoint = breakpointinputof(options.breakpoint);
|
|
4456
|
+
if (!breakpoint) return { allowed: false, reason: "The breakpoint needs a reviewed script url and a zero based line." };
|
|
4457
|
+
if (!ishttpsurl(breakpoint.url)) return { allowed: false, reason: "The breakpoint script url must be a reviewed HTTPS url." };
|
|
4458
|
+
if (breakpoint.condition !== void 0) {
|
|
4459
|
+
const conditioncheck = validatebreakpointcondition(breakpoint.condition);
|
|
4460
|
+
if (!conditioncheck.allowed) return conditioncheck;
|
|
4461
|
+
}
|
|
4462
|
+
return { allowed: true };
|
|
4463
|
+
}
|
|
4464
|
+
if (kind === "stepcode") {
|
|
4465
|
+
if (stepmodeof(options.mode) === void 0) return { allowed: false, reason: "The step code mode must be one of stepover, stepinto, stepout or resume." };
|
|
4466
|
+
return { allowed: true };
|
|
4467
|
+
}
|
|
4468
|
+
if (kind === "watchexpr") {
|
|
4469
|
+
if (watchexpressionof(options.expression) === void 0) return { allowed: false, reason: "The watch expression needs the reviewed expression text." };
|
|
4470
|
+
if (options.reviewed !== true) return { allowed: false, reason: "Watch expressions must be reviewed before evaluation; set the explicit reviewed flag on the step." };
|
|
4471
|
+
return { allowed: true };
|
|
4472
|
+
}
|
|
4473
|
+
if (kind === "overridescript") {
|
|
4474
|
+
const override = overrideinputof(options.override);
|
|
4475
|
+
if (!override) return { allowed: false, reason: "The script override needs a reviewed url pattern and its full fixture source." };
|
|
4476
|
+
if (patternorigin(override.urlpattern) === void 0) return { allowed: false, reason: "Script overrides without a named https origin pattern are refused." };
|
|
4477
|
+
if (options.reviewed !== true) return { allowed: false, reason: "The full fixture source must be reviewed before the script override runs; set the explicit reviewed flag on the step." };
|
|
4478
|
+
return { allowed: true };
|
|
4479
|
+
}
|
|
4480
|
+
return { allowed: true };
|
|
4481
|
+
}
|
|
4482
|
+
function planallowlist(steps) {
|
|
4483
|
+
const attach = steps.find((step) => step.kind === "attachcdp");
|
|
4484
|
+
if (!attach) return void 0;
|
|
4485
|
+
let options = {};
|
|
4486
|
+
try {
|
|
4487
|
+
options = parseoptions(attach);
|
|
4488
|
+
} catch {
|
|
4489
|
+
options = {};
|
|
4490
|
+
}
|
|
4491
|
+
const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
4492
|
+
if (domains.length === 0) return void 0;
|
|
4493
|
+
const gated = cdpallowlistof(options.allowlist);
|
|
4494
|
+
return { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} };
|
|
4495
|
+
}
|
|
3758
4496
|
function controltarget(step) {
|
|
3759
4497
|
let options = {};
|
|
3760
4498
|
try {
|
|
@@ -4160,6 +4898,14 @@ function validatestep(step, origin) {
|
|
|
4160
4898
|
const controlcheck = validatecontrolgrammar(step, options);
|
|
4161
4899
|
if (!controlcheck.allowed) return controlcheck;
|
|
4162
4900
|
}
|
|
4901
|
+
if (isdebugkind(step.kind)) {
|
|
4902
|
+
const timelinecheck = validatetimelinegrammar(step, options);
|
|
4903
|
+
if (!timelinecheck.allowed) return timelinecheck;
|
|
4904
|
+
}
|
|
4905
|
+
if (iscdpkind(step.kind)) {
|
|
4906
|
+
const cdpcheck = validatecdpgrammar(step, options);
|
|
4907
|
+
if (!cdpcheck.allowed) return cdpcheck;
|
|
4908
|
+
}
|
|
4163
4909
|
if (step.kind === "tabcreate") {
|
|
4164
4910
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
4165
4911
|
if (options.window !== void 0 && (typeof options.window !== "number" || !Number.isInteger(options.window) || options.window < 0)) return { allowed: false, reason: "The reviewed target window id must be a non-negative integer." };
|
|
@@ -4261,6 +5007,50 @@ function canexecute(input) {
|
|
|
4261
5007
|
const watchgatecheck = watchgate(input.session, input.settings, now);
|
|
4262
5008
|
if (!watchgatecheck.allowed) return watchgatecheck;
|
|
4263
5009
|
}
|
|
5010
|
+
if (isdebugkind(input.step.kind)) {
|
|
5011
|
+
const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
|
|
5012
|
+
if (!timelinegatecheck.allowed) return timelinegatecheck;
|
|
5013
|
+
}
|
|
5014
|
+
if (iscdpkind(input.step.kind)) {
|
|
5015
|
+
const debuggatecheck = debuggate(input.session, input.tabid, input.origin, now);
|
|
5016
|
+
if (!debuggatecheck.allowed) return debuggatecheck;
|
|
5017
|
+
if (!input.plan) return { allowed: false, reason: "The devtools protocol steps need an approved plan." };
|
|
5018
|
+
const allowlist = planallowlist(input.plan.steps);
|
|
5019
|
+
if (input.step.kind !== "attachcdp") {
|
|
5020
|
+
if (allowlist === void 0) return { allowed: false, reason: "The devtools protocol step needs the attachcdp step of the same plan with its enabled domains first." };
|
|
5021
|
+
if (input.step.kind === "cdpcmd") {
|
|
5022
|
+
let cdpoptions2 = {};
|
|
5023
|
+
try {
|
|
5024
|
+
cdpoptions2 = parseoptions(input.step);
|
|
5025
|
+
} catch {
|
|
5026
|
+
cdpoptions2 = {};
|
|
5027
|
+
}
|
|
5028
|
+
const command = cdpoptions2.command && typeof cdpoptions2.command === "object" && !Array.isArray(cdpoptions2.command) ? cdpoptions2.command : void 0;
|
|
5029
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
5030
|
+
if (methoddomain(method) === void 0 || !allowlistcovers(allowlist, method)) return { allowed: false, reason: `The raw command ${method || ""} stays outside the enabled domain allowlist of the plan attach; review the attach domains or the method gates.` };
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
let cdpoptions = {};
|
|
5034
|
+
try {
|
|
5035
|
+
cdpoptions = parseoptions(input.step);
|
|
5036
|
+
} catch {
|
|
5037
|
+
cdpoptions = {};
|
|
5038
|
+
}
|
|
5039
|
+
if (input.step.kind === "setbreakpoint") {
|
|
5040
|
+
const breakpoint = breakpointinputof(cdpoptions.breakpoint);
|
|
5041
|
+
if (breakpoint) {
|
|
5042
|
+
const targetgate = origincheck(input.session, breakpoint.url);
|
|
5043
|
+
if (!targetgate.allowed) return targetgate;
|
|
5044
|
+
}
|
|
5045
|
+
}
|
|
5046
|
+
if (input.step.kind === "overridescript") {
|
|
5047
|
+
const override = overrideinputof(cdpoptions.override);
|
|
5048
|
+
if (override) {
|
|
5049
|
+
const targetgate = origincheck(input.session, override.urlpattern);
|
|
5050
|
+
if (!targetgate.allowed) return targetgate;
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
4264
5054
|
if (iscontrolkind(input.step.kind)) {
|
|
4265
5055
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
4266
5056
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -4344,7 +5134,7 @@ function canexecute(input) {
|
|
|
4344
5134
|
}
|
|
4345
5135
|
|
|
4346
5136
|
// version.ts
|
|
4347
|
-
var packageversion = "1.1.
|
|
5137
|
+
var packageversion = "1.1.46";
|
|
4348
5138
|
|
|
4349
5139
|
// types.ts
|
|
4350
5140
|
var protocolversion = packageversion;
|
|
@@ -4369,6 +5159,20 @@ function parseproposal(value, origin, grants) {
|
|
|
4369
5159
|
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
4370
5160
|
if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
4371
5161
|
const planwindow = expiresat - createdat;
|
|
5162
|
+
const attachinput = stepsinput.map((input) => record(input)).find((candidate) => candidate.kind === "attachcdp");
|
|
5163
|
+
let planallowlist2;
|
|
5164
|
+
if (attachinput !== void 0) {
|
|
5165
|
+
const attachoptions = (() => {
|
|
5166
|
+
try {
|
|
5167
|
+
return parseoptions({ id: "attach", kind: "attachcdp", summary: "attach", risk: "sensitive", ...typeof attachinput.options === "string" ? { options: attachinput.options } : {} });
|
|
5168
|
+
} catch {
|
|
5169
|
+
return {};
|
|
5170
|
+
}
|
|
5171
|
+
})();
|
|
5172
|
+
const domains = Array.isArray(attachoptions.domains) ? attachoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
5173
|
+
const gated = cdpallowlistof(attachoptions.allowlist);
|
|
5174
|
+
planallowlist2 = domains.length > 0 ? { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} } : void 0;
|
|
5175
|
+
}
|
|
4372
5176
|
const steps = stepsinput.map((input, index) => {
|
|
4373
5177
|
const candidate = record(input);
|
|
4374
5178
|
const kind = text(candidate.kind, `step ${index + 1} kind`);
|
|
@@ -4402,6 +5206,53 @@ function parseproposal(value, origin, grants) {
|
|
|
4402
5206
|
const bypass = proxy && typeof proxy === "object" && !Array.isArray(proxy) ? proxy.bypass : void 0;
|
|
4403
5207
|
if (!Array.isArray(bypass) || bypass.length === 0) throw new Error("Proxy routes without a bypass list are refused.");
|
|
4404
5208
|
}
|
|
5209
|
+
if (step.kind === "watchconsole" || step.kind === "watcherrors" || step.kind === "watchtasks") {
|
|
5210
|
+
let debugoptions = {};
|
|
5211
|
+
try {
|
|
5212
|
+
debugoptions = parseoptions(step);
|
|
5213
|
+
} catch {
|
|
5214
|
+
debugoptions = {};
|
|
5215
|
+
}
|
|
5216
|
+
const granted = covered.some((pattern) => {
|
|
5217
|
+
try {
|
|
5218
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
5219
|
+
} catch {
|
|
5220
|
+
return false;
|
|
5221
|
+
}
|
|
5222
|
+
});
|
|
5223
|
+
if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
|
|
5224
|
+
if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
|
|
5225
|
+
}
|
|
5226
|
+
if (iscdpkind(step.kind)) {
|
|
5227
|
+
const granted = covered.some((pattern) => {
|
|
5228
|
+
try {
|
|
5229
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
5230
|
+
} catch {
|
|
5231
|
+
return false;
|
|
5232
|
+
}
|
|
5233
|
+
});
|
|
5234
|
+
if (!granted) throw new Error(`The ${step.kind} step of ${origin} targets an origin outside the grants.`);
|
|
5235
|
+
let cdpoptions = {};
|
|
5236
|
+
try {
|
|
5237
|
+
cdpoptions = parseoptions(step);
|
|
5238
|
+
} catch {
|
|
5239
|
+
cdpoptions = {};
|
|
5240
|
+
}
|
|
5241
|
+
if (step.kind === "attachcdp") {
|
|
5242
|
+
if (teardownplanof(cdpoptions.teardown) === void 0) throw new Error("Attach steps without a reviewed teardown plan are refused.");
|
|
5243
|
+
const domains = Array.isArray(cdpoptions.domains) ? cdpoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
5244
|
+
if (domains.length === 0) throw new Error("Attach steps need a non-empty enabled domain list of the reviewed domain grammar.");
|
|
5245
|
+
const gated = cdpallowlistof(cdpoptions.allowlist);
|
|
5246
|
+
if (cdpoptions.allowlist !== void 0 && (!gated || !gated.domains.every((domain) => domains.includes(domain)))) throw new Error("The reviewed method allowlist must stay inside the enabled domains of the attach.");
|
|
5247
|
+
}
|
|
5248
|
+
if (step.kind === "cdpcmd") {
|
|
5249
|
+
const command = cdpoptions.command && typeof cdpoptions.command === "object" && !Array.isArray(cdpoptions.command) ? cdpoptions.command : void 0;
|
|
5250
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
5251
|
+
if (methoddomain(method) === void 0) throw new Error("Raw commands need a reviewed method of the Domain.method form.");
|
|
5252
|
+
if (planallowlist2 === void 0) throw new Error("Raw command steps need the attachcdp step of the same plan with its enabled domains first.");
|
|
5253
|
+
if (!allowlistcovers(planallowlist2, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the plan attach.`);
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
4405
5256
|
const evaluation = validatestep(step, origin);
|
|
4406
5257
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
4407
5258
|
const target = outboundtarget(step);
|
|
@@ -4476,7 +5327,7 @@ function requestbody(input) {
|
|
|
4476
5327
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
4477
5328
|
}
|
|
4478
5329
|
function outcomeresponse(input) {
|
|
4479
|
-
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {} });
|
|
5330
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, outcome: input.outcome, ...input.resolvedtarget ? { resolvedtarget: input.resolvedtarget } : {}, ...input.capture ? { capture: input.capture } : {}, ...input.media ? { media: input.media } : {}, ...input.transport ? { transport: input.transport } : {}, ...input.network ? { network: input.network } : {}, ...input.control ? { control: input.control } : {}, ...input.timeline ? { timeline: input.timeline } : {}, ...input.cdp ? { cdp: input.cdp } : {} });
|
|
4480
5331
|
}
|
|
4481
5332
|
function mapresponse(input) {
|
|
4482
5333
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -4587,21 +5438,48 @@ function controlreport(input) {
|
|
|
4587
5438
|
});
|
|
4588
5439
|
return { version: protocolversion, blocks: input.blocks, mocks, rewrites: input.rewrites, cookies: input.cookies, proxies: input.proxies, ratelimits: input.ratelimits };
|
|
4589
5440
|
}
|
|
5441
|
+
function timelinereport(input) {
|
|
5442
|
+
return { version: protocolversion, entries: input.entries, errors: input.errors, rejections: input.rejections, longtasks: input.longtasks, levelcounts: input.levelcounts };
|
|
5443
|
+
}
|
|
5444
|
+
function consolediffreport(input) {
|
|
5445
|
+
return { version: protocolversion, diff: input.diff };
|
|
5446
|
+
}
|
|
5447
|
+
function cdpreport(input) {
|
|
5448
|
+
const overrides = input.overrides.map((spec) => {
|
|
5449
|
+
const { source, ...metadata } = spec;
|
|
5450
|
+
void source;
|
|
5451
|
+
return metadata;
|
|
5452
|
+
});
|
|
5453
|
+
const grants = input.grants.map((grant) => {
|
|
5454
|
+
const { prompt, ...metadata } = grant;
|
|
5455
|
+
void prompt;
|
|
5456
|
+
return metadata;
|
|
5457
|
+
});
|
|
5458
|
+
return { version: protocolversion, sessions: input.sessions, commands: input.commands, events: input.events, breakpoints: input.breakpoints, pauses: input.pauses, watches: input.watches, overrides, grants };
|
|
5459
|
+
}
|
|
4590
5460
|
export {
|
|
5461
|
+
allowlistcovers,
|
|
4591
5462
|
annotationplanof,
|
|
4592
5463
|
apientries,
|
|
4593
5464
|
apikeyconsentgranted,
|
|
4594
5465
|
apireplayspecof,
|
|
4595
5466
|
applyheaderules,
|
|
5467
|
+
argkind,
|
|
4596
5468
|
assetentries,
|
|
5469
|
+
attachcdpsession,
|
|
5470
|
+
attachtimeline,
|
|
4597
5471
|
authconsentgranted,
|
|
4598
5472
|
authorizeurl,
|
|
4599
5473
|
authreport,
|
|
4600
5474
|
blendrows,
|
|
4601
5475
|
blockgate,
|
|
5476
|
+
blockingduration,
|
|
4602
5477
|
blockruleof,
|
|
4603
5478
|
bodyfilterof,
|
|
4604
5479
|
bodymatches,
|
|
5480
|
+
breakpointbudgetallowed,
|
|
5481
|
+
breakpointceilingof,
|
|
5482
|
+
breakpointinputof,
|
|
4605
5483
|
buildname,
|
|
4606
5484
|
buildpdf,
|
|
4607
5485
|
buildsheet,
|
|
@@ -4617,16 +5495,26 @@ export {
|
|
|
4617
5495
|
captureformats,
|
|
4618
5496
|
capturekinds,
|
|
4619
5497
|
captureoptionsof,
|
|
5498
|
+
capturepause,
|
|
4620
5499
|
captureregion,
|
|
4621
5500
|
capturereport,
|
|
4622
5501
|
capturestates,
|
|
4623
5502
|
capturestitched,
|
|
4624
5503
|
capturetargets,
|
|
4625
5504
|
capturevisible,
|
|
5505
|
+
cdpallowlistof,
|
|
5506
|
+
cdpdomains,
|
|
5507
|
+
cdpeventruleof,
|
|
5508
|
+
cdpkinds,
|
|
5509
|
+
cdpreport,
|
|
4626
5510
|
channeloptionsof,
|
|
4627
5511
|
channelorigin,
|
|
4628
5512
|
closechannel,
|
|
4629
5513
|
collectmessages,
|
|
5514
|
+
consolecapture,
|
|
5515
|
+
consoleconsentcovers,
|
|
5516
|
+
consolediff,
|
|
5517
|
+
consolediffreport,
|
|
4630
5518
|
controlkinds,
|
|
4631
5519
|
controlreport,
|
|
4632
5520
|
controltarget,
|
|
@@ -4639,10 +5527,16 @@ export {
|
|
|
4639
5527
|
crossesviewport,
|
|
4640
5528
|
cursorfrom,
|
|
4641
5529
|
datasetresponse,
|
|
5530
|
+
debuggate,
|
|
5531
|
+
debuggerconsentcovers,
|
|
5532
|
+
debugwaitbudgetallowed,
|
|
4642
5533
|
dedupeimages,
|
|
4643
5534
|
actionrisk as deriveactionrisk,
|
|
5535
|
+
detachcdpsession,
|
|
4644
5536
|
diffresponse,
|
|
5537
|
+
diffreviewgrade,
|
|
4645
5538
|
downloadreport,
|
|
5539
|
+
errorcapture,
|
|
4646
5540
|
errorreportresponse,
|
|
4647
5541
|
eventresponse,
|
|
4648
5542
|
exchangesreport,
|
|
@@ -4651,6 +5545,7 @@ export {
|
|
|
4651
5545
|
failureclass,
|
|
4652
5546
|
fetchoptionsof,
|
|
4653
5547
|
fetchrequestof,
|
|
5548
|
+
filterentries,
|
|
4654
5549
|
filterexchanges,
|
|
4655
5550
|
finishrecording,
|
|
4656
5551
|
fixedheadermatch,
|
|
@@ -4669,7 +5564,9 @@ export {
|
|
|
4669
5564
|
imagefilterof,
|
|
4670
5565
|
imagematches,
|
|
4671
5566
|
imagenames,
|
|
5567
|
+
iscdpkind,
|
|
4672
5568
|
iscontrolkind,
|
|
5569
|
+
isdebugkind,
|
|
4673
5570
|
isformkind,
|
|
4674
5571
|
isnetwatchkind,
|
|
4675
5572
|
issocketkind,
|
|
@@ -4678,6 +5575,9 @@ export {
|
|
|
4678
5575
|
lapseframes,
|
|
4679
5576
|
lapseplanof,
|
|
4680
5577
|
layoutreport,
|
|
5578
|
+
levelrank,
|
|
5579
|
+
loglevels,
|
|
5580
|
+
longtaskcapture,
|
|
4681
5581
|
mapresponse,
|
|
4682
5582
|
matchmessage,
|
|
4683
5583
|
matchurlpattern,
|
|
@@ -4685,11 +5585,13 @@ export {
|
|
|
4685
5585
|
mediakinds,
|
|
4686
5586
|
mediareport,
|
|
4687
5587
|
messagefilterof,
|
|
5588
|
+
methoddomain,
|
|
4688
5589
|
mockfor,
|
|
4689
5590
|
mockspecof,
|
|
4690
5591
|
multipartchunks,
|
|
4691
5592
|
multipartpayloadof,
|
|
4692
5593
|
navstateresponse,
|
|
5594
|
+
netfailureentryof,
|
|
4693
5595
|
netlogreport,
|
|
4694
5596
|
netwatchkinds,
|
|
4695
5597
|
newblockrule,
|
|
@@ -4704,6 +5606,8 @@ export {
|
|
|
4704
5606
|
observationresponse,
|
|
4705
5607
|
openchannel,
|
|
4706
5608
|
outcomeresponse,
|
|
5609
|
+
overrideinputof,
|
|
5610
|
+
overridematches,
|
|
4707
5611
|
pairexchange,
|
|
4708
5612
|
pairstates,
|
|
4709
5613
|
parsehtmlbody,
|
|
@@ -4712,6 +5616,7 @@ export {
|
|
|
4712
5616
|
parsetokens,
|
|
4713
5617
|
passwordconsentgranted,
|
|
4714
5618
|
patternorigin,
|
|
5619
|
+
pauseretentionwindow,
|
|
4715
5620
|
payloadshapeof,
|
|
4716
5621
|
payloadvalid,
|
|
4717
5622
|
payloadwithdefaults,
|
|
@@ -4719,6 +5624,7 @@ export {
|
|
|
4719
5624
|
pdfpagesize,
|
|
4720
5625
|
pdfsegments,
|
|
4721
5626
|
pdftextlayout,
|
|
5627
|
+
planallowlist,
|
|
4722
5628
|
pollcursorof,
|
|
4723
5629
|
polldecision,
|
|
4724
5630
|
pollurl,
|
|
@@ -4740,8 +5646,11 @@ export {
|
|
|
4740
5646
|
receivemessage,
|
|
4741
5647
|
reconnectwaits,
|
|
4742
5648
|
recordingoptionsof,
|
|
5649
|
+
recordwatchvalue,
|
|
5650
|
+
redactconsoletext,
|
|
4743
5651
|
redactedcookies,
|
|
4744
5652
|
regionsteps,
|
|
5653
|
+
rejectioncapture,
|
|
4745
5654
|
replayurl,
|
|
4746
5655
|
requestbody,
|
|
4747
5656
|
resolutionverdict,
|
|
@@ -4750,36 +5659,58 @@ export {
|
|
|
4750
5659
|
retryafterof,
|
|
4751
5660
|
revertrule,
|
|
4752
5661
|
revocationruleof,
|
|
5662
|
+
rotatelogs,
|
|
5663
|
+
rotationruleof,
|
|
4753
5664
|
safetyresponse,
|
|
4754
5665
|
scaledrect,
|
|
4755
5666
|
seamweights,
|
|
4756
5667
|
selectorresponse,
|
|
5668
|
+
sendcdpcommand,
|
|
4757
5669
|
sendfetch,
|
|
4758
5670
|
sequenceintegrity,
|
|
5671
|
+
serializearg,
|
|
5672
|
+
serializecdpcommand,
|
|
4759
5673
|
sessionmemory,
|
|
4760
5674
|
signalsreport,
|
|
4761
5675
|
socketgate,
|
|
4762
5676
|
socketkinds,
|
|
5677
|
+
spamdetect,
|
|
5678
|
+
spamruleof,
|
|
4763
5679
|
sserequestheaders,
|
|
5680
|
+
stackframes,
|
|
5681
|
+
stackgate,
|
|
4764
5682
|
statusclassof,
|
|
5683
|
+
stepmodeof,
|
|
4765
5684
|
streamsummaries,
|
|
4766
5685
|
streamwindowof,
|
|
4767
5686
|
submitreviewgranted,
|
|
4768
5687
|
subscriptionoptionsof,
|
|
4769
5688
|
tabreportresponse,
|
|
5689
|
+
teardowncdpsession,
|
|
5690
|
+
teardownplanof,
|
|
4770
5691
|
templateurl,
|
|
4771
5692
|
thumbdirectiveof,
|
|
4772
5693
|
thumbgeometry,
|
|
5694
|
+
timelinecounts,
|
|
5695
|
+
timelinegate,
|
|
5696
|
+
timelinekinds,
|
|
5697
|
+
timelinereport,
|
|
5698
|
+
timelineretentionwindow,
|
|
5699
|
+
timelinesources,
|
|
4773
5700
|
tokenrequest,
|
|
4774
5701
|
trailreport,
|
|
4775
5702
|
transformgrammar,
|
|
4776
5703
|
unwrapgraphql,
|
|
4777
5704
|
urlencodeform,
|
|
5705
|
+
validatebreakpointcondition,
|
|
4778
5706
|
validatefieldmatch,
|
|
4779
5707
|
validateformrecord,
|
|
4780
5708
|
validatestep,
|
|
4781
5709
|
validatetargetref,
|
|
4782
5710
|
validatevaluegen,
|
|
5711
|
+
watchcdpevents,
|
|
5712
|
+
watcherdetached,
|
|
5713
|
+
watchexpressionof,
|
|
4783
5714
|
watchgate,
|
|
4784
5715
|
wizardreport
|
|
4785
5716
|
};
|