@ai-ecoverse/slicc-browsers 1.13.5 → 1.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/guard-driver.mjs +15 -6
- package/guard-runner.mjs +6 -5
- package/guard-vectors.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,7 +224,7 @@ A client may register `Fetch.enable` patterns naming a protected origin in eithe
|
|
|
224
224
|
- `observe?`: each observed name, as `{ "means", "dependsOn"?, "requires"?, "eitherWhen"? }`. `requires` gates only that observation when it is not also a whole-vector requirement: unavailable evidence is recorded as `"not-supported"`, excluded from that field's expectations, and reported in the row's `notSupported` list. `dependsOn` lists the refusals the observation depends on (see Refusals). `eitherWhen` is a check: when it holds on the guarded leg, that observation's expectation becomes `"either"` there (see Design-dependent outcomes);
|
|
225
225
|
- `live`: the liveness check;
|
|
226
226
|
- `control` (`origin: "ordinary"`, `via`, `expected`) and `expected`: an object of names and booleans, or text;
|
|
227
|
-
- `skip?` (`target`, `reason`): `reason` starts with a code, as in `not-applicable: …`.
|
|
227
|
+
- `skip?` (`target`, `reason`): `reason` starts with a code, as in `not-applicable: …`. A runner reports the row on `target` as `skip` with this `reason`, unless a capability is missing.
|
|
228
228
|
|
|
229
229
|
**Steps** are declarative and transport-neutral. Any step can take `on` (`control` or `guarded`), which runs it on that leg only.
|
|
230
230
|
Any `cdp`, `page` or `children` step can take `requires`, the capabilities it needs: `autoAttach` for `Target.setAutoAttach`, `children` and `session: "child"`, `browserSession` for `session: "browser"`, `browserContexts` for `Target.createBrowserContext` and `Target.disposeBrowserContext`, and `chromeUrlsAttach` for a step that must attach to a `chrome://` page to pass.
|
|
@@ -279,7 +279,7 @@ For remote/page harnesses, `fixtureClient(base)` is exported by `./guard-runner`
|
|
|
279
279
|
|
|
280
280
|
**Design-dependent outcomes.** A key listed in `designDependent` may expect `"either"`, which `true` and `false` both match: a guard that refuses a setup step lets the real document load, and one that blocks before commit doesn't. `detached` is design-dependent on the rows whose guarded leg proves the block (the pre-action beacon plus an exact-URL block or the history form): its `eitherWhen` holds the same proof, so a guard that blocks without detaching passes, and one that neither blocks nor detaches still fails. The hard negatives stay required.
|
|
281
281
|
|
|
282
|
-
**Observability.** `observable.own: false` marks a vector the guard host's own origin can't show (headers, auth, redirects); a runner
|
|
282
|
+
**Observability.** `observable.own: false` marks a vector the guard host's own origin can't show (headers, auth, redirects); a runner reports it as `skip` with `not-applicable` on that target.
|
|
283
283
|
|
|
284
284
|
**Applies to.** `appliesTo` lists the triggers a vector runs with (`href`, `iframe`, `popup`, `redirect`). Chrome doesn't apply some session overrides inside a cross-site iframe, so those E6 rows list `href` only.
|
|
285
285
|
|
|
@@ -306,10 +306,11 @@ It exports:
|
|
|
306
306
|
- `waitFor(check, ms, step?)`: polls `check` every `step` ms (default 25) until it holds, checking once more after the bound. A timer heartbeat every `step` runs for the whole wait, including while `check` is awaiting a reply, and any gap between beats longer than a second (or four steps, if longer) extends the bound by that gap, so a synchronous event-loop stall elsewhere in the process does not consume it. The extensions add up to at most `max(4 × ms, 30000)` ms, so a throttled page cannot stretch a wait without limit. `load`, the runner's waits and the error-page and redirect-body probes use it;
|
|
307
307
|
- `judge(vector, side, observed)`: `{ pass, refused, skipped, mismatches }`, where `refused` lists the leg's refusals and `skipped` the observations that depend on them or explicitly record unavailable observation capabilities;
|
|
308
308
|
- `result(vector, run)`: a report row from `{ target, variant, transport, via?, capabilities?, control, guarded, live }`. `capabilities` is the transport's; without it, the transport has them all;
|
|
309
|
+
- `skipReason(vector, target, scope)`: the reason the runner skips the row with when it has every capability: the vector's `skip.reason` when its `skip.target` is `target`, `not-applicable` when its `observable[target]` is `false` or its `scope` is neither `any` nor `scope`, otherwise `null`; `inapplicable(vector, target, scope)` is whether it isn't `null`;
|
|
309
310
|
- `knownOrigins` and `originDefaults`: the origin names a runner can miss, and the origins `plain`, `cross`, `log` and `own` fall back to when unset;
|
|
310
311
|
- `parseURL(value, base?)`: a `URL`, or `null` for anything `new URL` rejects or a non-string; every parse of a fixture log entry or a Chrome-reported URL in the runner and the fixture goes through it (`protected.mjs` keeps its own copy, since it imports nothing), so an unparseable entry never throws;
|
|
311
312
|
- `owned(entries, tag)`: the fixture log entries whose raw URL contains `tag`, the row's attribution through its `n=`, `tag=` or `to=` query;
|
|
312
|
-
- `summary({ guard, sha, set, results })`: `set` is the parsed `guard-vectors.json`. It returns `{ set, schemaVersion, guard, sha, variants, targets, transports, results, skipped, notRun, missing, forged, pass, line }`. `missing` lists each `{ id, target, variant, transport }` without a row, for every vector whose `scope` is `any` or the guard, every target it lists, every variant in `variants` or `results`, and every transport in `results`; `pass` needs it empty, where `line` is `GUARD-PASS <guard>@<sha> (vectors @ai-ecoverse/slicc-browsers@X.Y.Z)` or `GUARD-FAIL …`. `forged` lists each `{ id, target, variant, transport, why }` that is `not-run` on a vector that isn't `kind: "text"`, or `skip` without a reason the runner gives (`not-applicable
|
|
313
|
+
- `summary({ guard, sha, set, results })`: `set` is the parsed `guard-vectors.json`. It returns `{ set, schemaVersion, guard, sha, variants, targets, transports, results, skipped, notRun, missing, forged, pass, line }`. `missing` lists each `{ id, target, variant, transport }` without a row, for every vector whose `scope` is `any` or the guard, every target it lists, every variant in `variants` or `results`, and every transport in `results`; `pass` needs it empty, where `line` is `GUARD-PASS <guard>@<sha> (vectors @ai-ecoverse/slicc-browsers@X.Y.Z)` or `GUARD-FAIL …`. `forged` lists each `{ id, target, variant, transport, why }` that is `not-run` on a vector that isn't `kind: "text"`, or `skip` without a reason the runner gives (`skipReason(vector, target, guard)`, the reason the runner gives: the vector's own `skip.reason` on its `skip.target`, or `not-applicable` for a target it marks unobservable or a `scope` other than `any` and the guard; `not-applicable:no-<origin>-origin` for an origin in `knownOrigins` that the row's target, `plain`, the control's origin, the steps or a check names, directly or through `originDefaults`, where only the first branch of an `any` counts, or `not-supported:<capability>` for a capability the set requires, `browserSession` or `unguarded-observer`); `pass` needs it empty too, and `line` then ends in `forged <n>`.
|
|
313
314
|
|
|
314
315
|
**`npm run validate`** checks that:
|
|
315
316
|
- the file matches the schema, and ids are unique;
|
|
@@ -358,7 +359,7 @@ otherwise they run serially. `capabilities` is an object of booleans or an array
|
|
|
358
359
|
of supported names, and a capability that isn't declared is unsupported: a CDP
|
|
359
360
|
transport over a DevTools port declares `chromeUrlsAttach: true`, and an
|
|
360
361
|
extension on `chrome.debugger` leaves it out, so `C8.newtab` is skipped with
|
|
361
|
-
`not-supported:chromeUrlsAttach`. `capabilities.scope` selects owner-specific rows
|
|
362
|
+
`not-supported:chromeUrlsAttach`. `capabilities.scope` selects owner-specific rows; pass the same name as `guard` to `summary()`.
|
|
362
363
|
|
|
363
364
|
Map `ordinary`, `ordinary-same-site`, `seven`, `vault`, `vault-x`, `apex`, `own`
|
|
364
365
|
and `client` to origins. `chrome-newtab`, `chrome-new-tab-page`, `chrome-settings`, `chrome-passwords`, `chrome-search`, `network-error`, `chrome-extension-page` and `file-root` default to their `url`.
|
package/guard-driver.mjs
CHANGED
|
@@ -336,17 +336,26 @@ function originReason(vector, row, reason) {
|
|
|
336
336
|
].includes(origin);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
|
|
339
|
+
export const skipReason = (vector, target, scope) => {
|
|
340
|
+
if (!vector) return null;
|
|
341
|
+
if (vector.skip?.target === target) return vector.skip.reason;
|
|
342
|
+
if (vector.observable?.[target] === false) return 'not-applicable';
|
|
343
|
+
return vector.scope !== 'any' && vector.scope !== scope ? 'not-applicable' : null;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export const inapplicable = (vector, target, scope) => skipReason(vector, target, scope) !== null;
|
|
347
|
+
|
|
348
|
+
function skipWhy(vector, row, known, guard) {
|
|
340
349
|
const { reason } = row;
|
|
341
350
|
if (typeof reason !== 'string' || reason === '') return 'skip without a reason';
|
|
342
|
-
if (reason ===
|
|
343
|
-
if (vector
|
|
351
|
+
if (reason === skipReason(vector, row.target, guard)) return null;
|
|
352
|
+
if (originReason(vector, row, reason)) return null;
|
|
344
353
|
if (reason.startsWith('not-supported:') && known.has(reason.slice('not-supported:'.length)))
|
|
345
354
|
return null;
|
|
346
355
|
return `skip with a reason the runner never gives: ${reason}`;
|
|
347
356
|
}
|
|
348
357
|
|
|
349
|
-
function forgeries(set, results) {
|
|
358
|
+
function forgeries(set, results, guard) {
|
|
350
359
|
const vectors = new Map(set.vectors.map((v) => [v.id, v]));
|
|
351
360
|
const known = new Set([
|
|
352
361
|
'browserSession',
|
|
@@ -359,7 +368,7 @@ function forgeries(set, results) {
|
|
|
359
368
|
const why = [];
|
|
360
369
|
if (legs.includes('not-run') && vector?.kind !== 'text')
|
|
361
370
|
why.push('not-run on a vector that is not kind text');
|
|
362
|
-
if (legs.includes('skip')) why.push(skipWhy(vector, row, known));
|
|
371
|
+
if (legs.includes('skip')) why.push(skipWhy(vector, row, known, guard));
|
|
363
372
|
const found = why.filter(Boolean);
|
|
364
373
|
if (!found.length) return [];
|
|
365
374
|
const { id, target, variant, transport } = row;
|
|
@@ -377,7 +386,7 @@ export function summary({ guard, sha, set, results }) {
|
|
|
377
386
|
.flatMap((row) => variants.map((variant) => ({ ...row, variant })))
|
|
378
387
|
.flatMap((row) => uniq('transport').map((transport) => ({ ...row, transport })))
|
|
379
388
|
.filter((r) => !has.has(q([r.id, r.target, r.variant, r.transport])));
|
|
380
|
-
const forged = forgeries(set, results);
|
|
389
|
+
const forged = forgeries(set, results, guard);
|
|
381
390
|
const pass =
|
|
382
391
|
results.length > 0 && missing.length === 0 && forged.length === 0 && results.every(passed);
|
|
383
392
|
const name = `@ai-ecoverse/slicc-browsers@${set.setVersion}`;
|
package/guard-runner.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
canary,
|
|
9
9
|
expand,
|
|
10
10
|
hop,
|
|
11
|
+
inapplicable,
|
|
11
12
|
isRefusal,
|
|
12
13
|
knownOrigins,
|
|
13
14
|
loadedAfter,
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
parseURL,
|
|
18
19
|
summary as report,
|
|
19
20
|
result,
|
|
21
|
+
skipReason,
|
|
20
22
|
steps,
|
|
21
23
|
waitFor,
|
|
22
24
|
} from './guard-driver.mjs';
|
|
@@ -1289,10 +1291,7 @@ const needsObserver = (vector) =>
|
|
|
1289
1291
|
);
|
|
1290
1292
|
|
|
1291
1293
|
const inactive = (vector, target, capabilities) =>
|
|
1292
|
-
vector
|
|
1293
|
-
vector.observable?.[target] === false ||
|
|
1294
|
-
!supported(vector.requires, capabilities) ||
|
|
1295
|
-
(vector.scope !== 'any' && vector.scope !== capabilities.scope);
|
|
1294
|
+
inapplicable(vector, target, capabilities.scope) || !supported(vector.requires, capabilities);
|
|
1296
1295
|
|
|
1297
1296
|
const allSteps = (list) => list.flatMap((step) => [step, ...allSteps(step.children ?? [])]);
|
|
1298
1297
|
const browserContextStep = (vector, capabilities) =>
|
|
@@ -1495,7 +1494,9 @@ async function executeRow(job, opts, made, set, slot, index, flights) {
|
|
|
1495
1494
|
control: 'skip',
|
|
1496
1495
|
guarded: 'skip',
|
|
1497
1496
|
live: 'skip',
|
|
1498
|
-
reason: missing
|
|
1497
|
+
reason: missing
|
|
1498
|
+
? `not-supported:${missing}`
|
|
1499
|
+
: skipReason(vector, target, opts.capabilities.scope),
|
|
1499
1500
|
};
|
|
1500
1501
|
}
|
|
1501
1502
|
if (
|
package/guard-vectors.json
CHANGED