@ai-ecoverse/slicc-browsers 1.14.5 → 1.14.7
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 +2 -1
- package/guard-driver.mjs +75 -2
- package/guard-runner.mjs +2 -15
- package/guard-vectors.json +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -309,9 +309,10 @@ It exports:
|
|
|
309
309
|
- `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;
|
|
310
310
|
- `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;
|
|
311
311
|
- `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;
|
|
312
|
+
- `knownOrigins` and `originDefaults`: the origin names a runner can miss, and the origins `plain`, `cross`, `log` and `own` fall back to when unset;
|
|
312
313
|
- `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;
|
|
313
314
|
- `owned(entries, tag)`: the fixture log entries whose raw URL contains `tag`, the row's attribution through its `n=`, `tag=` or `to=` query;
|
|
314
|
-
- `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, 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 …`.
|
|
315
|
+
- `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`, `not-applicable:no-<origin>-origin` for an origin in `knownOrigins` that the row's target or vector names, directly or through `originDefaults`, the vector's own `skip.reason` on its target, 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>`.
|
|
315
316
|
|
|
316
317
|
**`npm run validate`** checks that:
|
|
317
318
|
- the file matches the schema, and ids are unique;
|
package/guard-driver.mjs
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
const q = JSON.stringify;
|
|
2
2
|
|
|
3
|
+
export const originDefaults = {
|
|
4
|
+
plain: 'ordinary',
|
|
5
|
+
cross: 'client',
|
|
6
|
+
log: 'ordinary',
|
|
7
|
+
own: 'client',
|
|
8
|
+
};
|
|
9
|
+
export const knownOrigins = [
|
|
10
|
+
'ordinary',
|
|
11
|
+
'ordinary-same-site',
|
|
12
|
+
'seven',
|
|
13
|
+
'vault',
|
|
14
|
+
'vault-x',
|
|
15
|
+
'apex',
|
|
16
|
+
'own',
|
|
17
|
+
'client',
|
|
18
|
+
'plain',
|
|
19
|
+
'cross',
|
|
20
|
+
'log',
|
|
21
|
+
];
|
|
22
|
+
|
|
3
23
|
export const beacon = (log, tag) =>
|
|
4
24
|
`try { const u = ${q(log)} + '/beacon?tag=' + ${q(tag)} + '&origin=' + encodeURIComponent(location.origin); if (!navigator.sendBeacon(u)) fetch(u, { keepalive: true, mode: 'no-cors' }).catch(() => {}); } catch {}`;
|
|
5
25
|
|
|
@@ -276,6 +296,56 @@ const passed = (r) =>
|
|
|
276
296
|
[r.control, r.guarded, r.live].every((s) => s === 'skip') ||
|
|
277
297
|
[r.control, r.guarded, r.live].every((s) => s === 'not-run');
|
|
278
298
|
|
|
299
|
+
const capabilities = (value) =>
|
|
300
|
+
Array.isArray(value)
|
|
301
|
+
? value.flatMap(capabilities)
|
|
302
|
+
: value && typeof value === 'object'
|
|
303
|
+
? Object.entries(value).flatMap(([key, item]) =>
|
|
304
|
+
key === 'requires' && Array.isArray(item) ? item : capabilities(item)
|
|
305
|
+
)
|
|
306
|
+
: [];
|
|
307
|
+
|
|
308
|
+
function originReason(vector, row, reason) {
|
|
309
|
+
const origin = /^not-applicable:no-(.+)-origin$/.exec(reason)?.[1];
|
|
310
|
+
if (!knownOrigins.includes(origin)) return false;
|
|
311
|
+
return [
|
|
312
|
+
...q([`{${row.target}}`, '{plain}', vector ?? null]).matchAll(/\{([A-Za-z][A-Za-z-]*)\}/g),
|
|
313
|
+
]
|
|
314
|
+
.map((match) => (match[1] === 'beacon' || match[1] === 'probe' ? 'log' : match[1]))
|
|
315
|
+
.some((name) => name === origin || originDefaults[name] === origin);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function skipWhy(vector, row, known) {
|
|
319
|
+
const { reason } = row;
|
|
320
|
+
if (typeof reason !== 'string' || reason === '') return 'skip without a reason';
|
|
321
|
+
if (reason === 'not-applicable' || originReason(vector, row, reason)) return null;
|
|
322
|
+
if (vector?.skip?.target === row.target && vector.skip.reason === reason) return null;
|
|
323
|
+
if (reason.startsWith('not-supported:') && known.has(reason.slice('not-supported:'.length)))
|
|
324
|
+
return null;
|
|
325
|
+
return `skip with a reason the runner never gives: ${reason}`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function forgeries(set, results) {
|
|
329
|
+
const vectors = new Map(set.vectors.map((v) => [v.id, v]));
|
|
330
|
+
const known = new Set([
|
|
331
|
+
'browserSession',
|
|
332
|
+
'unguarded-observer',
|
|
333
|
+
...capabilities(set.vectors).filter((c) => typeof c === 'string' && c !== ''),
|
|
334
|
+
]);
|
|
335
|
+
return results.flatMap((row) => {
|
|
336
|
+
const legs = [row.control, row.guarded, row.live];
|
|
337
|
+
const vector = vectors.get(row.id);
|
|
338
|
+
const why = [];
|
|
339
|
+
if (legs.includes('not-run') && vector?.kind !== 'text')
|
|
340
|
+
why.push('not-run on a vector that is not kind text');
|
|
341
|
+
if (legs.includes('skip')) why.push(skipWhy(vector, row, known));
|
|
342
|
+
const found = why.filter(Boolean);
|
|
343
|
+
if (!found.length) return [];
|
|
344
|
+
const { id, target, variant, transport } = row;
|
|
345
|
+
return [{ id, target, variant, transport, why: found.join('; ') }];
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
279
349
|
export function summary({ guard, sha, set, results }) {
|
|
280
350
|
const uniq = (k) => [...new Set(results.map((r) => r[k]))].sort();
|
|
281
351
|
const variants = [...new Set([...Object.keys(set.variants ?? {}), ...uniq('variant')])].sort();
|
|
@@ -286,7 +356,9 @@ export function summary({ guard, sha, set, results }) {
|
|
|
286
356
|
.flatMap((row) => variants.map((variant) => ({ ...row, variant })))
|
|
287
357
|
.flatMap((row) => uniq('transport').map((transport) => ({ ...row, transport })))
|
|
288
358
|
.filter((r) => !has.has(q([r.id, r.target, r.variant, r.transport])));
|
|
289
|
-
const
|
|
359
|
+
const forged = forgeries(set, results);
|
|
360
|
+
const pass =
|
|
361
|
+
results.length > 0 && missing.length === 0 && forged.length === 0 && results.every(passed);
|
|
290
362
|
const name = `@ai-ecoverse/slicc-browsers@${set.setVersion}`;
|
|
291
363
|
return {
|
|
292
364
|
set: name,
|
|
@@ -302,8 +374,9 @@ export function summary({ guard, sha, set, results }) {
|
|
|
302
374
|
.map(({ id, target, reason }) => ({ id, target, reason })),
|
|
303
375
|
notRun: [...new Set(results.filter((r) => r.control === 'not-run').map((r) => r.id))],
|
|
304
376
|
missing,
|
|
377
|
+
forged,
|
|
305
378
|
pass,
|
|
306
|
-
line: `${pass ? 'GUARD-PASS' : 'GUARD-FAIL'} ${guard}@${sha} (vectors ${name})`,
|
|
379
|
+
line: `${pass ? 'GUARD-PASS' : 'GUARD-FAIL'} ${guard}@${sha} (vectors ${name})${forged.length ? ` forged ${forged.length}` : ''}`,
|
|
307
380
|
};
|
|
308
381
|
}
|
|
309
382
|
|
package/guard-runner.mjs
CHANGED
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
expand,
|
|
10
10
|
hop,
|
|
11
11
|
isRefusal,
|
|
12
|
+
knownOrigins,
|
|
12
13
|
loadedAfter,
|
|
14
|
+
originDefaults,
|
|
13
15
|
outcome,
|
|
14
16
|
owned,
|
|
15
17
|
parseURL,
|
|
@@ -1302,21 +1304,6 @@ const browserContextStep = (vector, capabilities) =>
|
|
|
1302
1304
|
supported(['browserContexts'], capabilities))
|
|
1303
1305
|
);
|
|
1304
1306
|
|
|
1305
|
-
const originDefaults = { plain: 'ordinary', cross: 'client', log: 'ordinary', own: 'client' };
|
|
1306
|
-
const knownOrigins = [
|
|
1307
|
-
'ordinary',
|
|
1308
|
-
'ordinary-same-site',
|
|
1309
|
-
'seven',
|
|
1310
|
-
'vault',
|
|
1311
|
-
'vault-x',
|
|
1312
|
-
'apex',
|
|
1313
|
-
'own',
|
|
1314
|
-
'client',
|
|
1315
|
-
'plain',
|
|
1316
|
-
'cross',
|
|
1317
|
-
'log',
|
|
1318
|
-
];
|
|
1319
|
-
|
|
1320
1307
|
function nullOrigins(value, targets) {
|
|
1321
1308
|
return [...q(value).matchAll(/\{([A-Za-z][A-Za-z-]*)\}/g)]
|
|
1322
1309
|
.map((match) => (match[1] === 'beacon' || match[1] === 'probe' ? 'log' : match[1]))
|
package/guard-vectors.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-browsers",
|
|
3
|
-
"version": "1.14.
|
|
3
|
+
"version": "1.14.7",
|
|
4
4
|
"description": "SLICC's shared browser catalog, JSON schemas and discovery conformance corpus",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"validate": "node scripts/validate.mjs",
|
|
67
67
|
"lint": "slicc-lint && test -z \"$(gofmt -l .)\" && go mod tidy -diff && go vet ./... && golangci-lint run && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings",
|
|
68
68
|
"format": "biome check --write . && gofmt -w . && cargo fmt",
|
|
69
|
-
"test": "npm run validate && node --test test/integration/self-contained.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/git-retry.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/dist-tag.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-stall.test.mjs && go test -race -count=1 ./test/integration/... && cargo test --all-features",
|
|
69
|
+
"test": "npm run validate && node --test test/integration/self-contained.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/git-retry.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/dist-tag.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs && go test -race -count=1 ./test/integration/... && cargo test --all-features",
|
|
70
70
|
"test:unit": "go test -race -cover ./...",
|
|
71
71
|
"build": "go build ./... && cargo build --all-features && cargo build --target wasm32-wasip1 --all-features && npm pack --dry-run",
|
|
72
72
|
"trust": "npx fledgling sync -y --skip-publish",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"test:unit:guard": "mkdir -p coverage/unit && node --experimental-vm-modules --no-warnings --experimental-test-coverage --test --test-coverage-include=guard-runner.mjs --test-coverage-include=fixture-server.mjs --test-coverage-include=fixture-client.mjs --test-coverage-include=observer-target.mjs --test-coverage-include=protected-error-page.mjs --test-coverage-include=unparseable-url.mjs --test-coverage-include=scripts/browser-runner.mjs --test-coverage-include=scripts/validate.mjs --test-reporter=lcov --test-reporter-destination=coverage/unit/guard.lcov test/unit/guard-*.test.mjs && slicc-diff-cover coverage/unit/guard.lcov"
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@ai-ecoverse/slicc-shared-web": "2.
|
|
84
|
+
"@ai-ecoverse/slicc-shared-web": "2.4.0",
|
|
85
85
|
"@biomejs/biome": "2.5.15",
|
|
86
86
|
"ajv": "8.20.0",
|
|
87
87
|
"ajv-formats": "3.0.1",
|