@wenathlan/extension 1.1.45 → 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 +5 -4
- package/dist/cdpbus.d.ts +104 -0
- package/dist/cdpbus.d.ts.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +504 -7
- package/dist/index.js.map +4 -4
- package/dist/memory.d.ts +37 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +17 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +34 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/runtimeline.d.ts +1 -1
- package/dist/runtimeline.d.ts.map +1 -1
- package/dist/types.d.ts +142 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +771 -9
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +235 -3
- package/extension/dist/pagebridge.js.map +3 -3
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +11 -7
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +130 -3
- package/extension/dist/sidepanel.js.map +2 -2
- package/extension/dist/style.css +2 -0
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
|
@@ -1126,6 +1126,108 @@ var sessionmemory = class {
|
|
|
1126
1126
|
async getconsoleconsents() {
|
|
1127
1127
|
return await this.adapter.get("consoleconsents") ?? [];
|
|
1128
1128
|
}
|
|
1129
|
+
/** Stores one devtools session record with its enabled domains and detach state, replacing the previous record of its id. */
|
|
1130
|
+
async setcdpsession(session) {
|
|
1131
|
+
const records = (await this.adapter.get("cdpsessions") ?? []).filter((item) => item.id !== session.id);
|
|
1132
|
+
await this.adapter.set("cdpsessions", [session, ...records]);
|
|
1133
|
+
}
|
|
1134
|
+
/** Returns every stored devtools session record, newest first. */
|
|
1135
|
+
async getcdpsessions() {
|
|
1136
|
+
return await this.adapter.get("cdpsessions") ?? [];
|
|
1137
|
+
}
|
|
1138
|
+
/** Stores one raw command outcome with its duration and error class. */
|
|
1139
|
+
async addcdpcommand(command) {
|
|
1140
|
+
const records = await this.adapter.get("cdpcommands") ?? [];
|
|
1141
|
+
await this.adapter.set("cdpcommands", [command, ...records]);
|
|
1142
|
+
}
|
|
1143
|
+
/** Returns every stored raw command outcome, newest first. */
|
|
1144
|
+
async getcdpcommands() {
|
|
1145
|
+
return await this.adapter.get("cdpcommands") ?? [];
|
|
1146
|
+
}
|
|
1147
|
+
/** Stores one domain event rule with its match filter, replacing the previous rule of its id. */
|
|
1148
|
+
async setcdpeventrule(rule) {
|
|
1149
|
+
const records = (await this.adapter.get("cdpeventrules") ?? []).filter((item) => item.id !== rule.id);
|
|
1150
|
+
await this.adapter.set("cdpeventrules", [rule, ...records]);
|
|
1151
|
+
}
|
|
1152
|
+
/** Returns every stored domain event rule, newest first. */
|
|
1153
|
+
async getcdpeventrules() {
|
|
1154
|
+
return await this.adapter.get("cdpeventrules") ?? [];
|
|
1155
|
+
}
|
|
1156
|
+
/** Stores one breakpoint record with its condition and hit counter, replacing the previous record of its id. */
|
|
1157
|
+
async addbreakpoint(spec) {
|
|
1158
|
+
const records = (await this.adapter.get("breakpoints") ?? []).filter((item) => item.id !== spec.id);
|
|
1159
|
+
await this.adapter.set("breakpoints", [spec, ...records]);
|
|
1160
|
+
}
|
|
1161
|
+
/** Returns every stored breakpoint record, newest first. */
|
|
1162
|
+
async getbreakpoints() {
|
|
1163
|
+
return await this.adapter.get("breakpoints") ?? [];
|
|
1164
|
+
}
|
|
1165
|
+
/** 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. */
|
|
1166
|
+
async addpause(pause) {
|
|
1167
|
+
const records = await this.getpauses();
|
|
1168
|
+
const combined = [pause, ...records.filter((item) => item.id !== pause.id)];
|
|
1169
|
+
const retention = (await this.getsettings())?.pauseretention;
|
|
1170
|
+
if (retention === void 0) {
|
|
1171
|
+
await this.adapter.set("pauses", combined);
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const kept = combined.slice(0, retention);
|
|
1175
|
+
const expired = combined.slice(retention).map((item) => {
|
|
1176
|
+
if (item.framesexpired === true) return item;
|
|
1177
|
+
const faded = { ...item, callframes: [], framesexpired: true };
|
|
1178
|
+
delete faded.domsnapshotid;
|
|
1179
|
+
return faded;
|
|
1180
|
+
});
|
|
1181
|
+
await this.adapter.set("pauses", [...kept, ...expired]);
|
|
1182
|
+
}
|
|
1183
|
+
/** Returns every stored pause state capture, newest first. */
|
|
1184
|
+
async getpauses() {
|
|
1185
|
+
return await this.adapter.get("pauses") ?? [];
|
|
1186
|
+
}
|
|
1187
|
+
/** Returns the pause state captures of one run, newest first. */
|
|
1188
|
+
async listpauses(runid) {
|
|
1189
|
+
const records = await this.getpauses();
|
|
1190
|
+
return records.filter((item) => item.runid === runid);
|
|
1191
|
+
}
|
|
1192
|
+
/** Stores one watch expression with its per pause values, replacing the previous expression of its id. */
|
|
1193
|
+
async setwatchexpression(expression) {
|
|
1194
|
+
const records = (await this.adapter.get("watchexpressions") ?? []).filter((item) => item.id !== expression.id);
|
|
1195
|
+
await this.adapter.set("watchexpressions", [expression, ...records]);
|
|
1196
|
+
}
|
|
1197
|
+
/** Returns every stored watch expression, newest first. */
|
|
1198
|
+
async getwatchexpressions() {
|
|
1199
|
+
return await this.adapter.get("watchexpressions") ?? [];
|
|
1200
|
+
}
|
|
1201
|
+
/** Stores one script override with its review provenance, replacing the previous override of its id. */
|
|
1202
|
+
async addscriptoverride(spec) {
|
|
1203
|
+
const records = (await this.adapter.get("scriptoverrides") ?? []).filter((item) => item.id !== spec.id);
|
|
1204
|
+
await this.adapter.set("scriptoverrides", [spec, ...records]);
|
|
1205
|
+
}
|
|
1206
|
+
/** Returns every stored script override, newest first. */
|
|
1207
|
+
async getscriptoverrides() {
|
|
1208
|
+
return await this.adapter.get("scriptoverrides") ?? [];
|
|
1209
|
+
}
|
|
1210
|
+
/** Stores one debugger consent decision per origin with the consented domain list, replacing the previous decision of its id. */
|
|
1211
|
+
async setdebuggergrant(grant) {
|
|
1212
|
+
const records = (await this.adapter.get("debuggergrants") ?? []).filter((item) => item.id !== grant.id);
|
|
1213
|
+
await this.adapter.set("debuggergrants", [grant, ...records]);
|
|
1214
|
+
}
|
|
1215
|
+
/** Returns every debugger consent decision, newest first. */
|
|
1216
|
+
async getdebuggergrants() {
|
|
1217
|
+
return await this.adapter.get("debuggergrants") ?? [];
|
|
1218
|
+
}
|
|
1219
|
+
/** Revokes every approved debugger consent of one origin: the revoke time stamps the records so the next attach needs a new reviewed prompt. */
|
|
1220
|
+
async revokedebuggergrants(origin, at) {
|
|
1221
|
+
const records = await this.getdebuggergrants();
|
|
1222
|
+
let revoked = 0;
|
|
1223
|
+
const updated = records.map((grant) => {
|
|
1224
|
+
if (grant.origin !== origin || grant.revokedat !== void 0) return grant;
|
|
1225
|
+
revoked += 1;
|
|
1226
|
+
return { ...grant, revokedat: at };
|
|
1227
|
+
});
|
|
1228
|
+
await this.adapter.set("debuggergrants", updated);
|
|
1229
|
+
return revoked;
|
|
1230
|
+
}
|
|
1129
1231
|
/** Merges expired entry counts into the per run level count summary that survives the retention window. */
|
|
1130
1232
|
async mergelevelsummary(runid, counts, now) {
|
|
1131
1233
|
const records = await this.getlevelsummaries();
|
|
@@ -2005,6 +2107,126 @@ function ratelimitwait(state, now) {
|
|
|
2005
2107
|
return Math.max(0, state.resetat - now);
|
|
2006
2108
|
}
|
|
2007
2109
|
|
|
2110
|
+
// cdpbus.ts
|
|
2111
|
+
var cdpkinds = ["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"];
|
|
2112
|
+
var cdpdomains = ["Runtime", "Log", "Debugger", "DOM", "Network", "Page"];
|
|
2113
|
+
function methoddomain(method) {
|
|
2114
|
+
const match = /^([A-Z][A-Za-z]*)\.([a-zA-Z][A-Za-z0-9]*)$/.exec(method.trim());
|
|
2115
|
+
return match?.[1];
|
|
2116
|
+
}
|
|
2117
|
+
function cdpallowlistof(value) {
|
|
2118
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2119
|
+
const entry = value;
|
|
2120
|
+
const domains = Array.isArray(entry.domains) ? entry.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
2121
|
+
if (domains.length === 0) return void 0;
|
|
2122
|
+
if (entry.methods === void 0) return { domains };
|
|
2123
|
+
const methods = Array.isArray(entry.methods) ? entry.methods.filter((method) => typeof method === "string" && methoddomain(method) !== void 0 && domains.includes(methoddomain(method))) : [];
|
|
2124
|
+
if (methods.length === 0) return void 0;
|
|
2125
|
+
return { domains, methods };
|
|
2126
|
+
}
|
|
2127
|
+
function allowlistcovers(allowlist, method) {
|
|
2128
|
+
const domain = methoddomain(method);
|
|
2129
|
+
if (domain === void 0) return false;
|
|
2130
|
+
if (!allowlist.domains.includes(domain)) return false;
|
|
2131
|
+
if (allowlist.methods !== void 0 && !allowlist.methods.includes(method)) return false;
|
|
2132
|
+
return true;
|
|
2133
|
+
}
|
|
2134
|
+
function attachcdpsession(input) {
|
|
2135
|
+
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 };
|
|
2136
|
+
}
|
|
2137
|
+
function detachcdpsession(session, at, userdetached = false) {
|
|
2138
|
+
return { ...session, detachedat: at, ...userdetached ? { userdetached: true } : {} };
|
|
2139
|
+
}
|
|
2140
|
+
function sendcdpcommand(input) {
|
|
2141
|
+
const domain = methoddomain(input.method);
|
|
2142
|
+
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 };
|
|
2143
|
+
return { ...input, method: input.method.trim(), domain, duration: input.duration, ...input.errorclass !== void 0 ? { errorclass: input.errorclass } : {}, at: input.at };
|
|
2144
|
+
}
|
|
2145
|
+
function serializecdpcommand(queue, command) {
|
|
2146
|
+
return [...queue, command];
|
|
2147
|
+
}
|
|
2148
|
+
function cdpeventruleof(value) {
|
|
2149
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2150
|
+
const entry = value;
|
|
2151
|
+
const domain = typeof entry.domain === "string" && cdpdomains.includes(entry.domain) ? entry.domain : void 0;
|
|
2152
|
+
const event = typeof entry.event === "string" && entry.event.trim() ? entry.event.trim() : void 0;
|
|
2153
|
+
if (domain === void 0 || event === void 0) return void 0;
|
|
2154
|
+
const match = typeof entry.match === "string" && entry.match.trim() ? entry.match.trim() : void 0;
|
|
2155
|
+
return { domain, event, ...match !== void 0 ? { match } : {} };
|
|
2156
|
+
}
|
|
2157
|
+
function watchcdpevents(rules, events) {
|
|
2158
|
+
const matched = [];
|
|
2159
|
+
const counts = {};
|
|
2160
|
+
for (const domain of cdpdomains) counts[domain] = 0;
|
|
2161
|
+
for (const event of events) {
|
|
2162
|
+
for (const rule of rules) {
|
|
2163
|
+
if (rule.domain !== event.domain || rule.event !== event.event) continue;
|
|
2164
|
+
if (rule.match !== void 0 && !(event.payload ?? "").includes(rule.match)) continue;
|
|
2165
|
+
matched.push({ ruleid: rule.id, domain: event.domain, event: event.event, ...event.payload !== void 0 ? { payload: event.payload } : {} });
|
|
2166
|
+
counts[event.domain] = (counts[event.domain] ?? 0) + 1;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
return { matched, counts };
|
|
2170
|
+
}
|
|
2171
|
+
function breakpointinputof(value) {
|
|
2172
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2173
|
+
const entry = value;
|
|
2174
|
+
const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : void 0;
|
|
2175
|
+
const line = typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : void 0;
|
|
2176
|
+
if (url === void 0 || line === void 0) return void 0;
|
|
2177
|
+
const column = typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : void 0;
|
|
2178
|
+
const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : void 0;
|
|
2179
|
+
return { url, line, ...column !== void 0 ? { column } : {}, ...condition !== void 0 ? { condition } : {} };
|
|
2180
|
+
}
|
|
2181
|
+
function capturepause(input) {
|
|
2182
|
+
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 };
|
|
2183
|
+
}
|
|
2184
|
+
function stepmodeof(value) {
|
|
2185
|
+
const modes = ["stepover", "stepinto", "stepout", "resume"];
|
|
2186
|
+
return typeof value === "string" && modes.includes(value) ? value : void 0;
|
|
2187
|
+
}
|
|
2188
|
+
function watchexpressionof(value) {
|
|
2189
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2190
|
+
const entry = value;
|
|
2191
|
+
const expression = typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : void 0;
|
|
2192
|
+
if (expression === void 0) return void 0;
|
|
2193
|
+
const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
|
|
2194
|
+
return { expression, scope };
|
|
2195
|
+
}
|
|
2196
|
+
function recordwatchvalue(expression, pauseid, value, at) {
|
|
2197
|
+
return { ...expression, values: [...expression.values, { pauseid, value, at }] };
|
|
2198
|
+
}
|
|
2199
|
+
function overrideinputof(value) {
|
|
2200
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2201
|
+
const entry = value;
|
|
2202
|
+
const urlpattern = typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : void 0;
|
|
2203
|
+
const source = typeof entry.source === "string" ? entry.source : void 0;
|
|
2204
|
+
if (urlpattern === void 0 || source === void 0 || source.trim().length === 0) return void 0;
|
|
2205
|
+
return { urlpattern, source };
|
|
2206
|
+
}
|
|
2207
|
+
function teardownplanof(value) {
|
|
2208
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
2209
|
+
const entry = value;
|
|
2210
|
+
const revertsteps = Array.isArray(entry.revertsteps) ? entry.revertsteps.filter((step) => typeof step === "string" && step.trim().length > 0) : [];
|
|
2211
|
+
const policy = entry.resumepolicy;
|
|
2212
|
+
if (revertsteps.length === 0) return void 0;
|
|
2213
|
+
if (policy !== void 0 && policy !== "resume" && policy !== "pause" && policy !== "ask") return void 0;
|
|
2214
|
+
return { revertsteps, resumepolicy: policy ?? "ask" };
|
|
2215
|
+
}
|
|
2216
|
+
function teardowncdpsession(input) {
|
|
2217
|
+
const revertedbreakpoints = input.breakpoints.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
2218
|
+
const revertedoverrides = input.overrides.filter((spec) => spec.revertedat === void 0).map((spec) => spec.id);
|
|
2219
|
+
const resumepolicy = input.userdetached ? "pause" : input.plan?.resumepolicy ?? "ask";
|
|
2220
|
+
return {
|
|
2221
|
+
session: detachcdpsession(input.session, input.at, input.userdetached),
|
|
2222
|
+
revertedbreakpoints,
|
|
2223
|
+
revertedoverrides,
|
|
2224
|
+
resumepolicy,
|
|
2225
|
+
paused: input.userdetached || resumepolicy === "pause",
|
|
2226
|
+
keepsalive: input.userdetached
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2008
2230
|
// netauth.ts
|
|
2009
2231
|
function oauthflowof(value) {
|
|
2010
2232
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
@@ -2143,7 +2365,7 @@ ${file.content}\r
|
|
|
2143
2365
|
// runtimeline.ts
|
|
2144
2366
|
var timelinekinds = ["watchconsole", "watcherrors", "watchtasks"];
|
|
2145
2367
|
var loglevels = ["error", "warn", "info", "log", "debug", "trace"];
|
|
2146
|
-
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network"];
|
|
2368
|
+
var timelinesources = ["console", "error", "rejection", "resource", "longtask", "network", "cdp"];
|
|
2147
2369
|
function attachtimeline(input) {
|
|
2148
2370
|
return { runid: input.runid, origin: input.origin, stepids: [...input.stepids], attachedat: input.now, entries: [] };
|
|
2149
2371
|
}
|
|
@@ -2238,9 +2460,9 @@ function consolediff(input) {
|
|
|
2238
2460
|
}
|
|
2239
2461
|
|
|
2240
2462
|
// policy.ts
|
|
2241
|
-
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"]);
|
|
2242
|
-
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"]);
|
|
2243
|
-
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"]);
|
|
2463
|
+
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"]);
|
|
2464
|
+
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"]);
|
|
2465
|
+
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"]);
|
|
2244
2466
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
2245
2467
|
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
2246
2468
|
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"]);
|
|
@@ -2257,6 +2479,7 @@ var socketactions = /* @__PURE__ */ new Set(["opensocket", "sendmessage", "waitm
|
|
|
2257
2479
|
var netwatchactions = /* @__PURE__ */ new Set(["watchrequests", "readheaders", "capturebodies", "mapapi", "extractapi"]);
|
|
2258
2480
|
var controlactions = /* @__PURE__ */ new Set(["blockrequest", "mockresponse", "rewriteheaders", "setcookies", "readcookies", "clearcookies", "authflow", "saveapikey", "routeproxy", "postform", "postfiles"]);
|
|
2259
2481
|
var debugactions = /* @__PURE__ */ new Set(["watchconsole", "watcherrors", "watchtasks"]);
|
|
2482
|
+
var cdpactions = /* @__PURE__ */ new Set(["attachcdp", "detachcdp", "cdpcmd", "watchcdp", "setbreakpoint", "stepcode", "watchexpr", "overridescript"]);
|
|
2260
2483
|
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"]);
|
|
2261
2484
|
var fieldkinds = ["text", "email", "phone", "date", "number", "select", "check", "radio", "file", "password", "card", "code"];
|
|
2262
2485
|
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
@@ -2275,6 +2498,9 @@ function hostpattern(origin) {
|
|
|
2275
2498
|
function isdebugkind(kind) {
|
|
2276
2499
|
return debugactions.has(kind);
|
|
2277
2500
|
}
|
|
2501
|
+
function iscdpkind(kind) {
|
|
2502
|
+
return cdpactions.has(kind);
|
|
2503
|
+
}
|
|
2278
2504
|
function actionrisk(kind) {
|
|
2279
2505
|
if (!allowedactions.has(kind)) throw new Error("Unsupported browser action.");
|
|
2280
2506
|
if (sensitiveactions.has(kind)) return "sensitive";
|
|
@@ -3575,6 +3801,127 @@ function rotationruleof(value) {
|
|
|
3575
3801
|
if (maxentries === void 0 || overflowtarget === void 0) return void 0;
|
|
3576
3802
|
return { maxentries, overflowtarget };
|
|
3577
3803
|
}
|
|
3804
|
+
function debuggate(session, tabid2, origin, now) {
|
|
3805
|
+
if (!session || session.stoppedat) return { allowed: false, reason: "No active browser session exists for the devtools protocol step." };
|
|
3806
|
+
if (session.expiresat <= now) return { allowed: false, reason: "The browser session has expired and cannot run a devtools protocol step." };
|
|
3807
|
+
if (session.pausedat) return { allowed: false, reason: "The browser session is paused and cannot run a devtools protocol step." };
|
|
3808
|
+
if (session.tabid !== tabid2) return { allowed: false, reason: `The devtools protocol step needs the run tab ${session.tabid} and refuses tab ${tabid2}.` };
|
|
3809
|
+
if (!origingranted(session, origin)) return { allowed: false, reason: `The devtools protocol step on ${origin} needs the session origin grants first.` };
|
|
3810
|
+
return { allowed: true };
|
|
3811
|
+
}
|
|
3812
|
+
function debuggerconsentcovers(origin, domains, grants) {
|
|
3813
|
+
const needed = [...new Set(domains)];
|
|
3814
|
+
const covering = grants.find((grant) => grant.origin === origin && grant.approved === true && grant.revokedat === void 0 && needed.every((domain) => grant.domains.includes(domain)));
|
|
3815
|
+
if (covering) return { allowed: true };
|
|
3816
|
+
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.` };
|
|
3817
|
+
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.` };
|
|
3818
|
+
}
|
|
3819
|
+
function validatebreakpointcondition(condition) {
|
|
3820
|
+
const expression = condition.trim();
|
|
3821
|
+
if (expression.length === 0) return { allowed: false, reason: "The breakpoint condition must not be empty." };
|
|
3822
|
+
if (/(?<![=!<>])=(?!=)/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse assignment because the reviewed grammar is comparison only." };
|
|
3823
|
+
if (/[A-Za-z_$][\w$]*\s*\(/.test(expression)) return { allowed: false, reason: "Breakpoint conditions refuse calls because the reviewed grammar is comparison only." };
|
|
3824
|
+
const literal = /^(?:-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|true|false|null)$/;
|
|
3825
|
+
const tokens = expression.match(/(?:[A-Za-z_$][\w$]*|-?\d+(?:\.\d+)?|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|===|!==|==|!=|>=|<=|&&|\|\||[!.<>()+\-*\/%])/g);
|
|
3826
|
+
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." };
|
|
3827
|
+
const identifierlike = /^(?:true|false|null)$/;
|
|
3828
|
+
for (const token of tokens) {
|
|
3829
|
+
if (literal.test(token) || identifierlike.test(token)) continue;
|
|
3830
|
+
if (["===", "!==", "==", "!=", ">=", "<=", "&&", "||", "!", ".", "(", ")", "<", ">", "+", "-", "*", "/", "%"].includes(token)) continue;
|
|
3831
|
+
if (/^[A-Za-z_$][\w$]*$/.test(token)) continue;
|
|
3832
|
+
return { allowed: false, reason: `The token ${token} of the breakpoint condition stays outside the reviewed expression grammar.` };
|
|
3833
|
+
}
|
|
3834
|
+
return { allowed: true };
|
|
3835
|
+
}
|
|
3836
|
+
function breakpointbudgetallowed(active, ceiling) {
|
|
3837
|
+
if (ceiling === void 0) return { allowed: true };
|
|
3838
|
+
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." };
|
|
3839
|
+
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.` };
|
|
3840
|
+
return { allowed: true };
|
|
3841
|
+
}
|
|
3842
|
+
function breakpointceilingof(settings) {
|
|
3843
|
+
return settings?.breakpointceiling;
|
|
3844
|
+
}
|
|
3845
|
+
function validatecdpgrammar(step, options) {
|
|
3846
|
+
const kind = step.kind;
|
|
3847
|
+
if (kind === "attachcdp") {
|
|
3848
|
+
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(", ")}.` };
|
|
3849
|
+
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." };
|
|
3850
|
+
if (options.allowlist !== void 0) {
|
|
3851
|
+
const allowlist = cdpallowlistof(options.allowlist);
|
|
3852
|
+
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." };
|
|
3853
|
+
}
|
|
3854
|
+
const budgetcheck = debugwaitbudgetallowed(typeof options.wait === "number" ? options.wait : void 0, void 0);
|
|
3855
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3856
|
+
return { allowed: true };
|
|
3857
|
+
}
|
|
3858
|
+
if (kind === "detachcdp") return { allowed: true };
|
|
3859
|
+
if (kind === "cdpcmd") {
|
|
3860
|
+
const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : void 0;
|
|
3861
|
+
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." };
|
|
3862
|
+
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." };
|
|
3863
|
+
if (command.resultpath !== void 0 && typeof command.resultpath !== "string") return { allowed: false, reason: "The reviewed result path must be a dotted path string." };
|
|
3864
|
+
return { allowed: true };
|
|
3865
|
+
}
|
|
3866
|
+
if (kind === "watchcdp") {
|
|
3867
|
+
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." };
|
|
3868
|
+
let watchwindow;
|
|
3869
|
+
if (options.watch !== void 0) {
|
|
3870
|
+
const watch = options.watch;
|
|
3871
|
+
if (!watch || typeof watch !== "object" || Array.isArray(watch)) return { allowed: false, reason: "The reviewed event watch window must be an object." };
|
|
3872
|
+
const reviewed = watch;
|
|
3873
|
+
if (reviewed.window !== void 0) {
|
|
3874
|
+
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." };
|
|
3875
|
+
watchwindow = reviewed.window;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
if (watchwindow === void 0) return { allowed: false, reason: "The event watch needs a reviewed lifetime window before any domain event is observed." };
|
|
3879
|
+
const budgetcheck = debugwaitbudgetallowed(watchwindow, typeof options.wait === "number" ? options.wait : void 0);
|
|
3880
|
+
if (!budgetcheck.allowed) return budgetcheck;
|
|
3881
|
+
return { allowed: true };
|
|
3882
|
+
}
|
|
3883
|
+
if (kind === "setbreakpoint") {
|
|
3884
|
+
const breakpoint = breakpointinputof(options.breakpoint);
|
|
3885
|
+
if (!breakpoint) return { allowed: false, reason: "The breakpoint needs a reviewed script url and a zero based line." };
|
|
3886
|
+
if (!ishttpsurl(breakpoint.url)) return { allowed: false, reason: "The breakpoint script url must be a reviewed HTTPS url." };
|
|
3887
|
+
if (breakpoint.condition !== void 0) {
|
|
3888
|
+
const conditioncheck = validatebreakpointcondition(breakpoint.condition);
|
|
3889
|
+
if (!conditioncheck.allowed) return conditioncheck;
|
|
3890
|
+
}
|
|
3891
|
+
return { allowed: true };
|
|
3892
|
+
}
|
|
3893
|
+
if (kind === "stepcode") {
|
|
3894
|
+
if (stepmodeof(options.mode) === void 0) return { allowed: false, reason: "The step code mode must be one of stepover, stepinto, stepout or resume." };
|
|
3895
|
+
return { allowed: true };
|
|
3896
|
+
}
|
|
3897
|
+
if (kind === "watchexpr") {
|
|
3898
|
+
if (watchexpressionof(options.expression) === void 0) return { allowed: false, reason: "The watch expression needs the reviewed expression text." };
|
|
3899
|
+
if (options.reviewed !== true) return { allowed: false, reason: "Watch expressions must be reviewed before evaluation; set the explicit reviewed flag on the step." };
|
|
3900
|
+
return { allowed: true };
|
|
3901
|
+
}
|
|
3902
|
+
if (kind === "overridescript") {
|
|
3903
|
+
const override = overrideinputof(options.override);
|
|
3904
|
+
if (!override) return { allowed: false, reason: "The script override needs a reviewed url pattern and its full fixture source." };
|
|
3905
|
+
if (patternorigin(override.urlpattern) === void 0) return { allowed: false, reason: "Script overrides without a named https origin pattern are refused." };
|
|
3906
|
+
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." };
|
|
3907
|
+
return { allowed: true };
|
|
3908
|
+
}
|
|
3909
|
+
return { allowed: true };
|
|
3910
|
+
}
|
|
3911
|
+
function planallowlist(steps) {
|
|
3912
|
+
const attach = steps.find((step) => step.kind === "attachcdp");
|
|
3913
|
+
if (!attach) return void 0;
|
|
3914
|
+
let options = {};
|
|
3915
|
+
try {
|
|
3916
|
+
options = parseoptions(attach);
|
|
3917
|
+
} catch {
|
|
3918
|
+
options = {};
|
|
3919
|
+
}
|
|
3920
|
+
const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
3921
|
+
if (domains.length === 0) return void 0;
|
|
3922
|
+
const gated = cdpallowlistof(options.allowlist);
|
|
3923
|
+
return { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} };
|
|
3924
|
+
}
|
|
3578
3925
|
function controltarget(step) {
|
|
3579
3926
|
let options = {};
|
|
3580
3927
|
try {
|
|
@@ -3988,6 +4335,10 @@ function validatestep(step, origin) {
|
|
|
3988
4335
|
const timelinecheck = validatetimelinegrammar(step, options);
|
|
3989
4336
|
if (!timelinecheck.allowed) return timelinecheck;
|
|
3990
4337
|
}
|
|
4338
|
+
if (iscdpkind(step.kind)) {
|
|
4339
|
+
const cdpcheck = validatecdpgrammar(step, options);
|
|
4340
|
+
if (!cdpcheck.allowed) return cdpcheck;
|
|
4341
|
+
}
|
|
3991
4342
|
if (step.kind === "tabcreate") {
|
|
3992
4343
|
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
3993
4344
|
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." };
|
|
@@ -4093,6 +4444,46 @@ function canexecute(input) {
|
|
|
4093
4444
|
const timelinegatecheck = timelinegate(input.session, input.tabid, input.origin, now);
|
|
4094
4445
|
if (!timelinegatecheck.allowed) return timelinegatecheck;
|
|
4095
4446
|
}
|
|
4447
|
+
if (iscdpkind(input.step.kind)) {
|
|
4448
|
+
const debuggatecheck = debuggate(input.session, input.tabid, input.origin, now);
|
|
4449
|
+
if (!debuggatecheck.allowed) return debuggatecheck;
|
|
4450
|
+
if (!input.plan) return { allowed: false, reason: "The devtools protocol steps need an approved plan." };
|
|
4451
|
+
const allowlist = planallowlist(input.plan.steps);
|
|
4452
|
+
if (input.step.kind !== "attachcdp") {
|
|
4453
|
+
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." };
|
|
4454
|
+
if (input.step.kind === "cdpcmd") {
|
|
4455
|
+
let cdpoptions2 = {};
|
|
4456
|
+
try {
|
|
4457
|
+
cdpoptions2 = parseoptions(input.step);
|
|
4458
|
+
} catch {
|
|
4459
|
+
cdpoptions2 = {};
|
|
4460
|
+
}
|
|
4461
|
+
const command = cdpoptions2.command && typeof cdpoptions2.command === "object" && !Array.isArray(cdpoptions2.command) ? cdpoptions2.command : void 0;
|
|
4462
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
4463
|
+
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.` };
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
let cdpoptions = {};
|
|
4467
|
+
try {
|
|
4468
|
+
cdpoptions = parseoptions(input.step);
|
|
4469
|
+
} catch {
|
|
4470
|
+
cdpoptions = {};
|
|
4471
|
+
}
|
|
4472
|
+
if (input.step.kind === "setbreakpoint") {
|
|
4473
|
+
const breakpoint = breakpointinputof(cdpoptions.breakpoint);
|
|
4474
|
+
if (breakpoint) {
|
|
4475
|
+
const targetgate = origincheck(input.session, breakpoint.url);
|
|
4476
|
+
if (!targetgate.allowed) return targetgate;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
if (input.step.kind === "overridescript") {
|
|
4480
|
+
const override = overrideinputof(cdpoptions.override);
|
|
4481
|
+
if (override) {
|
|
4482
|
+
const targetgate = origincheck(input.session, override.urlpattern);
|
|
4483
|
+
if (!targetgate.allowed) return targetgate;
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4096
4487
|
if (iscontrolkind(input.step.kind)) {
|
|
4097
4488
|
const controlgate = sessiongate({ session: input.session, tabid: input.tabid, origin: input.origin, now, action: "control the network" });
|
|
4098
4489
|
if (!controlgate.allowed) return controlgate;
|
|
@@ -4326,9 +4717,15 @@ function recordtimeline(progress, planid, stepid, entry, now) {
|
|
|
4326
4717
|
const outcome = { stepid, ok: true, summary: `Captured ${entry.entries} timeline entr${entry.entries === 1 ? "y" : "ies"} with ${entry.collapsed} collapsed repeat${entry.collapsed === 1 ? "" : "s"}, ${entry.errors} error${entry.errors === 1 ? "" : "s"}, ${entry.rejections} rejection${entry.rejections === 1 ? "" : "s"} and ${entry.longtasks} long task${entry.longtasks === 1 ? "" : "s"}.`, details: { timeline: entry }, at: now };
|
|
4327
4718
|
return recordoutcome(base, planid, outcome, now);
|
|
4328
4719
|
}
|
|
4720
|
+
function recordcdp(progress, planid, stepid, entry, now) {
|
|
4721
|
+
const base = progress && progress.planid === planid ? progress : emptyprogress(planid, now);
|
|
4722
|
+
const summary = entry.family === "command" ? `The reviewed ${entry.method ?? "raw"} command of the ${entry.domain ?? "unknown"} domain returned in ${entry.duration ?? 0} millisecond${(entry.duration ?? 0) === 1 ? "" : "s"}${entry.errorclass !== void 0 ? ` with the ${entry.errorclass} error class` : ""}.` : `The devtools ${entry.family} step ran${entry.domain !== void 0 ? ` on the ${entry.domain} domain` : ""}${entry.events !== void 0 ? ` and matched ${entry.events} event${entry.events === 1 ? "" : "s"}` : ""}${entry.hits !== void 0 ? ` with ${entry.hits} hit${entry.hits === 1 ? "" : "s"}` : ""}${entry.frames !== void 0 ? ` capturing ${entry.frames} call frame${entry.frames === 1 ? "" : "s"}` : ""}.`;
|
|
4723
|
+
const outcome = { stepid, ok: entry.errorclass === void 0, summary, details: { cdp: entry }, at: now };
|
|
4724
|
+
return recordoutcome(base, planid, outcome, now);
|
|
4725
|
+
}
|
|
4329
4726
|
|
|
4330
4727
|
// version.ts
|
|
4331
|
-
var packageversion = "1.1.
|
|
4728
|
+
var packageversion = "1.1.46";
|
|
4332
4729
|
|
|
4333
4730
|
// types.ts
|
|
4334
4731
|
var protocolversion = packageversion;
|
|
@@ -4353,6 +4750,20 @@ function parseproposal(value, origin, grants) {
|
|
|
4353
4750
|
const expiresat = typeof planinput.expiresat === "number" ? planinput.expiresat : createdat + 10 * 60 * 1e3;
|
|
4354
4751
|
if (expiresat <= createdat) throw new Error("Plan expiry must be in the future.");
|
|
4355
4752
|
const planwindow = expiresat - createdat;
|
|
4753
|
+
const attachinput = stepsinput.map((input) => record(input)).find((candidate) => candidate.kind === "attachcdp");
|
|
4754
|
+
let planallowlist2;
|
|
4755
|
+
if (attachinput !== void 0) {
|
|
4756
|
+
const attachoptions = (() => {
|
|
4757
|
+
try {
|
|
4758
|
+
return parseoptions({ id: "attach", kind: "attachcdp", summary: "attach", risk: "sensitive", ...typeof attachinput.options === "string" ? { options: attachinput.options } : {} });
|
|
4759
|
+
} catch {
|
|
4760
|
+
return {};
|
|
4761
|
+
}
|
|
4762
|
+
})();
|
|
4763
|
+
const domains = Array.isArray(attachoptions.domains) ? attachoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
4764
|
+
const gated = cdpallowlistof(attachoptions.allowlist);
|
|
4765
|
+
planallowlist2 = domains.length > 0 ? { domains, ...gated?.methods !== void 0 ? { methods: gated.methods } : {} } : void 0;
|
|
4766
|
+
}
|
|
4356
4767
|
const steps = stepsinput.map((input, index) => {
|
|
4357
4768
|
const candidate = record(input);
|
|
4358
4769
|
const kind = text(candidate.kind, `step ${index + 1} kind`);
|
|
@@ -4403,6 +4814,36 @@ function parseproposal(value, origin, grants) {
|
|
|
4403
4814
|
if (!granted) throw new Error(`The ${step.kind} capture of ${origin} targets an origin outside the grants.`);
|
|
4404
4815
|
if (debugoptions.level !== void 0 && !loglevels.includes(debugoptions.level)) throw new Error(`The reviewed level floor must be one of ${loglevels.join(", ")}.`);
|
|
4405
4816
|
}
|
|
4817
|
+
if (iscdpkind(step.kind)) {
|
|
4818
|
+
const granted = covered.some((pattern) => {
|
|
4819
|
+
try {
|
|
4820
|
+
return new URL(origin).origin === new URL(pattern).origin;
|
|
4821
|
+
} catch {
|
|
4822
|
+
return false;
|
|
4823
|
+
}
|
|
4824
|
+
});
|
|
4825
|
+
if (!granted) throw new Error(`The ${step.kind} step of ${origin} targets an origin outside the grants.`);
|
|
4826
|
+
let cdpoptions = {};
|
|
4827
|
+
try {
|
|
4828
|
+
cdpoptions = parseoptions(step);
|
|
4829
|
+
} catch {
|
|
4830
|
+
cdpoptions = {};
|
|
4831
|
+
}
|
|
4832
|
+
if (step.kind === "attachcdp") {
|
|
4833
|
+
if (teardownplanof(cdpoptions.teardown) === void 0) throw new Error("Attach steps without a reviewed teardown plan are refused.");
|
|
4834
|
+
const domains = Array.isArray(cdpoptions.domains) ? cdpoptions.domains.filter((domain) => typeof domain === "string" && cdpdomains.includes(domain)) : [];
|
|
4835
|
+
if (domains.length === 0) throw new Error("Attach steps need a non-empty enabled domain list of the reviewed domain grammar.");
|
|
4836
|
+
const gated = cdpallowlistof(cdpoptions.allowlist);
|
|
4837
|
+
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.");
|
|
4838
|
+
}
|
|
4839
|
+
if (step.kind === "cdpcmd") {
|
|
4840
|
+
const command = cdpoptions.command && typeof cdpoptions.command === "object" && !Array.isArray(cdpoptions.command) ? cdpoptions.command : void 0;
|
|
4841
|
+
const method = typeof command?.method === "string" ? command.method : "";
|
|
4842
|
+
if (methoddomain(method) === void 0) throw new Error("Raw commands need a reviewed method of the Domain.method form.");
|
|
4843
|
+
if (planallowlist2 === void 0) throw new Error("Raw command steps need the attachcdp step of the same plan with its enabled domains first.");
|
|
4844
|
+
if (!allowlistcovers(planallowlist2, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the plan attach.`);
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4406
4847
|
const evaluation = validatestep(step, origin);
|
|
4407
4848
|
if (!evaluation.allowed) throw new Error(evaluation.reason);
|
|
4408
4849
|
const target = outboundtarget(step);
|
|
@@ -4477,7 +4918,7 @@ function requestbody(input) {
|
|
|
4477
4918
|
return JSON.stringify({ version: protocolversion, objective: input.objective, session: input.session, observation: input.observation, capabilities: input.capabilities });
|
|
4478
4919
|
}
|
|
4479
4920
|
function outcomeresponse(input) {
|
|
4480
|
-
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 } : {} });
|
|
4921
|
+
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 } : {} });
|
|
4481
4922
|
}
|
|
4482
4923
|
function mapresponse(input) {
|
|
4483
4924
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, map: input.map });
|
|
@@ -4579,6 +5020,19 @@ function timelinereport(input) {
|
|
|
4579
5020
|
function consolediffreport(input) {
|
|
4580
5021
|
return { version: protocolversion, diff: input.diff };
|
|
4581
5022
|
}
|
|
5023
|
+
function cdpreport(input) {
|
|
5024
|
+
const overrides = input.overrides.map((spec) => {
|
|
5025
|
+
const { source, ...metadata } = spec;
|
|
5026
|
+
void source;
|
|
5027
|
+
return metadata;
|
|
5028
|
+
});
|
|
5029
|
+
const grants = input.grants.map((grant) => {
|
|
5030
|
+
const { prompt, ...metadata } = grant;
|
|
5031
|
+
void prompt;
|
|
5032
|
+
return metadata;
|
|
5033
|
+
});
|
|
5034
|
+
return { version: protocolversion, sessions: input.sessions, commands: input.commands, events: input.events, breakpoints: input.breakpoints, pauses: input.pauses, watches: input.watches, overrides, grants };
|
|
5035
|
+
}
|
|
4582
5036
|
|
|
4583
5037
|
// capture.ts
|
|
4584
5038
|
var capturekinds = ["shotview", "shotfullpage", "shotelement", "shotregion", "contactsheet"];
|
|
@@ -6267,7 +6721,7 @@ function stepoptions2(step) {
|
|
|
6267
6721
|
}
|
|
6268
6722
|
async function refreshcapabilities() {
|
|
6269
6723
|
const report = await readcapabilities();
|
|
6270
|
-
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds] };
|
|
6724
|
+
const withmedia = { ...report, captures: [...capturekinds], media: [...mediakinds], http: [...httpkinds], netwatch: [...socketkinds, ...netwatchkinds], control: [...controlkinds], debug: [...timelinekinds, ...cdpkinds] };
|
|
6271
6725
|
await memory.setcapabilities(withmedia);
|
|
6272
6726
|
return withmedia;
|
|
6273
6727
|
}
|
|
@@ -6701,6 +7155,11 @@ async function tracktabupdate(tabid2, changeinfo) {
|
|
|
6701
7155
|
await audit("timeline", `Watcher ${id} detached when the run tab navigated to ${url} at ${detached.at}; every page hook of the destroyed context is gone with it.`, { sessionid: session.id });
|
|
6702
7156
|
activetimelinewatchers.delete(id);
|
|
6703
7157
|
}
|
|
7158
|
+
for (const [runid, active] of [...activecdpsessions.entries()]) {
|
|
7159
|
+
if (active.session.detachedat !== void 0 || active.session.tabid !== tabid2) continue;
|
|
7160
|
+
await detachcdpforrun(runid, `the run tab navigated to ${url}`).catch(() => {
|
|
7161
|
+
});
|
|
7162
|
+
}
|
|
6704
7163
|
return;
|
|
6705
7164
|
}
|
|
6706
7165
|
const buffer = navbuffers.get(tabid2) ?? [];
|
|
@@ -9460,6 +9919,234 @@ async function executetimelinestep(step, session, plan, tabid2, origin) {
|
|
|
9460
9919
|
await audit("timeline", `Captured ${stored.length} timeline entr${stored.length === 1 ? "y" : "ies"} of ${origin} for the reviewed window of ${watchwindow} milliseconds${collapsed > 0 ? ` with ${collapsed} collapsed repeat${collapsed === 1 ? "" : "s"}` : ""}${errorids.length > 0 ? `, ${errorids.length} error${errorids.length === 1 ? "" : "s"}` : ""}${rejectionids.length > 0 ? `, ${rejectionids.length} rejection${rejectionids.length === 1 ? "" : "s"}` : ""}${longtasks.length > 0 ? ` and ${longtasks.length} long task${longtasks.length === 1 ? "" : "s"}` : ""}; console, error and task watching derives from page-injected listeners and the performance buffers.`, extra);
|
|
9461
9920
|
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, timeline: { entries: stored.length, levels: counts, collapsed }, entries: stored, errorids, rejectionids, longtasks: longtasks.map((task) => ({ ...task, blocking: blocking.blocking })), ...flagged.length > 0 ? { flagged } : {}, ...rotationtarget ? { rotation: rotationtarget } : {} } };
|
|
9462
9921
|
}
|
|
9922
|
+
var cdpderivation = "devthink instrumented harness 1.1.46 (no chrome.debugger permission)";
|
|
9923
|
+
var activecdpsessions = /* @__PURE__ */ new Map();
|
|
9924
|
+
async function detachcdpforrun(runid, reason, userdetached = false) {
|
|
9925
|
+
const active = activecdpsessions.get(runid);
|
|
9926
|
+
if (!active) return;
|
|
9927
|
+
const at = Date.now();
|
|
9928
|
+
const decision = teardowncdpsession({ session: active.session, breakpoints: active.breakpoints, overrides: active.overrides, plan: active.teardown, userdetached, at });
|
|
9929
|
+
for (const id of decision.revertedbreakpoints) {
|
|
9930
|
+
const spec = active.breakpoints.find((candidate) => candidate.id === id);
|
|
9931
|
+
if (!spec) continue;
|
|
9932
|
+
const reverted = { ...spec, revertedat: at };
|
|
9933
|
+
await memory.addbreakpoint(reverted).catch(() => void 0);
|
|
9934
|
+
await audit("debugger", `Reverted the breakpoint ${id} of ${spec.url}:${spec.line} with ${spec.hits} hit${spec.hits === 1 ? "" : "s"} at ${reason}${spec.condition !== void 0 ? "; the condition stays reviewed in the record" : ""}.`, { stepid: spec.stepid }).catch(() => void 0);
|
|
9935
|
+
}
|
|
9936
|
+
for (const id of decision.revertedoverrides) {
|
|
9937
|
+
const spec = active.overrides.find((candidate) => candidate.id === id);
|
|
9938
|
+
if (!spec) continue;
|
|
9939
|
+
const reverted = { ...spec, revertedat: at };
|
|
9940
|
+
await memory.addscriptoverride(reverted).catch(() => void 0);
|
|
9941
|
+
await audit("debugger", `Reverted the script override ${id} of ${spec.urlpattern} with ${spec.hits} applied evaluation${spec.hits === 1 ? "" : "s"} at ${reason}; the fixture source never enters the audit trail.`, { stepid: spec.stepid }).catch(() => void 0);
|
|
9942
|
+
}
|
|
9943
|
+
for (const rule of active.rules) {
|
|
9944
|
+
if (rule.closedat !== void 0) continue;
|
|
9945
|
+
await memory.setcdpeventrule({ ...rule, closedat: at }).catch(() => void 0);
|
|
9946
|
+
}
|
|
9947
|
+
await memory.setcdpsession(decision.session).catch(() => void 0);
|
|
9948
|
+
await audit("debugger", `Session ${active.session.id} of ${active.session.origin} detached at ${reason} with the reviewed domains ${active.session.domains.join(", ")} enabled${decision.keepsalive ? "; the session record stays alive for review because the user detached the debugger" : ""}.`, { sessionid: active.session.id, planid: runid, stepid: active.session.stepid }).catch(() => void 0);
|
|
9949
|
+
if (!userdetached) {
|
|
9950
|
+
const revoked = await memory.revokedebuggergrants(active.session.origin, at).catch(() => 0);
|
|
9951
|
+
if (revoked > 0) await audit("debugger", `Revoked ${revoked} debugger consent record${revoked === 1 ? "" : "s"} of ${active.session.origin} at ${reason}; the next attach needs a new reviewed prompt.`, { planid: runid }).catch(() => void 0);
|
|
9952
|
+
}
|
|
9953
|
+
if (decision.paused) await audit("pause", `The devtools teardown at ${reason} paused the run for review; the resume policy is ${decision.resumepolicy}.`, { planid: runid }).catch(() => void 0);
|
|
9954
|
+
activecdpsessions.delete(runid);
|
|
9955
|
+
await refreshbadge().catch(() => void 0);
|
|
9956
|
+
}
|
|
9957
|
+
function cdpstateof(runid, session, teardown) {
|
|
9958
|
+
const existing = activecdpsessions.get(runid);
|
|
9959
|
+
if (existing) return existing;
|
|
9960
|
+
const created = { session, queue: [], rules: [], breakpoints: [], overrides: [], ...teardown !== void 0 ? { teardown } : {}, cancelled: false };
|
|
9961
|
+
activecdpsessions.set(runid, created);
|
|
9962
|
+
return created;
|
|
9963
|
+
}
|
|
9964
|
+
function pauseframes(entry) {
|
|
9965
|
+
if (!Array.isArray(entry)) return [];
|
|
9966
|
+
return entry.flatMap((frame) => {
|
|
9967
|
+
if (!frame || typeof frame !== "object") return [];
|
|
9968
|
+
const record2 = frame;
|
|
9969
|
+
if (typeof record2.url !== "string" || typeof record2.line !== "number") return [];
|
|
9970
|
+
return [{ url: record2.url, line: record2.line, ...typeof record2.column === "number" ? { column: record2.column } : {}, ...typeof record2.functionname === "string" ? { functionname: record2.functionname } : {} }];
|
|
9971
|
+
});
|
|
9972
|
+
}
|
|
9973
|
+
async function executecdpstep(step, session, plan, tabid2, origin) {
|
|
9974
|
+
const options = stepoptions2(step);
|
|
9975
|
+
const extra = { sessionid: session.id, planid: plan.id, stepid: step.id };
|
|
9976
|
+
const debuggatecheck = debuggate(session, tabid2, origin, Date.now());
|
|
9977
|
+
if (!debuggatecheck.allowed) throw new Error(debuggatecheck.reason ?? "The devtools protocol step stays outside the debug gate.");
|
|
9978
|
+
const grants = await memory.getdebuggergrants();
|
|
9979
|
+
const domains = Array.isArray(options.domains) ? options.domains.filter((domain) => typeof domain === "string") : [];
|
|
9980
|
+
if (step.kind === "attachcdp") {
|
|
9981
|
+
const consent = debuggerconsentcovers(origin, domains, grants);
|
|
9982
|
+
if (!consent.allowed) {
|
|
9983
|
+
const pending = grants.find((grant) => grant.origin === origin && grant.approved === void 0 && grant.domains.join(",") === domains.join(","));
|
|
9984
|
+
if (!pending) {
|
|
9985
|
+
const record2 = { id: randomid(), prompt: `Debugger attach on ${origin} with the reviewed domains ${domains.join(", ")} enabled for run ${plan.id}.`, origin, domains, consentedat: Date.now() };
|
|
9986
|
+
await memory.setdebuggergrant(record2);
|
|
9987
|
+
await refreshbadge();
|
|
9988
|
+
}
|
|
9989
|
+
throw new Error(`${consent.reason} The prompt is open in the review panel with the domain allowlist shown; approve it and run the step again.`);
|
|
9990
|
+
}
|
|
9991
|
+
} else {
|
|
9992
|
+
const consent = debuggerconsentcovers(origin, planallowlist(plan.steps)?.domains ?? [], grants);
|
|
9993
|
+
if (!consent.allowed) throw new Error(consent.reason ?? "The reviewed debugger consent is missing.");
|
|
9994
|
+
}
|
|
9995
|
+
const active = activecdpsessions.get(plan.id);
|
|
9996
|
+
if (step.kind !== "attachcdp" && (!active || active.session.detachedat !== void 0)) throw new Error("No attached devtools session covers this run; run the attachcdp step first.");
|
|
9997
|
+
if (step.kind === "attachcdp") {
|
|
9998
|
+
const teardown = teardownplanof(options.teardown);
|
|
9999
|
+
const sessionid = randomid();
|
|
10000
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The attach returned no result." };
|
|
10001
|
+
if (!output.ok) return output;
|
|
10002
|
+
const record2 = attachcdpsession({ id: sessionid, runid: plan.id, stepid: step.id, tabid: tabid2, origin, domains, now: Date.now(), debuggerversion: cdpderivation });
|
|
10003
|
+
cdpstateof(plan.id, record2, teardown);
|
|
10004
|
+
await memory.setcdpsession(record2);
|
|
10005
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "attach", domains: domains.length }, Date.now()));
|
|
10006
|
+
await audit("debugger", `Attached the devtools session ${sessionid} to the run tab ${tabid2} of ${origin} with the reviewed domains ${domains.join(", ")} enabled and the debugger version ${cdpderivation}; the teardown plan reverts ${teardown?.revertsteps.length ?? 0} step${(teardown?.revertsteps.length ?? 0) === 1 ? "" : "s"} with the ${teardown?.resumepolicy ?? "ask"} resume policy.`, extra);
|
|
10007
|
+
await refreshbadge();
|
|
10008
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, session: record2, cdp: { sessionid, state: "attached", commandids: [] } } };
|
|
10009
|
+
}
|
|
10010
|
+
if (step.kind === "detachcdp") {
|
|
10011
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10012
|
+
const before = { breakpoints: active.breakpoints.filter((spec) => spec.revertedat === void 0).length, overrides: active.overrides.filter((spec) => spec.revertedat === void 0).length };
|
|
10013
|
+
await detachcdpforrun(plan.id, "the reviewed detachcdp step");
|
|
10014
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "detach", hits: before.breakpoints + before.overrides }, Date.now()));
|
|
10015
|
+
return { ok: true, summary: `Detached the devtools session ${active.session.id} cleanly after reverting ${before.breakpoints} breakpoint${before.breakpoints === 1 ? "" : "s"} and ${before.overrides} override${before.overrides === 1 ? "" : "s"}; the detach is confirmed.`, details: { detached: true, ...before, cdp: { sessionid: active.session.id, state: "detached", commandids: active.queue.map((command) => command.id) } } };
|
|
10016
|
+
}
|
|
10017
|
+
if (step.kind === "cdpcmd") {
|
|
10018
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10019
|
+
const command = options.command && typeof options.command === "object" && !Array.isArray(options.command) ? options.command : {};
|
|
10020
|
+
const method = typeof command.method === "string" ? command.method : "";
|
|
10021
|
+
const planlist = planallowlist(plan.steps);
|
|
10022
|
+
const allowlist = { domains: active.session.domains, ...planlist?.methods !== void 0 ? { methods: planlist.methods } : {} };
|
|
10023
|
+
if (!allowlistcovers(allowlist, method)) throw new Error(`The raw command ${method} stays outside the enabled domain allowlist of the run attach.`);
|
|
10024
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The command returned no result." };
|
|
10025
|
+
const duration = typeof output.details?.duration === "number" ? output.details.duration : 0;
|
|
10026
|
+
const errorclass = typeof output.details?.errorclass === "string" ? output.details.errorclass : output.ok ? void 0 : "protocolerror";
|
|
10027
|
+
const record2 = sendcdpcommand({ id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, method, ...command.params && typeof command.params === "object" && !Array.isArray(command.params) ? { params: command.params } : {}, ...typeof command.resultpath === "string" ? { resultpath: command.resultpath } : {}, duration, ...errorclass !== void 0 ? { errorclass } : {}, at: Date.now() });
|
|
10028
|
+
active.queue = serializecdpcommand(active.queue, record2);
|
|
10029
|
+
await memory.addcdpcommand(record2);
|
|
10030
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "command", method, domain: record2.domain, duration, ...errorclass !== void 0 ? { errorclass } : {} }, Date.now()));
|
|
10031
|
+
const pausedetails = output.details?.paused;
|
|
10032
|
+
let pauseid;
|
|
10033
|
+
if (pausedetails && typeof pausedetails === "object") {
|
|
10034
|
+
pauseid = randomid();
|
|
10035
|
+
const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "breakpoint", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, at: Date.now() });
|
|
10036
|
+
await memory.addpause(pause);
|
|
10037
|
+
await audit("debugger", `The run paused on breakpoint ${pause.hitbreakpoint ?? "unknown"} with ${pause.callframes.length} call frame${pause.callframes.length === 1 ? "" : "s"} captured.`, extra);
|
|
10038
|
+
}
|
|
10039
|
+
await audit("debugger", `Sent the reviewed command ${method} of the ${record2.domain} domain to session ${active.session.id}; it returned in ${duration} millisecond${duration === 1 ? "" : "s"}${errorclass !== void 0 ? ` with the ${errorclass} error class` : ""}; the params never enter the audit trail.`, extra);
|
|
10040
|
+
await refreshbadge();
|
|
10041
|
+
if (!output.ok) return { ok: false, summary: output.summary, details: { ...output.details ?? {}, command: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
|
|
10042
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, command: record2, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: active.queue.map((item) => item.id) } } };
|
|
10043
|
+
}
|
|
10044
|
+
if (step.kind === "watchcdp") {
|
|
10045
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10046
|
+
const rules = [];
|
|
10047
|
+
for (const rule of Array.isArray(options.events) ? options.events : []) {
|
|
10048
|
+
const parsed = rule && typeof rule === "object" && !Array.isArray(rule) ? rule : void 0;
|
|
10049
|
+
const normalized = parsed !== void 0 ? await Promise.resolve(parsed) : void 0;
|
|
10050
|
+
const base = normalized !== void 0 ? { domain: String(normalized.domain ?? ""), event: String(normalized.event ?? ""), ...typeof normalized.match === "string" ? { match: normalized.match } : {} } : void 0;
|
|
10051
|
+
if (!base || !base.domain || !base.event) continue;
|
|
10052
|
+
const record2 = { id: randomid(), sessionid: active.session.id, runid: plan.id, stepid: step.id, domain: base.domain, event: base.event, ...base.match !== void 0 ? { match: base.match } : {}, events: 0, registeredat: Date.now() };
|
|
10053
|
+
rules.push(record2);
|
|
10054
|
+
active.rules.push(record2);
|
|
10055
|
+
await memory.setcdpeventrule(record2);
|
|
10056
|
+
}
|
|
10057
|
+
if (rules.length === 0) throw new Error("The event watch needs at least one reviewed domain event rule.");
|
|
10058
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The event watch returned no result." };
|
|
10059
|
+
const observed = detailarray(output.details, "events").flatMap((event) => {
|
|
10060
|
+
if (!event || typeof event !== "object") return [];
|
|
10061
|
+
const record2 = event;
|
|
10062
|
+
if (typeof record2.domain !== "string" || typeof record2.event !== "string") return [];
|
|
10063
|
+
return [{ domain: record2.domain, event: record2.event, ...typeof record2.payload === "string" ? { payload: record2.payload } : {} }];
|
|
10064
|
+
});
|
|
10065
|
+
const matched = watchcdpevents(rules, observed);
|
|
10066
|
+
for (const rule of active.rules) {
|
|
10067
|
+
const count = matched.matched.filter((item) => item.ruleid === rule.id).length;
|
|
10068
|
+
if (count > 0) rule.events += count;
|
|
10069
|
+
await memory.setcdpeventrule({ ...rule });
|
|
10070
|
+
}
|
|
10071
|
+
for (const item of matched.matched) {
|
|
10072
|
+
await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "info", source: "cdp", message: `${item.domain}.${item.event}${item.payload !== void 0 ? `: ${item.payload}` : ""}` });
|
|
10073
|
+
}
|
|
10074
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: matched.matched.length, ...rules[0] !== void 0 ? { domain: rules[0].domain } : {} }, Date.now()));
|
|
10075
|
+
await audit("debugger", `Watched ${rules.length} reviewed domain event rule${rules.length === 1 ? "" : "s"} of session ${active.session.id} for the reviewed window and forwarded ${matched.matched.length} matched event${matched.matched.length === 1 ? "" : "s"} into the run timeline; the subscription closes at run end or on step cancel.`, extra);
|
|
10076
|
+
await refreshbadge();
|
|
10077
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, eventcounts: matched.counts, matched: matched.matched, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10078
|
+
}
|
|
10079
|
+
if (step.kind === "setbreakpoint") {
|
|
10080
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10081
|
+
const input = breakpointinputof(options.breakpoint);
|
|
10082
|
+
if (!input) throw new Error("The breakpoint input is absent.");
|
|
10083
|
+
const settings = await memory.getsettings();
|
|
10084
|
+
const budgetcheck = breakpointbudgetallowed(active.breakpoints.filter((spec) => spec.revertedat === void 0).length, breakpointceilingof(settings));
|
|
10085
|
+
if (!budgetcheck.allowed) throw new Error(budgetcheck.reason ?? "The breakpoint ceiling refused the registration.");
|
|
10086
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The breakpoint registration returned no result." };
|
|
10087
|
+
if (!output.ok) return output;
|
|
10088
|
+
const registered = output.details?.breakpoint;
|
|
10089
|
+
const id = typeof registered?.id === "string" ? registered.id : randomid();
|
|
10090
|
+
const record2 = { id, runid: plan.id, stepid: step.id, url: input.url, line: input.line, ...input.column !== void 0 ? { column: input.column } : {}, ...input.condition !== void 0 ? { condition: input.condition } : {}, hits: 0, registeredat: Date.now() };
|
|
10091
|
+
active.breakpoints.push(record2);
|
|
10092
|
+
await memory.addbreakpoint(record2);
|
|
10093
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "breakpoint", hits: 0 }, Date.now()));
|
|
10094
|
+
await audit("debugger", `Registered the reviewed breakpoint ${id} at ${input.url}:${input.line}${input.condition !== void 0 ? ` under the reviewed condition` : ""} of session ${active.session.id}; the run pauses when an instrumented probe of the run hits it.`, extra);
|
|
10095
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, breakpoint: record2, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10096
|
+
}
|
|
10097
|
+
if (step.kind === "stepcode") {
|
|
10098
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10099
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The step returned no result." };
|
|
10100
|
+
const pausedetails = output.details?.pausestate;
|
|
10101
|
+
let pauseid;
|
|
10102
|
+
if (pausedetails && typeof pausedetails === "object") {
|
|
10103
|
+
pauseid = randomid();
|
|
10104
|
+
const domversion = await memory.nextobservationversion().catch(() => void 0);
|
|
10105
|
+
const pause = capturepause({ id: pauseid, runid: plan.id, stepid: step.id, reason: typeof pausedetails.reason === "string" ? pausedetails.reason : "step", callframes: pauseframes(pausedetails.frames), ...typeof pausedetails.hitbreakpoint === "string" ? { hitbreakpoint: pausedetails.hitbreakpoint } : {}, ...domversion !== void 0 ? { domsnapshotid: String(domversion) } : {}, at: Date.now() });
|
|
10106
|
+
await memory.addpause(pause);
|
|
10107
|
+
}
|
|
10108
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "step", frames: pauseframes(pausedetails?.frames).length }, Date.now()));
|
|
10109
|
+
await audit("debugger", `Stepped the paused probe of session ${active.session.id} with the ${stepmodeof(options.mode) ?? "reviewed"} mode and captured the pause state${pauseid !== void 0 ? ` as ${pauseid} with the dom snapshot through the page bridge` : ""}.`, extra);
|
|
10110
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, ...pauseid !== void 0 ? { pauseid } : {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10111
|
+
}
|
|
10112
|
+
if (step.kind === "watchexpr") {
|
|
10113
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10114
|
+
const input = watchexpressionof(options.expression);
|
|
10115
|
+
if (!input) throw new Error("The watch expression input is absent.");
|
|
10116
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The watch expression returned no result." };
|
|
10117
|
+
const stored = (await memory.getwatchexpressions()).find((item) => item.stepid === step.id);
|
|
10118
|
+
const base = stored ?? { id: randomid(), runid: plan.id, stepid: step.id, expression: input.expression, scope: input.scope, reviewed: true, values: [], at: Date.now() };
|
|
10119
|
+
if (output.ok) {
|
|
10120
|
+
const value = typeof output.details?.value === "string" ? output.details.value : "";
|
|
10121
|
+
const pauses = await memory.listpauses(plan.id);
|
|
10122
|
+
const pauseid = pauses[0]?.id ?? "nopause";
|
|
10123
|
+
const updated = recordwatchvalue(base, pauseid, value, Date.now());
|
|
10124
|
+
await memory.setwatchexpression(updated);
|
|
10125
|
+
await memory.addtimelineentry({ id: randomid(), runid: plan.id, stepid: step.id, time: Date.now(), level: "debug", source: "cdp", message: `Watch expression ${input.expression} evaluated to ${value} at pause ${pauseid} in the ${input.scope} scope.` });
|
|
10126
|
+
}
|
|
10127
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "watch", events: base.values.length }, Date.now()));
|
|
10128
|
+
await audit("debugger", `Evaluated the reviewed watch expression of step ${step.id} at the pause in the ${input.scope} scope; the value stores with its pause scope in the timeline and the expression text stays reviewed.`, extra);
|
|
10129
|
+
return { ok: Boolean(output.ok), summary: output.summary, details: { ...output.details ?? {}, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10130
|
+
}
|
|
10131
|
+
if (step.kind === "overridescript") {
|
|
10132
|
+
if (!active) throw new Error("No attached devtools session covers this run.");
|
|
10133
|
+
const override = options.override && typeof options.override === "object" && !Array.isArray(options.override) ? options.override : void 0;
|
|
10134
|
+
const urlpattern = typeof override?.urlpattern === "string" ? override.urlpattern : "";
|
|
10135
|
+
const source = typeof override?.source === "string" ? override.source : "";
|
|
10136
|
+
if (!urlpattern || !source) throw new Error("The script override input is absent.");
|
|
10137
|
+
const output = await dispatchpagestep(step, tabid2, origin, plan) ?? { ok: false, summary: "The override returned no result." };
|
|
10138
|
+
if (!output.ok) return output;
|
|
10139
|
+
const applied = output.details?.override;
|
|
10140
|
+
const id = typeof applied?.id === "string" ? applied.id : randomid();
|
|
10141
|
+
const record2 = { id, runid: plan.id, stepid: step.id, urlpattern, source, reviewed: true, reviewedat: Date.now(), hits: 0, appliedat: Date.now() };
|
|
10142
|
+
active.overrides.push(record2);
|
|
10143
|
+
await memory.addscriptoverride(record2);
|
|
10144
|
+
await memory.setprogress(recordcdp(await memory.getprogress(), plan.id, step.id, { family: "override", hits: 0 }, Date.now()));
|
|
10145
|
+
await audit("debugger", `Applied the reviewed script override ${id} of ${urlpattern} on new document evaluation through the page instrumentation; the fixture reverts at run end and the source never enters the audit trail.`, extra);
|
|
10146
|
+
return { ok: true, summary: output.summary, details: { ...output.details ?? {}, override: { id, urlpattern, appliedat: record2.appliedat, reviewed: true }, cdp: { sessionid: active.session.id, state: "attached", commandids: [] } } };
|
|
10147
|
+
}
|
|
10148
|
+
return { ok: false, summary: "The devtools step is not part of the instrumented family." };
|
|
10149
|
+
}
|
|
9463
10150
|
var activerules = /* @__PURE__ */ new Map();
|
|
9464
10151
|
var activeauthflows = /* @__PURE__ */ new Map();
|
|
9465
10152
|
function rulesetof(runid) {
|
|
@@ -9850,11 +10537,12 @@ async function refreshbadge() {
|
|
|
9850
10537
|
const recordingprompts = (await memory.getrecordingconsents()).filter((record2) => record2.approved === void 0).length;
|
|
9851
10538
|
const fetchprompts = (await memory.getfetchconsents()).filter((consent) => consent.approved === void 0).length;
|
|
9852
10539
|
const consoleprompts = (await memory.getconsoleconsents()).filter((consent) => consent.approved === void 0).length;
|
|
10540
|
+
const debuggerprompts = (await memory.getdebuggergrants()).filter((grant) => grant.approved === void 0).length;
|
|
9853
10541
|
const observedrequests = (await memory.getexchanges()).length;
|
|
9854
10542
|
const livechannels = (await memory.getchannels()).filter((channel) => channel.state === "open" || channel.state === "connecting").length;
|
|
9855
10543
|
const activerulescount = [...activerules.values()].reduce((total2, ruleset) => total2 + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0);
|
|
9856
10544
|
const tasktabs2 = new Set(badges.map((badge) => badge.tabid)).size;
|
|
9857
|
-
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + observedrequests + livechannels + activerulescount;
|
|
10545
|
+
const total = (queues?.prefetch ?? 0) + (queues?.batchopen ?? 0) + tasktabs2 + prompts + consents + quarantined + datasets + captures + media + recordingprompts + fetchprompts + consoleprompts + debuggerprompts + observedrequests + livechannels + activerulescount;
|
|
9858
10546
|
await chrome.action.setBadgeText({ text: total > 0 ? String(total) : "" }).catch(() => {
|
|
9859
10547
|
});
|
|
9860
10548
|
}
|
|
@@ -9925,6 +10613,9 @@ async function executestep(stepid) {
|
|
|
9925
10613
|
} else if (isdebugkind(step.kind)) {
|
|
9926
10614
|
if (!session || !plan || plan.state !== "approved") throw new Error("Debugging kinds refuse to run outside an approved session plan.");
|
|
9927
10615
|
output = await executetimelinestep(step, session, plan, tab.id, origin);
|
|
10616
|
+
} else if (iscdpkind(step.kind)) {
|
|
10617
|
+
if (!session || !plan || plan.state !== "approved") throw new Error("Devtools protocol kinds refuse to run outside an approved session plan.");
|
|
10618
|
+
output = await executecdpstep(step, session, plan, tab.id, origin);
|
|
9928
10619
|
} else {
|
|
9929
10620
|
if (step.target && freshcheckkinds.has(step.kind)) {
|
|
9930
10621
|
const fresh = await snapshot(tab.id);
|
|
@@ -9982,6 +10673,8 @@ async function executestep(stepid) {
|
|
|
9982
10673
|
});
|
|
9983
10674
|
await revertcontrolsforrun(plan.id, "plan completion").catch(() => {
|
|
9984
10675
|
});
|
|
10676
|
+
await detachcdpforrun(plan.id, "plan completion").catch(() => {
|
|
10677
|
+
});
|
|
9985
10678
|
const done = { ...plan, state: "completed", completedat: Date.now() };
|
|
9986
10679
|
await memory.setplan(done);
|
|
9987
10680
|
await audit("complete", "Every reviewed step of the approved plan has executed.", { ...session ? { sessionid: session.id } : {}, planid: done.id });
|
|
@@ -10158,7 +10851,7 @@ async function handlerequest(message, sender) {
|
|
|
10158
10851
|
const livetab = session ? await chrome.tabs.get(session.tabid).catch(() => void 0) : void 0;
|
|
10159
10852
|
const waitprofile = session ? waitprofiles.find((record2) => record2.origin === session.origin) : void 0;
|
|
10160
10853
|
const livestate = { phase: livetab?.status === "loading" ? "loading" : "complete", ...navrecords[0] ? { finalurl: navrecords[0].finalurl, redirects: navrecords[0].chain } : {} };
|
|
10161
|
-
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), socketsactive: activesockets.size, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
10854
|
+
return { config: await memory.getconfig(), session, plan, progress: plan && progress?.planid === plan.id ? progress : void 0, diagnostic: await memory.getdiagnostic(), audit: await memory.getaudit(), capabilities: await refreshcapabilities(), outcomes: await memory.getoutcomes(), holds: heldkeysreport({ tabid: session?.tabid ?? 0, holds }), dialogs: await memory.getdialogs(), retries: await memory.getretries(), ...signals ? { signals: signalsreport({ signals }) } : { signals: signalsreport({}) }, banners: await memory.getbanners(), mutationevents: await memory.getmutationevents(), focusevents: await memory.getfocusevents(), diffs: await memory.getdiffs(), selectors: await memory.getselectors(), ...a11y ? { a11y } : {}, ...reader ? { reader } : {}, ...map ? { map } : {}, trail: trailreport({ ...session ? { sessionid: session.id } : {}, trail }), navrecords, ratestates, safeties, curated, waitprofiles, auths, navcontrol, navqueues, artifacts, navstate: livestate, ...waitprofile ? { waitprofile } : {}, offline: !navigator.onLine, tabs, windows, layouts: layoutreport({ layouts }), tabgroups, tabmetas, badges, snapshots, closedtabs, tabwatchevents, clones, tasktabgauge: taskgauge, ...controltab ? { controltab } : {}, tabreport: report, profiles, tickets, wizards: wizardreport({ ...session ? { sessionid: session.id } : {}, wizards, picks }), picks, errorreports, captchas, detections, ...codeentry !== void 0 ? { codeentry: true } : {}, datasets, imports, extractsessions, streams, exports, provenances, taskrules, sheetendpoints: sheetgrants, downloads, netlogs, clipconsents, clips, quarantines, cleanuprules, cleanupruns, capturecounters, inventory, mimefilters, scanhooks, captures: capturemetadata, capturepairs, capturepolicy: runsettings?.capturepolicy ?? "manual", media: mediarecords, imagebatches, recordingconsents, recordingactive: [...activerecordings.values()].map((active) => ({ id: active.record.id, kind: active.record.kind, scope: active.record.scope, startedat: active.record.startedat, stopat: active.stopat })), recordingwindow: runsettings?.recordingwindow, calls, endpoints, fetchconsents, apikeys, callretention: runsettings?.callretention, fetchesactive: activefetches.size, exchanges, channels, subscriptions, apimap, messages: messagecount, webrequestgrant: runsettings?.webrequestgrant === true, bodyretention: runsettings?.bodyretention, timelineretention: runsettings?.timelineretention, timeline, consoleconsents: await memory.getconsoleconsents(), rotationtargets: await memory.getrotationtargets(), levelsummaries: await memory.getlevelsummaries(), cdpsessions: await memory.getcdpsessions(), cdpcommands: await memory.getcdpcommands(), cdpeventrules: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watchexpressions: await memory.getwatchexpressions(), scriptoverrides: await memory.getscriptoverrides(), debuggergrants: await memory.getdebuggergrants(), pauseretention: runsettings?.pauseretention, breakpointceiling: runsettings?.breakpointceiling, cdpattached: [...activecdpsessions.values()].filter((active) => active.session.detachedat === void 0).length, socketsactive: activesockets.size, traffic, tokens, authflows, activerules: [...activerules.values()].reduce((total, ruleset) => total + ruleset.blocks.filter((rule) => rule.revertedat === void 0).length + ruleset.mocks.filter((rule) => rule.revertedat === void 0).length + ruleset.rewrites.filter((rule) => rule.revertedat === void 0).length + (ruleset.proxy !== void 0 && ruleset.proxy.revertedat === void 0 ? 1 : 0), 0), ...stitchprogress.size > 0 ? { stitchprogress: [...stitchprogress.values()] } : {} };
|
|
10162
10855
|
}
|
|
10163
10856
|
case "capabilities":
|
|
10164
10857
|
return refreshcapabilities();
|
|
@@ -10932,6 +11625,68 @@ async function handlerequest(message, sender) {
|
|
|
10932
11625
|
await refreshbadge();
|
|
10933
11626
|
return { closed: true, id: inputclose.id ?? "" };
|
|
10934
11627
|
}
|
|
11628
|
+
case "setpauseretention": {
|
|
11629
|
+
const inputretention = message;
|
|
11630
|
+
const settings = await memory.getsettings();
|
|
11631
|
+
const retention = typeof inputretention.retention === "number" && Number.isInteger(inputretention.retention) && inputretention.retention >= 0 ? inputretention.retention : void 0;
|
|
11632
|
+
await memory.setsettings({ ...settings, ...retention !== void 0 ? { pauseretention: retention } : {} });
|
|
11633
|
+
await audit("configure", `The user set the pause capture retention to ${retention === void 0 ? "keep every capture" : `${retention} capture${retention === 1 ? "" : "s"}`}; the pause reason and hit breakpoint always survive.`);
|
|
11634
|
+
return { pauseretention: retention };
|
|
11635
|
+
}
|
|
11636
|
+
case "setbreakpointceiling": {
|
|
11637
|
+
const inputceiling = message;
|
|
11638
|
+
const settings = await memory.getsettings();
|
|
11639
|
+
const ceiling = typeof inputceiling.ceiling === "number" && Number.isInteger(inputceiling.ceiling) && inputceiling.ceiling >= 0 ? inputceiling.ceiling : void 0;
|
|
11640
|
+
await memory.setsettings({ ...settings, ...ceiling !== void 0 ? { breakpointceiling: ceiling } : {} });
|
|
11641
|
+
await audit("configure", `The user set the breakpoint ceiling to ${ceiling === void 0 ? "no ceiling" : `${ceiling} breakpoint${ceiling === 1 ? "" : "s"} per run`}; an absent value never refuses a breakpoint.`);
|
|
11642
|
+
return { breakpointceiling: ceiling };
|
|
11643
|
+
}
|
|
11644
|
+
case "approvedebuggerconsent": {
|
|
11645
|
+
const inputapprove = message;
|
|
11646
|
+
const records = await memory.getdebuggergrants();
|
|
11647
|
+
const record2 = records.find((item) => item.id === inputapprove.id);
|
|
11648
|
+
if (!record2) throw new Error("No debugger consent prompt matches the id.");
|
|
11649
|
+
const decided = { ...record2, approved: true, usedat: Date.now() };
|
|
11650
|
+
await memory.setdebuggergrant(decided);
|
|
11651
|
+
await audit("debugger", `Debugger consent on ${record2.origin} approved from the review panel with the domain allowlist ${record2.domains.join(", ")} shown; the decision covers those domains only.`, { stepid: record2.id });
|
|
11652
|
+
await refreshbadge();
|
|
11653
|
+
return { approved: true, origin: record2.origin, domains: record2.domains };
|
|
11654
|
+
}
|
|
11655
|
+
case "revokedebuggerconsent": {
|
|
11656
|
+
const session = await memory.getsession();
|
|
11657
|
+
const plan = await memory.getplan();
|
|
11658
|
+
const origin = session?.origin;
|
|
11659
|
+
if (!origin) throw new Error("No active session origin covers a debugger consent revoke.");
|
|
11660
|
+
const revoked = await memory.revokedebuggergrants(origin, Date.now());
|
|
11661
|
+
if (plan) await detachcdpforrun(plan.id, "the user detached the debugger", true);
|
|
11662
|
+
if (session) await memory.setsession({ ...session, pausedat: session.pausedat ?? Date.now() });
|
|
11663
|
+
await audit("debugger", `The user detached the debugger: ${revoked} consent record${revoked === 1 ? "" : "s"} of ${origin} revoked, every breakpoint and override reverted, the session record stays alive for review and the run paused for review before continuing.`, { ...session ? { sessionid: session.id } : {}, ...plan ? { planid: plan.id } : {} });
|
|
11664
|
+
await refreshbadge();
|
|
11665
|
+
return { revoked, paused: true };
|
|
11666
|
+
}
|
|
11667
|
+
case "revertcdpoverride": {
|
|
11668
|
+
const inputrevert = message;
|
|
11669
|
+
const plan = await memory.getplan();
|
|
11670
|
+
if (!plan) throw new Error("No plan is available for a script override revert.");
|
|
11671
|
+
const active = activecdpsessions.get(plan.id);
|
|
11672
|
+
const stored = (await memory.getscriptoverrides()).find((spec) => spec.id === inputrevert.id);
|
|
11673
|
+
if (!stored) throw new Error(`No script override matches ${inputrevert.id ?? ""}.`);
|
|
11674
|
+
if (stored.revertedat !== void 0) throw new Error("The script override already reverted.");
|
|
11675
|
+
const reverted = { ...stored, revertedat: Date.now() };
|
|
11676
|
+
if (active) {
|
|
11677
|
+
const index = active.overrides.findIndex((spec) => spec.id === stored.id);
|
|
11678
|
+
if (index >= 0) active.overrides[index] = reverted;
|
|
11679
|
+
}
|
|
11680
|
+
await memory.addscriptoverride(reverted);
|
|
11681
|
+
await audit("debugger", `Reverted the script override ${reverted.id} of ${reverted.urlpattern} from the review panel; later evaluations run the original source again and the fixture source never entered the audit trail.`, { planid: plan.id, stepid: reverted.stepid });
|
|
11682
|
+
await refreshbadge();
|
|
11683
|
+
return { reverted: true, id: reverted.id };
|
|
11684
|
+
}
|
|
11685
|
+
case "cdpreport": {
|
|
11686
|
+
const plan = await memory.getplan();
|
|
11687
|
+
if (!plan) throw new Error("No plan is available for a devtools protocol envelope.");
|
|
11688
|
+
return cdpreport({ sessions: await memory.getcdpsessions(), commands: await memory.getcdpcommands(), events: await memory.getcdpeventrules(), breakpoints: await memory.getbreakpoints(), pauses: await memory.getpauses(), watches: await memory.getwatchexpressions(), overrides: await memory.getscriptoverrides(), grants: await memory.getdebuggergrants() });
|
|
11689
|
+
}
|
|
10935
11690
|
case "stop": {
|
|
10936
11691
|
const session = await memory.getsession();
|
|
10937
11692
|
for (const [id, controller] of [...activefetches.entries()]) {
|
|
@@ -10953,12 +11708,19 @@ async function handlerequest(message, sender) {
|
|
|
10953
11708
|
});
|
|
10954
11709
|
await revertcontrolsforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
10955
11710
|
});
|
|
11711
|
+
await detachcdpforrun(stoppedplan.id, "run cancel").catch(() => {
|
|
11712
|
+
});
|
|
10956
11713
|
} else {
|
|
10957
11714
|
await closechannelsforrun("none").catch(() => {
|
|
10958
11715
|
});
|
|
10959
11716
|
await revertcontrolsforrun("none", "run cancel").catch(() => {
|
|
10960
11717
|
});
|
|
10961
11718
|
}
|
|
11719
|
+
for (const [runid, active] of [...activecdpsessions.entries()]) {
|
|
11720
|
+
active.cancelled = true;
|
|
11721
|
+
await detachcdpforrun(runid, "run cancel").catch(() => {
|
|
11722
|
+
});
|
|
11723
|
+
}
|
|
10962
11724
|
for (const [id, active] of [...activerecordings.entries()]) {
|
|
10963
11725
|
const finished = finishrecording(active.record, Date.now());
|
|
10964
11726
|
await memory.addmedia(finished).catch(() => {
|