@wenathlan/extension 1.1.34 → 1.1.36
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 +7 -5
- package/dist/index.js +533 -5
- package/dist/index.js.map +2 -2
- package/dist/memory.d.ts +91 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/policy.d.ts +20 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/protocol.d.ts +37 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/types.d.ts +271 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/extension/dist/background.js +2040 -17
- package/extension/dist/background.js.map +4 -4
- package/extension/dist/manifest.json +1 -1
- package/extension/dist/pagebridge.js +331 -12
- package/extension/dist/pagebridge.js.map +4 -4
- package/extension/dist/popup.html +1 -1
- package/extension/dist/popup.js +33 -2
- package/extension/dist/popup.js.map +2 -2
- package/extension/dist/sidepanel.html +1 -1
- package/extension/dist/sidepanel.js +396 -8
- package/extension/dist/sidepanel.js.map +4 -4
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -238,19 +238,219 @@ var sessionmemory = class {
|
|
|
238
238
|
async setsignals(signals) {
|
|
239
239
|
return this.adapter.set("signals", signals);
|
|
240
240
|
}
|
|
241
|
+
/** Appends one navigation trail entry of a session with its url, title, step ref and timestamp. */
|
|
242
|
+
async addtrailentry(sessionid, entry) {
|
|
243
|
+
const records = await this.gettrail(sessionid);
|
|
244
|
+
await this.adapter.set(`trail${sessionid}`, [...records, entry]);
|
|
245
|
+
}
|
|
246
|
+
/** Returns the navigation trail of a session, oldest first. */
|
|
247
|
+
async gettrail(sessionid) {
|
|
248
|
+
return await this.adapter.get(`trail${sessionid}`) ?? [];
|
|
249
|
+
}
|
|
250
|
+
/** Stores one wait profile for an origin with user configured values, replacing the previous profile of that origin. */
|
|
251
|
+
async setwaitprofile(record2) {
|
|
252
|
+
const records = (await this.getwaitprofiles()).filter((item) => item.origin !== record2.origin);
|
|
253
|
+
await this.adapter.set("waitprofiles", [...records, record2]);
|
|
254
|
+
}
|
|
255
|
+
/** Returns every stored wait profile with its origin and user configured values, newest first. */
|
|
256
|
+
async getwaitprofiles() {
|
|
257
|
+
return await this.adapter.get("waitprofiles") ?? [];
|
|
258
|
+
}
|
|
259
|
+
/** Records one navigation step with its redirect chain and final url. */
|
|
260
|
+
async addnavrecord(record2) {
|
|
261
|
+
const records = await this.getnavrecords();
|
|
262
|
+
await this.adapter.set("navrecords", [record2, ...records]);
|
|
263
|
+
}
|
|
264
|
+
/** Returns every stored navigation record with redirect chains and final urls, newest first. */
|
|
265
|
+
async getnavrecords() {
|
|
266
|
+
return await this.adapter.get("navrecords") ?? [];
|
|
267
|
+
}
|
|
268
|
+
/** Records one navigation intent detected from a plan for audit review. */
|
|
269
|
+
async addnavintent(record2) {
|
|
270
|
+
const records = await this.getnavintents();
|
|
271
|
+
await this.adapter.set("navintents", [record2, ...records]);
|
|
272
|
+
}
|
|
273
|
+
/** Returns every stored navigation intent record, newest first. */
|
|
274
|
+
async getnavintents() {
|
|
275
|
+
return await this.adapter.get("navintents") ?? [];
|
|
276
|
+
}
|
|
277
|
+
/** Replaces the rate limit window state of one domain. */
|
|
278
|
+
async setratestate(state) {
|
|
279
|
+
const records = (await this.getratestates()).filter((item) => item.domain !== state.domain);
|
|
280
|
+
await this.adapter.set("ratestates", [...records, state]);
|
|
281
|
+
}
|
|
282
|
+
/** Returns every rate limit window state per domain. */
|
|
283
|
+
async getratestates() {
|
|
284
|
+
return await this.adapter.get("ratestates") ?? [];
|
|
285
|
+
}
|
|
286
|
+
/** Records one curated link list with its review state before batch opening. */
|
|
287
|
+
async addcurated(list) {
|
|
288
|
+
const records = await this.getcurateds();
|
|
289
|
+
await this.adapter.set("curated", [list, ...records]);
|
|
290
|
+
}
|
|
291
|
+
/** Returns every stored curated link list, newest first. */
|
|
292
|
+
async getcurateds() {
|
|
293
|
+
return await this.adapter.get("curated") ?? [];
|
|
294
|
+
}
|
|
295
|
+
/** Stores reviewed basic auth credentials for one origin, replacing the previous record of that origin. */
|
|
296
|
+
async setauth(record2) {
|
|
297
|
+
const records = (await this.getauths()).filter((item) => item.origin !== record2.origin);
|
|
298
|
+
await this.adapter.set("auths", [...records, record2]);
|
|
299
|
+
}
|
|
300
|
+
/** Returns every stored reviewed basic auth record per origin. */
|
|
301
|
+
async getauths() {
|
|
302
|
+
return await this.adapter.get("auths") ?? [];
|
|
303
|
+
}
|
|
304
|
+
/** Records one task artifact routed into the artifact store. */
|
|
305
|
+
async addartifact(record2) {
|
|
306
|
+
const records = await this.getartifacts();
|
|
307
|
+
await this.adapter.set("artifacts", [record2, ...records]);
|
|
308
|
+
}
|
|
309
|
+
/** Returns every stored task artifact, newest first. */
|
|
310
|
+
async getartifacts() {
|
|
311
|
+
return await this.adapter.get("artifacts") ?? [];
|
|
312
|
+
}
|
|
313
|
+
/** Returns the navigation control state of paused navigation. */
|
|
314
|
+
async getnavcontrol() {
|
|
315
|
+
return this.adapter.get("navcontrol");
|
|
316
|
+
}
|
|
317
|
+
/** Replaces the navigation control state after a pause or resume transition. */
|
|
318
|
+
async setnavcontrol(control) {
|
|
319
|
+
return this.adapter.set("navcontrol", control);
|
|
320
|
+
}
|
|
321
|
+
/** Records one url safety verdict produced by a checksafe verification. */
|
|
322
|
+
async addsafety(verdict) {
|
|
323
|
+
const records = await this.getsafeties();
|
|
324
|
+
await this.adapter.set("safeties", [verdict, ...records]);
|
|
325
|
+
}
|
|
326
|
+
/** Returns every stored url safety verdict, newest first. */
|
|
327
|
+
async getsafeties() {
|
|
328
|
+
return await this.adapter.get("safeties") ?? [];
|
|
329
|
+
}
|
|
330
|
+
/** Records one recently closed tab so a reopentab step can restore it. */
|
|
331
|
+
async addrecenttab(tab) {
|
|
332
|
+
const records = await this.getrecenttabs();
|
|
333
|
+
await this.adapter.set("recenttabs", [tab, ...records]);
|
|
334
|
+
}
|
|
335
|
+
/** Returns every recently closed tab, newest first. */
|
|
336
|
+
async getrecenttabs() {
|
|
337
|
+
return await this.adapter.get("recenttabs") ?? [];
|
|
338
|
+
}
|
|
339
|
+
/** Returns the queued prefetch and batch open target counts shown in the popup badge. */
|
|
340
|
+
async getnavqueues() {
|
|
341
|
+
return this.adapter.get("navqueues");
|
|
342
|
+
}
|
|
343
|
+
/** Replaces the queued prefetch and batch open target counts. */
|
|
344
|
+
async setnavqueues(queues) {
|
|
345
|
+
return this.adapter.set("navqueues", queues);
|
|
346
|
+
}
|
|
347
|
+
/** Returns the last known navigation state of a tab, kept across service worker restarts. */
|
|
348
|
+
async getnavstate(tabid) {
|
|
349
|
+
return this.adapter.get(`navstate${tabid}`);
|
|
350
|
+
}
|
|
351
|
+
/** Replaces the last known navigation state of a tab. */
|
|
352
|
+
async setnavstate(tabid, state) {
|
|
353
|
+
return this.adapter.set(`navstate${tabid}`, state);
|
|
354
|
+
}
|
|
355
|
+
/** Stores one named tab layout with its window bounds and group states, replacing the previous layout of that name. */
|
|
356
|
+
async setlayout(layout) {
|
|
357
|
+
const records = (await this.getlayouts()).filter((item) => item.name !== layout.name);
|
|
358
|
+
await this.adapter.set("layouts", [layout, ...records]);
|
|
359
|
+
}
|
|
360
|
+
/** Returns one saved tab layout by name with its timestamp. */
|
|
361
|
+
async getlayout(name) {
|
|
362
|
+
return (await this.getlayouts()).find((item) => item.name === name);
|
|
363
|
+
}
|
|
364
|
+
/** Returns every saved tab layout with its window bounds and group states. */
|
|
365
|
+
async getlayouts() {
|
|
366
|
+
return await this.adapter.get("layouts") ?? [];
|
|
367
|
+
}
|
|
368
|
+
/** Stores one tab group definition with its color choice and member tabs, replacing the previous definition of that name. */
|
|
369
|
+
async settabgroup(group) {
|
|
370
|
+
const records = (await this.gettabgroups()).filter((item) => item.name !== group.name);
|
|
371
|
+
await this.adapter.set("tabgroups", [...records, group]);
|
|
372
|
+
}
|
|
373
|
+
/** Returns every stored tab group definition with its color choice, newest first. */
|
|
374
|
+
async gettabgroups() {
|
|
375
|
+
return await this.adapter.get("tabgroups") ?? [];
|
|
376
|
+
}
|
|
377
|
+
/** Records one tabmeta record with task provenance, replacing the previous metadata of that tab. */
|
|
378
|
+
async settabmeta(meta) {
|
|
379
|
+
const records = (await this.gettabmetas()).filter((item) => item.tabid !== meta.tabid);
|
|
380
|
+
await this.adapter.set("tabmetas", [...records, meta]);
|
|
381
|
+
}
|
|
382
|
+
/** Returns every stored tabmeta record with task provenance. */
|
|
383
|
+
async gettabmetas() {
|
|
384
|
+
return await this.adapter.get("tabmetas") ?? [];
|
|
385
|
+
}
|
|
386
|
+
/** Records one session snapshot of tabs and windows for later restore. */
|
|
387
|
+
async addsnapshot(snapshot) {
|
|
388
|
+
const records = await this.getsnapshots();
|
|
389
|
+
await this.adapter.set("snapshots", [snapshot, ...records]);
|
|
390
|
+
}
|
|
391
|
+
/** Returns every stored session snapshot, newest first. */
|
|
392
|
+
async getsnapshots() {
|
|
393
|
+
return await this.adapter.get("snapshots") ?? [];
|
|
394
|
+
}
|
|
395
|
+
/** Records one closed tab in the history kept for restoretab and reopenrun. */
|
|
396
|
+
async addclosedtab(tab) {
|
|
397
|
+
const records = await this.getclosedtabs();
|
|
398
|
+
await this.adapter.set("closedtabs", [tab, ...records]);
|
|
399
|
+
}
|
|
400
|
+
/** Returns the closed tab history, newest first. */
|
|
401
|
+
async getclosedtabs() {
|
|
402
|
+
return await this.adapter.get("closedtabs") ?? [];
|
|
403
|
+
}
|
|
404
|
+
/** Stores one badge state per task, replacing the previous badge of that task. */
|
|
405
|
+
async setbadge(badge) {
|
|
406
|
+
const records = (await this.getbadges()).filter((item) => item.taskid !== badge.taskid);
|
|
407
|
+
await this.adapter.set("badges", [...records, badge]);
|
|
408
|
+
}
|
|
409
|
+
/** Returns every stored badge state per task. */
|
|
410
|
+
async getbadges() {
|
|
411
|
+
return await this.adapter.get("badges") ?? [];
|
|
412
|
+
}
|
|
413
|
+
/** Records one tab event observed inside a reviewed watchtab registration. */
|
|
414
|
+
async addtabwatchevent(event) {
|
|
415
|
+
const records = await this.gettabwatchevents();
|
|
416
|
+
await this.adapter.set("tabwatchevents", [event, ...records]);
|
|
417
|
+
}
|
|
418
|
+
/** Returns the tab event stream of every reviewed watchtab registration, newest first. */
|
|
419
|
+
async gettabwatchevents() {
|
|
420
|
+
return await this.adapter.get("tabwatchevents") ?? [];
|
|
421
|
+
}
|
|
422
|
+
/** Returns the ids of the scratch windows opened for split work. */
|
|
423
|
+
async getscratchwindows() {
|
|
424
|
+
return await this.adapter.get("scratchwindows") ?? [];
|
|
425
|
+
}
|
|
426
|
+
/** Replaces the scratch window id list after one scratch window opens or closes. */
|
|
427
|
+
async setscratchwindows(ids) {
|
|
428
|
+
return this.adapter.set("scratchwindows", ids);
|
|
429
|
+
}
|
|
430
|
+
/** Returns the pinned control tab state with the live task feed. */
|
|
431
|
+
async getcontroltab() {
|
|
432
|
+
return this.adapter.get("controltab");
|
|
433
|
+
}
|
|
434
|
+
/** Replaces the pinned control tab state. */
|
|
435
|
+
async setcontroltab(state) {
|
|
436
|
+
return this.adapter.set("controltab", state);
|
|
437
|
+
}
|
|
241
438
|
};
|
|
242
439
|
function randomid() {
|
|
243
440
|
return crypto.randomUUID();
|
|
244
441
|
}
|
|
245
442
|
|
|
246
443
|
// policy.ts
|
|
247
|
-
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"]);
|
|
444
|
+
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"]);
|
|
248
445
|
var interactionactions = /* @__PURE__ */ new Set(["focus", "scroll", "hover", "clickdeep", "rightclick", "doubleclick", "scrollpage", "scrollby", "scrollend", "scrolltop", "fullscreen", "zoomset", "movepointer", "clicktext", "clickaria", "clickname", "expanddetails", "pierceshadow", "retryaction"]);
|
|
249
|
-
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"]);
|
|
446
|
+
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"]);
|
|
250
447
|
var allowedactions = /* @__PURE__ */ new Set([...sensitiveactions, ...interactionactions, ...readactions]);
|
|
251
|
-
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus"]);
|
|
448
|
+
var watchactions = /* @__PURE__ */ new Set(["watchmutate", "watchbanner", "watchfocus", "watchtab"]);
|
|
252
449
|
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"]);
|
|
253
|
-
var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor"]);
|
|
450
|
+
var valueactions = /* @__PURE__ */ new Set(["presskey", "drag", "drop", "upload", "readattribute", "removeattribute", "waittext", "evaluate", "zoomset", "tabactivate", "tabclose", "tabreload", "windowclose", "windowresize", "tabcreate", "windowcreate", "downloadfile", "typetime", "appendtext", "setvalue", "typeedit", "keyhold", "keyrelease", "chooseradio", "setslider", "setdate", "setcolor", "followlink", "setfragment", "handleauth", "navintent", "openclipboard", "checksafe", "reopentab", "spanav", "duplicatetab", "pintab", "mutetab", "movetab", "movetabwindow", "searchtabs", "badgetab", "attachmeta", "focuswindow", "maximizewindow", "minimizewindow", "restorewindow", "incognitowindow"]);
|
|
451
|
+
var tabscommandactions = /* @__PURE__ */ new Set(["querytabs", "duplicatetab", "closepattern", "pintab", "mutetab", "movetab", "movetabwindow", "grouptabs", "colorgroup", "collapsegroup", "discardtab", "reloadtabs", "zoomin", "zoomout", "watchtab", "switchtab", "maximizewindow", "minimizewindow", "restorewindow", "focuswindow", "scratchwindow", "incognitowindow", "restoretab", "savelayout", "restorelayout", "findclones", "searchtabs", "badgetab", "attachmeta", "listaudio", "reopenrun", "snapshotsession"]);
|
|
452
|
+
var layoutmutationactions = /* @__PURE__ */ new Set(["grouptabs", "colorgroup", "collapsegroup", "savelayout", "restorelayout"]);
|
|
453
|
+
var groupcolors = ["grey", "blue", "red", "yellow", "green", "pink", "purple", "cyan", "orange"];
|
|
254
454
|
function normalizeendpoint(value) {
|
|
255
455
|
const endpoint = new URL(value.trim());
|
|
256
456
|
if (endpoint.protocol !== "https:") throw new Error("Devthink accepts HTTPS endpoints only.");
|
|
@@ -286,6 +486,16 @@ function parseoptions(step) {
|
|
|
286
486
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("Step options must be a JSON object.");
|
|
287
487
|
return parsed;
|
|
288
488
|
}
|
|
489
|
+
function istabscommandkind(kind) {
|
|
490
|
+
return tabscommandactions.has(kind);
|
|
491
|
+
}
|
|
492
|
+
function islayoutkind(kind) {
|
|
493
|
+
return layoutmutationactions.has(kind);
|
|
494
|
+
}
|
|
495
|
+
function layoutmutationgranted(session, now) {
|
|
496
|
+
if (!session || session.stoppedat || session.expiresat <= now) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
|
|
497
|
+
return { allowed: true };
|
|
498
|
+
}
|
|
289
499
|
function waitduration(step) {
|
|
290
500
|
const requested = step.value ? Number.parseInt(step.value, 10) : 250;
|
|
291
501
|
if (!Number.isFinite(requested) || requested < 0) throw new Error("Wait duration must be zero or a positive number of milliseconds.");
|
|
@@ -338,6 +548,35 @@ function origingranted(session, origin) {
|
|
|
338
548
|
const grants = session.grants ?? [session.origin];
|
|
339
549
|
return grants.includes(origin);
|
|
340
550
|
}
|
|
551
|
+
function originverified(url, grants, verdicts) {
|
|
552
|
+
let origin = "";
|
|
553
|
+
try {
|
|
554
|
+
origin = new URL(url).origin;
|
|
555
|
+
} catch {
|
|
556
|
+
return { allowed: false, reason: "The reviewed navigation URL is invalid." };
|
|
557
|
+
}
|
|
558
|
+
if (grants.includes(origin)) return { allowed: true };
|
|
559
|
+
const covered = verdicts.find((verdict) => verdict.safe && (verdict.url === url || safeorigin(verdict.url) === origin));
|
|
560
|
+
if (covered) return { allowed: true };
|
|
561
|
+
return { allowed: false, reason: `The origin ${origin} is outside the session grants and has no safe checksafe verdict; run checksafe and review it first.` };
|
|
562
|
+
}
|
|
563
|
+
function safeorigin(url) {
|
|
564
|
+
try {
|
|
565
|
+
return new URL(url).origin;
|
|
566
|
+
} catch {
|
|
567
|
+
return "";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function navigationgranted(session, url) {
|
|
571
|
+
let origin = "";
|
|
572
|
+
try {
|
|
573
|
+
origin = new URL(url).origin;
|
|
574
|
+
} catch {
|
|
575
|
+
return { allowed: false, reason: "The reviewed navigation URL is invalid." };
|
|
576
|
+
}
|
|
577
|
+
if (origingranted(session, origin)) return { allowed: true };
|
|
578
|
+
return { allowed: false, reason: `Navigation to ${origin} leaves the task tab origins and needs the user consent of a session grant first.` };
|
|
579
|
+
}
|
|
341
580
|
function validateinnerstep(options, origin) {
|
|
342
581
|
const stepid = options.stepid;
|
|
343
582
|
const kind = options.kind;
|
|
@@ -361,6 +600,166 @@ function validateinnerstep(options, origin) {
|
|
|
361
600
|
};
|
|
362
601
|
return validatestep(inner, origin);
|
|
363
602
|
}
|
|
603
|
+
function ishttpsurl(value) {
|
|
604
|
+
if (typeof value !== "string" || !value.trim()) return false;
|
|
605
|
+
try {
|
|
606
|
+
return new URL(value).protocol === "https:";
|
|
607
|
+
} catch {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
function validatenavtarget(value, kind) {
|
|
612
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed navtarget with a url is required in options." };
|
|
613
|
+
const target = value;
|
|
614
|
+
if (!ishttpsurl(target.url)) return { allowed: false, reason: "The reviewed navtarget url must use HTTPS." };
|
|
615
|
+
const container = target.container ?? "tab";
|
|
616
|
+
if (container !== "current" && container !== "tab" && container !== "window" && container !== "private") return { allowed: false, reason: "The reviewed navtarget container must be current, tab, window or private." };
|
|
617
|
+
if (target.position !== void 0 && target.position !== "adjacent" && target.position !== "end") return { allowed: false, reason: "The reviewed navtarget position must be adjacent or end." };
|
|
618
|
+
if (kind === "openprivate" && container !== "private") return { allowed: false, reason: "The openprivate step requires the private container." };
|
|
619
|
+
if (kind === "openlink" && container === "private") return { allowed: false, reason: "The openlink step cannot open the private container; use openprivate." };
|
|
620
|
+
return { allowed: true };
|
|
621
|
+
}
|
|
622
|
+
function validatewaitprofile(value) {
|
|
623
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed waitprofile with load signals is required in options." };
|
|
624
|
+
const profile = value;
|
|
625
|
+
if (!Array.isArray(profile.signals) || profile.signals.length === 0 || !profile.signals.every((signal) => isnonempty(signal))) return { allowed: false, reason: "The reviewed waitprofile needs a non-empty list of load signals." };
|
|
626
|
+
if (!nonnegativeoption(profile, "idle")) return { allowed: false, reason: "The reviewed waitprofile idle threshold must be zero or a positive number of milliseconds." };
|
|
627
|
+
if (!nonnegativeoption(profile, "timeout")) return { allowed: false, reason: "The reviewed waitprofile timeout must be zero or a positive number of milliseconds." };
|
|
628
|
+
if (profile.overrides !== void 0) {
|
|
629
|
+
if (!Array.isArray(profile.overrides) || profile.overrides.length === 0) return { allowed: false, reason: "The reviewed waitprofile overrides must be a non-empty list when present." };
|
|
630
|
+
for (const entry of profile.overrides) {
|
|
631
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return { allowed: false, reason: "Every reviewed waitprofile override must be an object with an origin." };
|
|
632
|
+
const override = entry;
|
|
633
|
+
if (!ishttpsurl(override.origin)) return { allowed: false, reason: "Every reviewed waitprofile override origin must use HTTPS." };
|
|
634
|
+
if (override.signals !== void 0 && (!Array.isArray(override.signals) || !override.signals.every((signal) => isnonempty(signal)))) return { allowed: false, reason: "The reviewed waitprofile override signals must be a list of non-empty strings." };
|
|
635
|
+
if (!nonnegativeoption(override, "idle") || !nonnegativeoption(override, "timeout")) return { allowed: false, reason: "The reviewed waitprofile override thresholds must be zero or positive numbers." };
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return { allowed: true };
|
|
639
|
+
}
|
|
640
|
+
function validateurlpattern(value) {
|
|
641
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed urlpattern is required in options." };
|
|
642
|
+
const pattern = value;
|
|
643
|
+
if (pattern.mode !== "exact" && pattern.mode !== "prefix" && pattern.mode !== "host" && pattern.mode !== "pattern") return { allowed: false, reason: "The reviewed urlpattern mode must be exact, prefix, host or pattern." };
|
|
644
|
+
if (!ishttpsurl(pattern.url)) return { allowed: false, reason: "The reviewed urlpattern url must use HTTPS." };
|
|
645
|
+
if (pattern.query !== void 0) {
|
|
646
|
+
if (!pattern.query || typeof pattern.query !== "object" || Array.isArray(pattern.query)) return { allowed: false, reason: "The reviewed urlpattern query part must be an object of parameter names and values." };
|
|
647
|
+
for (const item of Object.values(pattern.query)) if (typeof item !== "string") return { allowed: false, reason: "The reviewed urlpattern query values must be strings." };
|
|
648
|
+
}
|
|
649
|
+
if (pattern.fragment !== void 0 && !isnonempty(pattern.fragment)) return { allowed: false, reason: "The reviewed urlpattern fragment must be a non-empty string." };
|
|
650
|
+
return { allowed: true };
|
|
651
|
+
}
|
|
652
|
+
function validateurllist(options, key) {
|
|
653
|
+
const urls = options[key];
|
|
654
|
+
if (!Array.isArray(urls) || urls.length === 0 || !urls.every((url) => ishttpsurl(url))) return { allowed: false, reason: `A reviewed non-empty list of HTTPS urls is required in options as ${key}.` };
|
|
655
|
+
return { allowed: true };
|
|
656
|
+
}
|
|
657
|
+
function validateratelimit(value) {
|
|
658
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed ratelimit with a window and a ceiling is required in options." };
|
|
659
|
+
const limit = value;
|
|
660
|
+
if (limit.domain !== void 0 && !isnonempty(limit.domain)) return { allowed: false, reason: "The reviewed ratelimit domain must be a non-empty string." };
|
|
661
|
+
if (typeof limit.window !== "number" || !Number.isFinite(limit.window) || limit.window <= 0) return { allowed: false, reason: "The reviewed ratelimit window must be a positive number of milliseconds with no code ceiling." };
|
|
662
|
+
if (typeof limit.ceiling !== "number" || !Number.isInteger(limit.ceiling) || limit.ceiling < 1) return { allowed: false, reason: "The reviewed ratelimit ceiling must be a positive integer with no code ceiling." };
|
|
663
|
+
return { allowed: true };
|
|
664
|
+
}
|
|
665
|
+
function validatetabquery(value) {
|
|
666
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return { allowed: false, reason: "A reviewed tabquery with at least one matcher is required in options." };
|
|
667
|
+
const query = value;
|
|
668
|
+
const hasmatcher = query.url !== void 0 || query.title !== void 0 || query.id !== void 0 || query.pattern !== void 0;
|
|
669
|
+
if (!hasmatcher) return { allowed: false, reason: "The reviewed tabquery needs a url, title, id or pattern matcher." };
|
|
670
|
+
if (query.url !== void 0 && !isnonempty(query.url)) return { allowed: false, reason: "The reviewed tabquery url matcher must be a non-empty string." };
|
|
671
|
+
if (query.title !== void 0 && !isnonempty(query.title)) return { allowed: false, reason: "The reviewed tabquery title matcher must be a non-empty string." };
|
|
672
|
+
if (query.pattern !== void 0 && !isnonempty(query.pattern)) return { allowed: false, reason: "The reviewed tabquery pattern matcher must be a non-empty string." };
|
|
673
|
+
if (query.id !== void 0 && (typeof query.id !== "number" || !Number.isInteger(query.id) || query.id < 0)) return { allowed: false, reason: "The reviewed tabquery id matcher must be a non-negative integer tab id." };
|
|
674
|
+
return { allowed: true };
|
|
675
|
+
}
|
|
676
|
+
function validategroupcolor(value) {
|
|
677
|
+
return typeof value === "string" && groupcolors.includes(value);
|
|
678
|
+
}
|
|
679
|
+
function validateidlist(options, key) {
|
|
680
|
+
const ids = options[key];
|
|
681
|
+
return Array.isArray(ids) && ids.length > 0 && ids.every((id) => typeof id === "number" && Number.isInteger(id) && id >= 0);
|
|
682
|
+
}
|
|
683
|
+
function validatetabsgrammar(step, options) {
|
|
684
|
+
const kind = step.kind;
|
|
685
|
+
if (kind === "querytabs" || kind === "closepattern") {
|
|
686
|
+
const querycheck = validatetabquery(options.tabquery);
|
|
687
|
+
if (!querycheck.allowed) return querycheck;
|
|
688
|
+
if (kind === "closepattern" && options.reviewed !== true) return { allowed: false, reason: "The close pattern needs the explicit reviewed flag before any tab closes." };
|
|
689
|
+
}
|
|
690
|
+
if (kind === "duplicatetab" || kind === "pintab" || kind === "mutetab" || kind === "movetab" || kind === "movetabwindow" || kind === "badgetab" || kind === "attachmeta") {
|
|
691
|
+
if (!isnumericid(step.value)) return { allowed: false, reason: "A numeric browser tab id is required." };
|
|
692
|
+
}
|
|
693
|
+
if (kind === "focuswindow" || kind === "maximizewindow" || kind === "minimizewindow" || kind === "restorewindow") {
|
|
694
|
+
if (!isnumericid(step.value)) return { allowed: false, reason: "A numeric browser window id is required." };
|
|
695
|
+
}
|
|
696
|
+
if (kind === "pintab" && typeof options.pinned !== "boolean") return { allowed: false, reason: "A reviewed pinned flag is required in options." };
|
|
697
|
+
if (kind === "mutetab" && typeof options.muted !== "boolean") return { allowed: false, reason: "A reviewed muted flag is required in options." };
|
|
698
|
+
if (kind === "movetab") {
|
|
699
|
+
if (typeof options.index !== "number" || !Number.isInteger(options.index) || options.index < 0) return { allowed: false, reason: "A reviewed non-negative target index is required in options." };
|
|
700
|
+
}
|
|
701
|
+
if (kind === "movetabwindow") {
|
|
702
|
+
if (typeof options.windowid !== "number" || !Number.isInteger(options.windowid) || options.windowid < 0) return { allowed: false, reason: "A reviewed target window id is required in options." };
|
|
703
|
+
}
|
|
704
|
+
if (kind === "grouptabs") {
|
|
705
|
+
const group = options.group;
|
|
706
|
+
if (!group || typeof group !== "object" || Array.isArray(group)) return { allowed: false, reason: "A reviewed group with a name is required in options." };
|
|
707
|
+
const spec = group;
|
|
708
|
+
if (!isnonempty(spec.name)) return { allowed: false, reason: "The reviewed group needs a non-empty name." };
|
|
709
|
+
if (!validategroupcolor(spec.color)) return { allowed: false, reason: "The reviewed group color must be a Chromium tab group color." };
|
|
710
|
+
if (!validateidlist(spec, "tabids")) return { allowed: false, reason: "The reviewed group needs a non-empty list of member tab ids." };
|
|
711
|
+
}
|
|
712
|
+
if (kind === "colorgroup") {
|
|
713
|
+
if (!isnonempty(options.name)) return { allowed: false, reason: "A reviewed group name is required in options." };
|
|
714
|
+
if (!validategroupcolor(options.color)) return { allowed: false, reason: "The reviewed group color must be a Chromium tab group color." };
|
|
715
|
+
}
|
|
716
|
+
if (kind === "collapsegroup") {
|
|
717
|
+
if (!isnonempty(options.name)) return { allowed: false, reason: "A reviewed group name is required in options." };
|
|
718
|
+
if (typeof options.collapsed !== "boolean") return { allowed: false, reason: "A reviewed collapsed flag is required in options." };
|
|
719
|
+
}
|
|
720
|
+
if (kind === "discardtab" || kind === "reloadtabs") {
|
|
721
|
+
if (!isnumericid(step.value) && !validateidlist(options, "tabs")) return { allowed: false, reason: "A numeric tab id or a reviewed list of tab ids is required." };
|
|
722
|
+
}
|
|
723
|
+
if (kind === "zoomin" || kind === "zoomout") {
|
|
724
|
+
if (options.step !== void 0 && (typeof options.step !== "number" || !Number.isFinite(options.step) || options.step <= 0)) return { allowed: false, reason: "The reviewed zoom step must be a positive number with no code ceiling." };
|
|
725
|
+
if (step.value !== void 0 && step.value !== "" && !isnumericid(step.value)) return { allowed: false, reason: "The reviewed zoom target must be a numeric tab id." };
|
|
726
|
+
}
|
|
727
|
+
if (kind === "switchtab") {
|
|
728
|
+
if (options.direction !== "next" && options.direction !== "previous") return { allowed: false, reason: "A reviewed switch direction of next or previous is required in options." };
|
|
729
|
+
}
|
|
730
|
+
if (kind === "restorewindow") {
|
|
731
|
+
const bounds = options.bounds;
|
|
732
|
+
if (bounds !== void 0) {
|
|
733
|
+
if (!bounds || typeof bounds !== "object" || Array.isArray(bounds)) return { allowed: false, reason: "The reviewed window bounds must be an object." };
|
|
734
|
+
const shape = bounds;
|
|
735
|
+
for (const field of ["left", "top", "width", "height"]) {
|
|
736
|
+
if (typeof shape[field] !== "number" || !Number.isFinite(shape[field])) return { allowed: false, reason: "The reviewed window bounds need numeric left, top, width and height." };
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
if (kind === "scratchwindow") {
|
|
741
|
+
if (step.value !== void 0 && step.value !== "" && !ishttpsurl(step.value)) return { allowed: false, reason: "The reviewed scratch window url must use HTTPS." };
|
|
742
|
+
}
|
|
743
|
+
if (kind === "incognitowindow" && !ishttpsurl(step.value)) return { allowed: false, reason: "A reviewed HTTPS url is required to open an incognito window." };
|
|
744
|
+
if (kind === "restoretab" && step.value !== void 0 && step.value !== "" && !ishttpsurl(step.value)) return { allowed: false, reason: "The reviewed restore url must use HTTPS." };
|
|
745
|
+
if (kind === "savelayout" || kind === "restorelayout") {
|
|
746
|
+
if (!isnonempty(options.name)) return { allowed: false, reason: "A reviewed layout name is required in options." };
|
|
747
|
+
}
|
|
748
|
+
if (kind === "badgetab") {
|
|
749
|
+
if (!isnonempty(options.label)) return { allowed: false, reason: "A reviewed badge label is required in options." };
|
|
750
|
+
if (options.taskid !== void 0 && !isnonempty(options.taskid)) return { allowed: false, reason: "The reviewed badge task id must be a non-empty string." };
|
|
751
|
+
}
|
|
752
|
+
if (kind === "attachmeta") {
|
|
753
|
+
const labels = options.labels;
|
|
754
|
+
const taskrefs = options.taskrefs;
|
|
755
|
+
const haslabels = Array.isArray(labels) && labels.length > 0 && labels.every((label) => isnonempty(label));
|
|
756
|
+
const hastaskrefs = Array.isArray(taskrefs) && taskrefs.length > 0 && taskrefs.every((ref) => isnonempty(ref));
|
|
757
|
+
if (!haslabels && !hastaskrefs) return { allowed: false, reason: "Reviewed labels or task refs are required in options to attach metadata." };
|
|
758
|
+
if (options.provenance !== void 0 && !isnonempty(options.provenance)) return { allowed: false, reason: "The reviewed provenance must be a non-empty string." };
|
|
759
|
+
}
|
|
760
|
+
if (kind === "reopenrun" && !isnonempty(options.run)) return { allowed: false, reason: "A reviewed run id is required in options to reopen its tabs." };
|
|
761
|
+
return { allowed: true };
|
|
762
|
+
}
|
|
364
763
|
function validatestep(step, origin) {
|
|
365
764
|
if (!allowedactions.has(step.kind)) return { allowed: false, reason: "Unsupported action kind." };
|
|
366
765
|
if (!step.summary.trim()) return { allowed: false, reason: "A human-readable action summary is required." };
|
|
@@ -498,6 +897,84 @@ function validatestep(step, origin) {
|
|
|
498
897
|
const versions = options.versions;
|
|
499
898
|
if (!Array.isArray(versions) || versions.length !== 2 || !versions.every((version) => typeof version === "number" && Number.isInteger(version) && version >= 1)) return { allowed: false, reason: "Two reviewed observation version numbers are required in options." };
|
|
500
899
|
}
|
|
900
|
+
if (step.kind === "openlink" || step.kind === "openprivate" || step.kind === "deeplink") {
|
|
901
|
+
const targetcheck = validatenavtarget(options.navtarget, step.kind);
|
|
902
|
+
if (!targetcheck.allowed) return targetcheck;
|
|
903
|
+
if (step.kind === "deeplink") {
|
|
904
|
+
const app = options.app;
|
|
905
|
+
if (!isnonempty(app)) return { allowed: false, reason: "A reviewed deep link app pattern is required in options." };
|
|
906
|
+
const params = options.params;
|
|
907
|
+
if (params !== void 0 && (!params || typeof params !== "object" || Array.isArray(params) || !Object.values(params).every((item) => typeof item === "string"))) return { allowed: false, reason: "The reviewed deep link params must be an object of string values." };
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (step.kind === "waitload" && !nonnegativeoption(options, "timeout")) return { allowed: false, reason: "The waitload timeout must be zero or a positive number of milliseconds." };
|
|
911
|
+
if (step.kind === "waiturl" || step.kind === "spawait") {
|
|
912
|
+
if (step.kind === "waiturl") {
|
|
913
|
+
const patterncheck = validateurlpattern(options.urlpattern);
|
|
914
|
+
if (!patterncheck.allowed) return patterncheck;
|
|
915
|
+
}
|
|
916
|
+
if (!nonnegativeoption(options, "timeout")) return { allowed: false, reason: "The wait timeout must be zero or a positive number of milliseconds." };
|
|
917
|
+
if (!nonnegativeoption(options, "poll")) return { allowed: false, reason: "The wait poll interval must be zero or a positive number of milliseconds." };
|
|
918
|
+
}
|
|
919
|
+
if (step.kind === "followlink") {
|
|
920
|
+
if (options.fragment !== void 0 && typeof options.fragment !== "boolean") return { allowed: false, reason: "The reviewed followlink fragment flag must be a boolean." };
|
|
921
|
+
}
|
|
922
|
+
if (step.kind === "spanav") {
|
|
923
|
+
if (options.routepattern !== void 0) {
|
|
924
|
+
const routecheck = validateurlpattern(options.routepattern);
|
|
925
|
+
if (!routecheck.allowed) return routecheck;
|
|
926
|
+
}
|
|
927
|
+
if (!nonnegativeoption(options, "timeout")) return { allowed: false, reason: "The spanav route timeout must be zero or a positive number of milliseconds." };
|
|
928
|
+
}
|
|
929
|
+
if (step.kind === "rewritequery") {
|
|
930
|
+
const set = options.set;
|
|
931
|
+
const remove = options.remove;
|
|
932
|
+
if (set === void 0 && remove === void 0) return { allowed: false, reason: "Reviewed query parameters to set or remove are required in options." };
|
|
933
|
+
if (set !== void 0 && (!set || typeof set !== "object" || Array.isArray(set) || !Object.values(set).every((item) => typeof item === "string"))) return { allowed: false, reason: "The reviewed query parameters to set must be an object of string values." };
|
|
934
|
+
if (remove !== void 0 && (!Array.isArray(remove) || !remove.every((item) => isnonempty(item)))) return { allowed: false, reason: "The reviewed query parameters to remove must be a list of non-empty names." };
|
|
935
|
+
}
|
|
936
|
+
if (step.kind === "navlist") {
|
|
937
|
+
const listcheck = validateurllist(options, "urls");
|
|
938
|
+
if (!listcheck.allowed) return listcheck;
|
|
939
|
+
}
|
|
940
|
+
if (step.kind === "navprofile") {
|
|
941
|
+
const profilecheck = validatewaitprofile(options.waitprofile);
|
|
942
|
+
if (!profilecheck.allowed) return profilecheck;
|
|
943
|
+
}
|
|
944
|
+
if (step.kind === "handleauth" && !ishttpsurl(step.value)) return { allowed: false, reason: "A reviewed HTTPS origin or url is required as the auth target." };
|
|
945
|
+
if (step.kind === "printpdf" && options.name !== void 0 && !isnonempty(options.name)) return { allowed: false, reason: "The reviewed artifact name must be a non-empty string." };
|
|
946
|
+
if (step.kind === "prefetch") {
|
|
947
|
+
const listcheck = validateurllist(options, "urls");
|
|
948
|
+
if (!listcheck.allowed) return listcheck;
|
|
949
|
+
}
|
|
950
|
+
if (step.kind === "preconnect") {
|
|
951
|
+
const origins = options.origins;
|
|
952
|
+
if (!Array.isArray(origins) || origins.length === 0 || !origins.every((originurl) => ishttpsurl(originurl))) return { allowed: false, reason: "A reviewed non-empty list of HTTPS origins is required in options." };
|
|
953
|
+
}
|
|
954
|
+
if (step.kind === "reopentab" && step.value !== void 0 && !ishttpsurl(step.value)) return { allowed: false, reason: "The reviewed reopen url must use HTTPS." };
|
|
955
|
+
if (step.kind === "navrate") {
|
|
956
|
+
const limitcheck = validateratelimit(options.ratelimit);
|
|
957
|
+
if (!limitcheck.allowed) return limitcheck;
|
|
958
|
+
}
|
|
959
|
+
if (step.kind === "checksafe" && !ishttpsurl(step.value)) return { allowed: false, reason: "A reviewed HTTPS url is required for the safety check." };
|
|
960
|
+
if (step.kind === "batchopen") {
|
|
961
|
+
const listcheck = validateurllist(options, "urls");
|
|
962
|
+
if (!listcheck.allowed) return listcheck;
|
|
963
|
+
}
|
|
964
|
+
if (istabscommandkind(step.kind)) {
|
|
965
|
+
const tabscheck = validatetabsgrammar(step, options);
|
|
966
|
+
if (!tabscheck.allowed) return tabscheck;
|
|
967
|
+
}
|
|
968
|
+
if (step.kind === "tabcreate") {
|
|
969
|
+
if (options.background !== void 0 && typeof options.background !== "boolean") return { allowed: false, reason: "The reviewed background flag must be a boolean." };
|
|
970
|
+
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." };
|
|
971
|
+
}
|
|
972
|
+
if (step.kind === "windowcreate") {
|
|
973
|
+
for (const field of ["left", "top", "width", "height"]) {
|
|
974
|
+
if (options[field] !== void 0 && (typeof options[field] !== "number" || !Number.isFinite(options[field]))) return { allowed: false, reason: `The reviewed window ${field} must be a number.` };
|
|
975
|
+
}
|
|
976
|
+
if (options.state !== void 0 && !["normal", "maximized", "minimized", "fullscreen"].includes(options.state)) return { allowed: false, reason: "The reviewed window state must be normal, maximized, minimized or fullscreen." };
|
|
977
|
+
}
|
|
501
978
|
return { allowed: true };
|
|
502
979
|
}
|
|
503
980
|
function sessiongate(input) {
|
|
@@ -515,11 +992,42 @@ function canexecute(input) {
|
|
|
515
992
|
if (input.plan.expiresat <= now) return { allowed: false, reason: "The approved plan has expired." };
|
|
516
993
|
if ((input.step.kind === "pierceshadow" || input.step.kind === "enterframe") && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The shadow or frame step is outside the session origin grants." };
|
|
517
994
|
if (input.step.kind === "readjson" && !origingranted(input.session, input.origin)) return { allowed: false, reason: "The json state read is outside the session origin grants." };
|
|
995
|
+
if (input.step.kind === "navlist") {
|
|
996
|
+
let options = {};
|
|
997
|
+
try {
|
|
998
|
+
options = parseoptions(input.step);
|
|
999
|
+
} catch {
|
|
1000
|
+
options = {};
|
|
1001
|
+
}
|
|
1002
|
+
for (const url of Array.isArray(options.urls) ? options.urls : []) {
|
|
1003
|
+
if (typeof url !== "string") continue;
|
|
1004
|
+
const navigation = navigationgranted(input.session, url);
|
|
1005
|
+
if (!navigation.allowed) return navigation;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (islayoutkind(input.step.kind) && !layoutmutationgranted(input.session, now).allowed) return { allowed: false, reason: "Group and layout mutations stay inside the active session." };
|
|
1009
|
+
if (input.step.kind === "openlink" || input.step.kind === "openprivate" || input.step.kind === "batchopen" || input.step.kind === "prefetch" || input.step.kind === "deeplink" || input.step.kind === "reopentab") {
|
|
1010
|
+
let options = {};
|
|
1011
|
+
try {
|
|
1012
|
+
options = parseoptions(input.step);
|
|
1013
|
+
} catch {
|
|
1014
|
+
options = {};
|
|
1015
|
+
}
|
|
1016
|
+
const grammar = validatestep(input.step, input.origin);
|
|
1017
|
+
if (!grammar.allowed) return grammar;
|
|
1018
|
+
const grants = input.session?.grants ?? [input.session?.origin ?? input.origin];
|
|
1019
|
+
const targets = input.step.kind === "batchopen" || input.step.kind === "prefetch" ? Array.isArray(options.urls) ? options.urls : [] : input.step.kind === "reopentab" ? [input.step.value] : [options.navtarget?.url];
|
|
1020
|
+
for (const target of targets) {
|
|
1021
|
+
if (typeof target !== "string" || !target) continue;
|
|
1022
|
+
const verified = originverified(target, grants, input.verdicts ?? []);
|
|
1023
|
+
if (!verified.allowed) return verified;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
518
1026
|
return validatestep(input.step, input.origin);
|
|
519
1027
|
}
|
|
520
1028
|
|
|
521
1029
|
// version.ts
|
|
522
|
-
var packageversion = "1.1.
|
|
1030
|
+
var packageversion = "1.1.36";
|
|
523
1031
|
|
|
524
1032
|
// types.ts
|
|
525
1033
|
var protocolversion = packageversion;
|
|
@@ -608,6 +1116,21 @@ function signalsreport(input) {
|
|
|
608
1116
|
function selectorresponse(input) {
|
|
609
1117
|
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, candidates: input.candidates });
|
|
610
1118
|
}
|
|
1119
|
+
function navstateresponse(input) {
|
|
1120
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, navstate: input.navstate });
|
|
1121
|
+
}
|
|
1122
|
+
function trailreport(input) {
|
|
1123
|
+
return { version: protocolversion, ...input.sessionid ? { sessionid: input.sessionid } : {}, trail: input.trail };
|
|
1124
|
+
}
|
|
1125
|
+
function safetyresponse(input) {
|
|
1126
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, verdicts: input.verdicts });
|
|
1127
|
+
}
|
|
1128
|
+
function tabreportresponse(input) {
|
|
1129
|
+
return JSON.stringify({ version: protocolversion, planid: input.plan.id, planstate: input.plan.state, report: input.report });
|
|
1130
|
+
}
|
|
1131
|
+
function layoutreport(input) {
|
|
1132
|
+
return { version: protocolversion, layouts: input.layouts };
|
|
1133
|
+
}
|
|
611
1134
|
export {
|
|
612
1135
|
canexecute,
|
|
613
1136
|
actionrisk as deriveactionrisk,
|
|
@@ -616,7 +1139,9 @@ export {
|
|
|
616
1139
|
heldkeysreport,
|
|
617
1140
|
hostpattern,
|
|
618
1141
|
iswatchkind,
|
|
1142
|
+
layoutreport,
|
|
619
1143
|
mapresponse,
|
|
1144
|
+
navstateresponse,
|
|
620
1145
|
normalizeendpoint,
|
|
621
1146
|
observationmodeof,
|
|
622
1147
|
observationresponse,
|
|
@@ -626,9 +1151,12 @@ export {
|
|
|
626
1151
|
randomid,
|
|
627
1152
|
requestbody,
|
|
628
1153
|
resolutionverdict,
|
|
1154
|
+
safetyresponse,
|
|
629
1155
|
selectorresponse,
|
|
630
1156
|
sessionmemory,
|
|
631
1157
|
signalsreport,
|
|
1158
|
+
tabreportresponse,
|
|
1159
|
+
trailreport,
|
|
632
1160
|
validatestep,
|
|
633
1161
|
validatetargetref
|
|
634
1162
|
};
|