@ai-ecoverse/slicc-browsers 1.18.15 → 1.18.17

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "setVersion": "1.18.15",
3
+ "setVersion": "1.18.17",
4
4
  "sources": [
5
5
  {
6
6
  "repo": "ai-ecoverse/slicc-cli",
@@ -1200,7 +1200,7 @@
1200
1200
  }
1201
1201
  },
1202
1202
  "eventsRedacted": {
1203
- "means": "protected error-page recover: eventsRedacted",
1203
+ "means": "protected error-page recover: every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
1204
1204
  "requires": ["discovery"],
1205
1205
  "check": {
1206
1206
  "result": "errorPage",
@@ -1349,7 +1349,7 @@
1349
1349
  }
1350
1350
  },
1351
1351
  "eventsRedacted": {
1352
- "means": "protected error-page recover (query): eventsRedacted",
1352
+ "means": "protected error-page recover (query): every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
1353
1353
  "requires": ["discovery"],
1354
1354
  "check": {
1355
1355
  "result": "errorPage",
@@ -1498,7 +1498,7 @@
1498
1498
  }
1499
1499
  },
1500
1500
  "eventsRedacted": {
1501
- "means": "protected error-page recover (fragment): eventsRedacted",
1501
+ "means": "protected error-page recover (fragment): every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
1502
1502
  "requires": ["discovery"],
1503
1503
  "check": {
1504
1504
  "result": "errorPage",
@@ -1646,7 +1646,7 @@
1646
1646
  }
1647
1647
  },
1648
1648
  "eventsRedacted": {
1649
- "means": "protected error-page restore: eventsRedacted",
1649
+ "means": "protected error-page restore: every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
1650
1650
  "requires": ["discovery"],
1651
1651
  "check": {
1652
1652
  "result": "errorPage",
@@ -1827,7 +1827,7 @@
1827
1827
  }
1828
1828
  },
1829
1829
  "eventsRedacted": {
1830
- "means": "protected error-page sticky: eventsRedacted",
1830
+ "means": "protected error-page sticky: every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
1831
1831
  "requires": ["discovery"],
1832
1832
  "check": {
1833
1833
  "result": "errorPage",
@@ -2020,7 +2020,7 @@
2020
2020
  }
2021
2021
  },
2022
2022
  "eventsRedacted": {
2023
- "means": "protected error-page sticky (query): eventsRedacted",
2023
+ "means": "protected error-page sticky (query): every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
2024
2024
  "requires": ["discovery"],
2025
2025
  "check": {
2026
2026
  "result": "errorPage",
@@ -2213,7 +2213,7 @@
2213
2213
  }
2214
2214
  },
2215
2215
  "eventsRedacted": {
2216
- "means": "protected error-page sticky (fragment): eventsRedacted",
2216
+ "means": "protected error-page sticky (fragment): every discovery event for the tab shows the failed URL's origin only or a masked or absent url, and no url or title carries the failed URL, its path, query or fragment; one after the client's navigation shows that origin, Chrome's error page, or an empty url titled with the protected host",
2217
2217
  "requires": ["discovery"],
2218
2218
  "check": {
2219
2219
  "result": "errorPage",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-browsers",
3
- "version": "1.18.15",
3
+ "version": "1.18.17",
4
4
  "description": "SLICC's shared browser catalog, JSON schemas and discovery conformance corpus",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -68,7 +68,7 @@
68
68
  "validate": "node scripts/validate.mjs",
69
69
  "lint": "slicc-lint && test -z \"$(gofmt -l .)\" && go mod tidy -diff && go vet ./... && GOOS=darwin go vet ./... && GOOS=windows go vet ./... && golangci-lint run && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings && cargo check --target wasm32-wasip1 --no-default-features && cargo check --target wasm32-wasip1 --features serde && cargo check --target wasm32-wasip1 --all-features",
70
70
  "format": "biome check --write . && gofmt -w . && cargo fmt",
71
- "test": "export PATH=\"$HOME/.cargo/bin:$PATH\" && node --test --test-concurrency=1 test/integration/self-contained.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/guard-redirect-session-end.test.mjs test/integration/guard-redirect-history.test.mjs test/integration/guard-redirect-iframe.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-browser-ui.test.mjs test/integration/guard-snapshot-settle.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-exit-refusal.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.test.mjs test/integration/guard-bfcache-marker.test.mjs test/integration/guard-bfcache-history.test.mjs test/integration/guard-bfcache-review.test.mjs test/integration/guard-bfcache-quarantine.test.mjs test/integration/guard-error-page-navigation.test.mjs test/integration/guard-world-paint.test.mjs test/integration/gates.test.mjs test/integration/validate.test.mjs test/integration/go.test.mjs test/integration/cargo.test.mjs test/integration/workflows.test.mjs test/integration/guard-sweep-union.test.mjs && node --test --test-concurrency=2 test/integration/guard-lifecycle.test.mjs test/integration/chromium-launch.test.mjs test/integration/guard-fit.test.mjs test/integration/guard-chain.test.mjs test/integration/guard-validation.test.mjs test/integration/guard-liveness.test.mjs test/integration/guard-observations.test.mjs test/integration/guard-child-latency.test.mjs test/integration/guard-adoption.test.mjs test/integration/guard-scoping.test.mjs test/integration/guard-late-pause.test.mjs test/integration/guard-legs.test.mjs test/integration/guard-fetch-enable.test.mjs test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.test.mjs test/integration/guard-worlds.test.mjs test/integration/guard-world-discovery.test.mjs test/integration/guard-world-guesses.test.mjs test/integration/guard-unparseable.test.mjs test/integration/guard-redirect-secret.test.mjs test/integration/guard-error-page.test.mjs test/integration/guard-discovery.test.mjs test/integration/guard-bfcache-chrome.test.mjs test/integration/guard-browser-page.test.mjs test/integration/guard-scales.test.mjs test/integration/guard-shared-log.test.mjs test/integration/guard-pre-beacon.test.mjs test/integration/guard-world-pause.test.mjs test/integration/guard-parallel.test.mjs test/integration/guard-client-exit.test.mjs test/integration/guard-runner.test.mjs test/integration/guard-sweep-ordinary.test.mjs test/integration/guard-sweep-seven.test.mjs test/integration/guard-sweep-vault.test.mjs test/integration/guard-sweep-vault-x.test.mjs test/integration/guard-sweep-apex.test.mjs test/integration/guard-sweep-others.test.mjs",
71
+ "test": "export PATH=\"$HOME/.cargo/bin:$PATH\" && node --test --test-concurrency=1 test/integration/self-contained.test.mjs test/integration/browser-runner.test.mjs test/integration/fixture-client.test.mjs test/integration/guard-redirect-carrier.test.mjs test/integration/guard-redirect-session-end.test.mjs test/integration/guard-redirect-history.test.mjs test/integration/guard-redirect-iframe.test.mjs test/integration/guard-incomplete.test.mjs test/integration/guard-unparseable-wipe.test.mjs test/integration/guard-browser-ui.test.mjs test/integration/guard-snapshot-settle.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-exit-refusal.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.test.mjs test/integration/guard-bfcache-marker.test.mjs test/integration/guard-bfcache-history.test.mjs test/integration/guard-bfcache-review.test.mjs test/integration/guard-bfcache-quarantine.test.mjs test/integration/guard-error-page-navigation.test.mjs test/integration/guard-error-page-events.test.mjs test/integration/guard-world-paint.test.mjs test/integration/gates.test.mjs test/integration/validate.test.mjs test/integration/go.test.mjs test/integration/cargo.test.mjs test/integration/workflows.test.mjs test/integration/guard-sweep-union.test.mjs && node --test --test-concurrency=2 test/integration/guard-lifecycle.test.mjs test/integration/chromium-launch.test.mjs test/integration/guard-fit.test.mjs test/integration/guard-chain.test.mjs test/integration/guard-validation.test.mjs test/integration/guard-liveness.test.mjs test/integration/guard-observations.test.mjs test/integration/guard-child-latency.test.mjs test/integration/guard-adoption.test.mjs test/integration/guard-scoping.test.mjs test/integration/guard-late-pause.test.mjs test/integration/guard-legs.test.mjs test/integration/guard-fetch-enable.test.mjs test/integration/guard-diagnostics.test.mjs test/integration/guard-chrome.test.mjs test/integration/guard-worlds.test.mjs test/integration/guard-world-discovery.test.mjs test/integration/guard-world-guesses.test.mjs test/integration/guard-unparseable.test.mjs test/integration/guard-redirect-secret.test.mjs test/integration/guard-error-page.test.mjs test/integration/guard-discovery.test.mjs test/integration/guard-bfcache-chrome.test.mjs test/integration/guard-browser-page.test.mjs test/integration/guard-scales.test.mjs test/integration/guard-shared-log.test.mjs test/integration/guard-pre-beacon.test.mjs test/integration/guard-world-pause.test.mjs test/integration/guard-parallel.test.mjs test/integration/guard-client-exit.test.mjs test/integration/guard-runner.test.mjs test/integration/guard-sweep-ordinary.test.mjs test/integration/guard-sweep-seven.test.mjs test/integration/guard-sweep-vault.test.mjs test/integration/guard-sweep-vault-x.test.mjs test/integration/guard-sweep-apex.test.mjs test/integration/guard-sweep-others.test.mjs",
72
72
  "test:unit": "mkdir -p coverage/unit && node --experimental-test-coverage --test --test-concurrency=1 --test-reporter=lcov --test-reporter-destination=coverage/unit/lcov.info test/unit/*.test.mjs && go test -race -cover ./...",
73
73
  "build": "go build ./... && cargo build --all-features && cargo build --target wasm32-wasip1 --all-features && npm pack --dry-run",
74
74
  "prepublishOnly": "node scripts/set-crate-version.mjs \"$npm_package_version\"",
@@ -79,8 +79,8 @@
79
79
  "test:unit:guard": "mkdir -p coverage/unit && node --experimental-vm-modules --no-warnings --experimental-test-coverage --test --test-concurrency=1 --test-coverage-include=guard-runner.mjs --test-coverage-include=guard-driver.mjs --test-coverage-include=fixture-server.mjs --test-coverage-include=fixture-client.mjs --test-coverage-include=observer-target.mjs --test-coverage-include=frame-read.mjs --test-coverage-include=bfcache-restore.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"
80
80
  },
81
81
  "devDependencies": {
82
- "@ai-ecoverse/slicc-cdp": "1.41.17",
83
- "@ai-ecoverse/slicc-shared-web": "2.12.3",
82
+ "@ai-ecoverse/slicc-cdp": "1.42.3",
83
+ "@ai-ecoverse/slicc-shared-web": "2.13.4",
84
84
  "@biomejs/biome": "2.5.15",
85
85
  "ajv": "8.20.0",
86
86
  "ajv-formats": "3.0.1",
@@ -51,12 +51,79 @@ async function lockChecks({ client, session, targetId, result }) {
51
51
  result.closeRefused = refused(await outcome(client.send('Target.closeTarget', { targetId })));
52
52
  }
53
53
 
54
+ const discoveryMethods = ['Target.targetCreated', 'Target.targetInfoChanged'];
55
+ const errorPageURL = 'chrome-error://chromewebdata/';
56
+ const lower = (value) => String(value ?? '').toLowerCase();
57
+ const plain = (value) =>
58
+ lower(
59
+ String(value ?? '').replace(/%([0-9a-f]{2})/gi, (_, hex) =>
60
+ String.fromCharCode(Number.parseInt(hex, 16))
61
+ )
62
+ );
63
+ const unschemed = (value) => value.replace(/^[a-z][a-z\d+.-]*:\/\//, '');
64
+
65
+ const encoded = (url) =>
66
+ Array.from(
67
+ new TextEncoder().encode(url),
68
+ (byte) => `%${byte.toString(16).toUpperCase().padStart(2, '0')}`
69
+ ).join('');
70
+
71
+ const triggerURL = (ordinary, url) => `${ordinary}/page?k=deferred&defer=0&to=${encoded(url)}`;
72
+
73
+ function discoveryRules({ failedURL, tag, trigger }) {
74
+ const failed = new URL(failedURL);
75
+ const { origin, host, hostname } = failed;
76
+ const named = [hostname, host, origin];
77
+ const parts = [
78
+ failedURL,
79
+ `${failed.pathname}${failed.search}${failed.hash}`,
80
+ failed.pathname,
81
+ failed.search.slice(1),
82
+ failed.hash.slice(1),
83
+ tag,
84
+ ]
85
+ .filter((part) => part && part !== '/')
86
+ .map(plain);
87
+ const forms = (value) => [lower(value), plain(value)];
88
+ const leaks = (value) => forms(value).some((form) => parts.some((part) => form.includes(part)));
89
+ const names = (value) =>
90
+ forms(value).some((form) => form.includes(hostname)) || parseURL(value)?.origin === origin;
91
+ const cleanTitle = (title) => !leaks(title) && (!names(title) || named.includes(lower(title)));
92
+ const ownTitle = ({ url, title }) =>
93
+ plain(title) !== '' && [plain(url), unschemed(plain(url))].includes(plain(title));
94
+ const home = parseURL(trigger)?.href;
95
+ const clean = (info) => {
96
+ if (parseURL(info.url)?.href === home) return ownTitle(info) || cleanTitle(info.title);
97
+ return (
98
+ !leaks(info.url) && (lower(info.url) === origin || !names(info.url)) && cleanTitle(info.title)
99
+ );
100
+ };
101
+ const shows = ({ url, title }) =>
102
+ [origin, errorPageURL].includes(lower(url)) ||
103
+ (lower(url) === '' && named.includes(lower(title)));
104
+ return { clean, shows };
105
+ }
106
+
107
+ function eventsRedacted({ events, targetId, start, sent, ...failure }) {
108
+ const infos = (from) =>
109
+ events
110
+ .slice(from)
111
+ .filter(
112
+ (e) => discoveryMethods.includes(e.method) && e.params?.targetInfo?.targetId === targetId
113
+ )
114
+ .map((e) => e.params.targetInfo);
115
+ const { clean, shows } = discoveryRules(failure);
116
+ return infos(sent).some(shows) && infos(start).every(clean);
117
+ }
118
+
54
119
  async function redaction({
55
120
  client,
56
121
  targetId,
57
122
  failedURL,
58
- protectedOrigin,
123
+ ordinary,
124
+ tag,
59
125
  start,
126
+ sent,
60
127
  result,
61
128
  discovery,
62
129
  }) {
@@ -83,16 +150,16 @@ async function redaction({
83
150
  info.result?.targetInfo?.url === failedURL &&
84
151
  listed?.url === failedURL &&
85
152
  attachedInfo?.url === failedURL;
86
- const targetEvents = client.events
87
- .slice(start)
88
- .filter(
89
- (e) =>
90
- ['Target.targetCreated', 'Target.targetInfoChanged'].includes(e.method) &&
91
- e.params?.targetInfo?.targetId === targetId &&
92
- e.params.targetInfo.url.includes(new URL(protectedOrigin).host)
93
- );
94
153
  result.eventsRedacted = discovery
95
- ? targetEvents.length > 0 && targetEvents.every((e) => only(e.params.targetInfo))
154
+ ? eventsRedacted({
155
+ events: client.events,
156
+ targetId,
157
+ start,
158
+ sent,
159
+ failedURL,
160
+ tag,
161
+ trigger: triggerURL(ordinary, failedURL),
162
+ })
96
163
  : 'not-supported';
97
164
  }
98
165
 
@@ -120,19 +187,9 @@ async function seed(reader, observer, loaded, initial, allowed) {
120
187
  return out.ok ? out.result : `observer setup failed: ${out.error.message}`;
121
188
  }
122
189
 
123
- const encoded = (url) =>
124
- Array.from(
125
- new TextEncoder().encode(url),
126
- (byte) => `%${byte.toString(16).toUpperCase().padStart(2, '0')}`
127
- ).join('');
128
-
129
190
  async function scripted(client, session, ordinary, url) {
130
191
  const out = await outcome(
131
- client.send(
132
- 'Page.navigate',
133
- { url: `${ordinary}/page?k=deferred&defer=0&to=${encoded(url)}` },
134
- session
135
- )
192
+ client.send('Page.navigate', { url: triggerURL(ordinary, url) }, session)
136
193
  );
137
194
  return out.ok ? undefined : `client navigation failed: ${out.error.message}`;
138
195
  }
@@ -233,6 +290,7 @@ export async function protectedErrorPage({
233
290
  );
234
291
  }
235
292
  await outcome(reader.send('Page.bringToFront', {}, observer));
293
+ const sent = client.events.length;
236
294
  const unsent = await scripted(client, session, ordinary, failedURL);
237
295
  if (unsent) return missing(unsent);
238
296
  let evidence;
@@ -259,7 +317,17 @@ export async function protectedErrorPage({
259
317
  return missing(`missing error commit ${JSON.stringify(evidence)}`);
260
318
  result.live = live;
261
319
  if (!live) return { ...result, liveWhy: commitWhy(result.diagnostics.errorCommit) };
262
- await redaction({ client, targetId, failedURL, protectedOrigin, start, result, discovery });
320
+ await redaction({
321
+ client,
322
+ targetId,
323
+ failedURL,
324
+ ordinary,
325
+ tag,
326
+ start,
327
+ sent,
328
+ result,
329
+ discovery,
330
+ });
263
331
  const beforeReload = (await frame()).loaderId;
264
332
  result.reload = (await outcome(client.send('Page.reload', {}, session))).ok;
265
333
  if (result.reload) {
@@ -3,6 +3,7 @@ import { createObserverTarget } from './observer-target.mjs';
3
3
 
4
4
  const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
5
  const refused = (out) => !out.ok && /^slicc: refused:/.test(out.error?.message ?? '');
6
+ const rejected = new Set(['', 'about:blank', 'data:text/html,%3Ctitle%3Eblocked%3C%2Ftitle%3E']);
6
7
  const hosts = [
7
8
  ['exa mple.sliccy.ai', 'exa%20mple.sliccy.ai'],
8
9
  ['exa%20mple.sliccy.ai', 'exa%20mple.sliccy.ai'],
@@ -63,7 +64,7 @@ async function document(reader, targetId, expected, allowNativeRejection = false
63
64
  if (out.ok && value?.title === 'doc' && canonicalURL(value.href) === expected) return value;
64
65
  await pause(25);
65
66
  } while (Date.now() < deadline);
66
- if (allowNativeRejection && ['', 'about:blank', 'about:blank#blocked'].includes(targetURL))
67
+ if (allowNativeRejection && rejected.has(canonicalURL(targetURL)))
67
68
  return { nativeRejected: true, url: last?.url ?? '' };
68
69
  throw new Error(
69
70
  `unparseable: Chrome did not load a fixture document at ${expected}: ${JSON.stringify(last)}`