@ai-ecoverse/slicc-browsers 1.18.4 → 1.18.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 +168 -608
- package/bfcache-restore.mjs +44 -6
- package/guard-vectors.json +1 -1
- package/package.json +3 -3
- package/redirect-secret.mjs +34 -7
package/bfcache-restore.mjs
CHANGED
|
@@ -159,14 +159,51 @@ class Restore {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
async trigger() {
|
|
162
|
+
const method = 'Page.navigateToHistoryEntry';
|
|
162
163
|
const params = { entryId: this.entry.id };
|
|
163
|
-
|
|
164
|
-
this.
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
if (this.mode !== 'history') {
|
|
165
|
+
this.result.triggerPath = 'observer';
|
|
166
|
+
this.result.trigger = await this.read(method, params);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const command = { id: 'history-trigger', method, params, sentAt: Date.now() };
|
|
170
|
+
const work = this.client.send(method, params, this.session);
|
|
171
|
+
void outcome(work).then((reply) => {
|
|
172
|
+
this.result.triggerReply = reply;
|
|
173
|
+
if (reply.ok && this.result.triggerPath === 'observer-after-audited-timeout') {
|
|
174
|
+
this.result.triggerPath = 'client-late-answer';
|
|
175
|
+
this.missingEvidence('history navigation answered after non-forwarding audit');
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
const out = await bounded(work, this.budget);
|
|
167
179
|
this.result.trigger = out;
|
|
168
|
-
|
|
169
|
-
|
|
180
|
+
const timedOut = out.error?.message === 'no answer';
|
|
181
|
+
this.result.triggerPath = timedOut ? 'client-timeout' : 'client';
|
|
182
|
+
let refused = refusal(out);
|
|
183
|
+
if (timedOut && this.forwardAudit) {
|
|
184
|
+
const audit = await bounded(
|
|
185
|
+
typeof this.gate.audit === 'function'
|
|
186
|
+
? Promise.resolve().then(() =>
|
|
187
|
+
this.gate.audit({ historyTrigger: command, probes: [], final: false })
|
|
188
|
+
)
|
|
189
|
+
: Promise.reject(new Error('forwarding audit unavailable')),
|
|
190
|
+
this.budget
|
|
191
|
+
);
|
|
192
|
+
this.result.triggerAudit = audit;
|
|
193
|
+
const decision = audit.result?.historyTrigger;
|
|
194
|
+
refused =
|
|
195
|
+
audit.ok &&
|
|
196
|
+
decision?.complete === true &&
|
|
197
|
+
decision.id === command.id &&
|
|
198
|
+
decision.forwarded === false &&
|
|
199
|
+
!(await bounded(work, 0)).ok;
|
|
200
|
+
}
|
|
201
|
+
if (refused) {
|
|
202
|
+
this.result.triggerPath = timedOut
|
|
203
|
+
? 'observer-after-audited-timeout'
|
|
204
|
+
: 'observer-after-refusal';
|
|
205
|
+
this.result.fallback = await this.read(method, params);
|
|
206
|
+
}
|
|
170
207
|
}
|
|
171
208
|
|
|
172
209
|
async probe(method, params, current) {
|
|
@@ -243,6 +280,7 @@ class Restore {
|
|
|
243
280
|
this.messageStart = this.client.messages?.length ?? 0;
|
|
244
281
|
this.result.delay.startedAt = Date.now();
|
|
245
282
|
const trigger = this.trigger();
|
|
283
|
+
if (this.mode === 'history') await trigger;
|
|
246
284
|
const restored = await waitFor(async () => {
|
|
247
285
|
const f = await this.frame();
|
|
248
286
|
return f?.url === this.url && !f.unreachableUrl && (await this.state())?.persisted === true;
|
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.18.
|
|
3
|
+
"version": "1.18.6",
|
|
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 ./... && golangci-lint run && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings",
|
|
70
70
|
"format": "biome check --write . && gofmt -w . && cargo fmt",
|
|
71
|
-
"test": "npm run validate && 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/git-retry.test.mjs test/integration/release-line.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-incomplete.test.mjs test/integration/guard-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.test.mjs test/integration/guard-bfcache-review.test.mjs test/integration/guard-bfcache-quarantine.test.mjs test/integration/guard-error-page-navigation.test.mjs && go test -race -count=1 ./test/integration/... && cargo test --all-features",
|
|
71
|
+
"test": "npm run validate && 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/git-retry.test.mjs test/integration/release-line.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-stall.test.mjs test/integration/guard-forged.test.mjs test/integration/guard-exit-steps.test.mjs test/integration/guard-frame-read.test.mjs test/integration/guard-bfcache.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 && go test -race -count=1 ./test/integration/... && cargo test --all-features",
|
|
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
|
"trust": "npx fledgling sync -y --skip-publish",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"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=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"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
|
-
"@ai-ecoverse/slicc-shared-web": "2.7.
|
|
86
|
+
"@ai-ecoverse/slicc-shared-web": "2.7.9",
|
|
87
87
|
"@biomejs/biome": "2.5.15",
|
|
88
88
|
"ajv": "8.20.0",
|
|
89
89
|
"ajv-formats": "3.0.1",
|
package/redirect-secret.mjs
CHANGED
|
@@ -668,14 +668,38 @@ class RedirectProbe {
|
|
|
668
668
|
await this.send('Runtime.evaluate', {
|
|
669
669
|
expression: `(()=>{const f=document.createElement('iframe');f.src=${JSON.stringify(url)};document.body.append(f)})();void 0`,
|
|
670
670
|
});
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
671
|
+
const control = this.side === 'control';
|
|
672
|
+
const ms = Math.round(5000 * this.scale);
|
|
673
|
+
let snapshot;
|
|
674
|
+
let dom;
|
|
675
|
+
let ordinaryIframeSnapshot = false;
|
|
676
|
+
let ordinaryIframeDOM = false;
|
|
677
|
+
const read = async () => {
|
|
678
|
+
await this.send('Page.getFrameTree');
|
|
679
|
+
snapshot = await this.send('Page.captureSnapshot', { format: 'mhtml' });
|
|
680
|
+
dom = await this.send('DOMSnapshot.captureSnapshot', { computedStyles: [] });
|
|
681
|
+
ordinaryIframeSnapshot ||= snapshot.ok && this.contains(snapshot);
|
|
682
|
+
ordinaryIframeDOM ||= dom.ok && this.contains(dom);
|
|
683
|
+
return this.ended() || (ordinaryIframeSnapshot && ordinaryIframeDOM);
|
|
684
|
+
};
|
|
685
|
+
if (control) {
|
|
686
|
+
if (!(await waitFor(read, ms, 50))) {
|
|
687
|
+
const missing = [
|
|
688
|
+
!ordinaryIframeSnapshot && 'ordinaryIframeSnapshot',
|
|
689
|
+
!ordinaryIframeDOM && 'ordinaryIframeDOM',
|
|
690
|
+
].filter(Boolean);
|
|
691
|
+
this.missing.push(
|
|
692
|
+
`redirect-secret: control iframe ${missing.join(', ')} not observed within ${ms}ms`
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
} else {
|
|
696
|
+
await pause(500);
|
|
697
|
+
await read();
|
|
698
|
+
}
|
|
675
699
|
this.responses.push({
|
|
676
700
|
iframeSnapshotAttempted: !snapshot.ended && !dom.ended,
|
|
677
|
-
ordinaryIframeSnapshot:
|
|
678
|
-
ordinaryIframeDOM:
|
|
701
|
+
ordinaryIframeSnapshot: ordinaryIframeSnapshot || (control ? 'incomplete' : false),
|
|
702
|
+
ordinaryIframeDOM: ordinaryIframeDOM || (control ? 'incomplete' : false),
|
|
679
703
|
});
|
|
680
704
|
}
|
|
681
705
|
|
|
@@ -750,7 +774,10 @@ class RedirectProbe {
|
|
|
750
774
|
extraHeld &&
|
|
751
775
|
(extra.length >= this.destinations.length * 4 || (this.cut() ? 'incomplete' : false));
|
|
752
776
|
const unseen = this.end || this.halted ? 'incomplete' : false;
|
|
753
|
-
const found = (key) =>
|
|
777
|
+
const found = (key) => {
|
|
778
|
+
const values = this.responses.map((entry) => entry[key]);
|
|
779
|
+
return values.includes(true) || (values.includes('incomplete') ? 'incomplete' : false);
|
|
780
|
+
};
|
|
754
781
|
const fetchReads = this.answers('Fetch.getResponseBody');
|
|
755
782
|
const fetchLeaked = fetchReads.answered.some((entry) => !refused(entry));
|
|
756
783
|
const evidence = {
|