@ai-ecoverse/slicc-browsers 1.7.1 → 1.7.2
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 -1
- package/guard-runner.mjs +28 -4
- package/guard-vectors.json +847 -323
- package/package.json +3 -3
- package/schema/guard-vectors.schema.json +19 -56
package/README.md
CHANGED
|
@@ -307,7 +307,13 @@ observations: a missing or failing effect fails the control. Guarded liveness
|
|
|
307
307
|
requires the exact target URL in the fixture log or a target-origin `/live`
|
|
308
308
|
heartbeat; alternatively a pre-action `/live?tag=...&step=pre&to=...` request and
|
|
309
309
|
an exact-URL block/error from the client or unguarded frame tree. An error for a
|
|
310
|
-
different URL is not evidence.
|
|
310
|
+
different URL is not evidence. `blocked` accepts either `{ url }` for one exact URL
|
|
311
|
+
or `{ anyOf: [url, ...] }` for a nonempty list of exact URLs; each URL can be a
|
|
312
|
+
string or `{ hops, to, index? }`. Both forms allow `session` and `frame`. Redirect
|
|
313
|
+
vectors list every nested hop URL and the final destination because Chromium
|
|
314
|
+
reports the URL intercepted by the guard. A matching block still requires the
|
|
315
|
+
pre-action beacon alongside it; an unrelated error cannot certify the chain.
|
|
316
|
+
A declared setup refusal can supply liveness only
|
|
311
317
|
when its `refusal.match` matched. Transport connectivity and command completion
|
|
312
318
|
alone never prove liveness. Cookie and storage rows seed a canary outside the
|
|
313
319
|
guard and read it back outside the guard; refused writes require unchanged
|
package/guard-runner.mjs
CHANGED
|
@@ -245,7 +245,7 @@ class Execution {
|
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
async blocked(spec, observer) {
|
|
248
|
-
const
|
|
248
|
+
const urls = (spec.anyOf ?? [spec.url]).map(address);
|
|
249
249
|
const events = this.client.events.slice(this.eventStart);
|
|
250
250
|
if (observer === 'client') {
|
|
251
251
|
const requests = new Map(
|
|
@@ -259,10 +259,10 @@ class Execution {
|
|
|
259
259
|
(!spec.frame ||
|
|
260
260
|
event.params.frame.parentId ||
|
|
261
261
|
event.sessionId === this.sessions.child) &&
|
|
262
|
-
event.params.frame.unreachableUrl
|
|
262
|
+
urls.includes(event.params.frame.unreachableUrl) &&
|
|
263
263
|
event.params.frame.url.startsWith('chrome-error://')) ||
|
|
264
264
|
(event.method === 'Network.loadingFailed' &&
|
|
265
|
-
requests.get(event.params.requestId)
|
|
265
|
+
urls.includes(requests.get(event.params.requestId)) &&
|
|
266
266
|
(event.params.blockedReason || event.params.errorText === 'net::ERR_BLOCKED_BY_CLIENT'))
|
|
267
267
|
);
|
|
268
268
|
}
|
|
@@ -279,7 +279,8 @@ class Execution {
|
|
|
279
279
|
const visit = (tree) =>
|
|
280
280
|
Boolean(
|
|
281
281
|
tree &&
|
|
282
|
-
((tree.frame.unreachableUrl
|
|
282
|
+
((urls.includes(tree.frame.unreachableUrl) &&
|
|
283
|
+
tree.frame.url.startsWith('chrome-error://')) ||
|
|
283
284
|
(tree.childFrames ?? []).some(visit))
|
|
284
285
|
);
|
|
285
286
|
return (
|
|
@@ -624,6 +625,7 @@ class Execution {
|
|
|
624
625
|
);
|
|
625
626
|
if (c.refused || c.reached) return this.callCheck(c);
|
|
626
627
|
if (c.detached) {
|
|
628
|
+
const events = this.client.events.slice(this.eventStart, this.cleanupEventStart);
|
|
627
629
|
const session = this.sessions[c.detached];
|
|
628
630
|
return (
|
|
629
631
|
Boolean(session) &&
|
|
@@ -938,6 +940,27 @@ async function executeRow(job, opts, clients, set, slot, index) {
|
|
|
938
940
|
return row;
|
|
939
941
|
}
|
|
940
942
|
|
|
943
|
+
const invalidBlocked = (check) =>
|
|
944
|
+
Boolean(
|
|
945
|
+
check?.blocked &&
|
|
946
|
+
((check.blocked.url === undefined) === (check.blocked.anyOf === undefined) ||
|
|
947
|
+
(check.blocked.anyOf !== undefined &&
|
|
948
|
+
(!Array.isArray(check.blocked.anyOf) || check.blocked.anyOf.length === 0)))
|
|
949
|
+
) ||
|
|
950
|
+
[...(check?.all ?? []), ...(check?.any ?? []), ...(check?.not ? [check.not] : [])].some(
|
|
951
|
+
invalidBlocked
|
|
952
|
+
);
|
|
953
|
+
|
|
954
|
+
function validateBlocked(vectors) {
|
|
955
|
+
for (const vector of vectors)
|
|
956
|
+
if (
|
|
957
|
+
[vector.liveCheck, ...Object.values(vector.observe ?? {}).map((o) => o.check)].some(
|
|
958
|
+
invalidBlocked
|
|
959
|
+
)
|
|
960
|
+
)
|
|
961
|
+
throw new TypeError(`${vector.id}: blocked needs exactly one url or nonempty anyOf`);
|
|
962
|
+
}
|
|
963
|
+
|
|
941
964
|
function validate(set, opts) {
|
|
942
965
|
if (!opts.fixture?.log) throw new TypeError('fixture.log is required');
|
|
943
966
|
for (const vector of set.vectors)
|
|
@@ -954,6 +977,7 @@ function validate(set, opts) {
|
|
|
954
977
|
for (const vector of set.vectors)
|
|
955
978
|
if (vector.kind === 'cdp' && invalidLive(vector.liveCheck))
|
|
956
979
|
throw new TypeError(`${vector.id}: liveCheck needs guarded attempt evidence`);
|
|
980
|
+
validateBlocked(set.vectors);
|
|
957
981
|
const variants = Array.isArray(opts.variant) ? opts.variant : [opts.variant];
|
|
958
982
|
for (const name of variants)
|
|
959
983
|
if (!(name in set.variants)) throw new TypeError(`Unknown variant: ${name}`);
|