@ai-ecoverse/slicc-browsers 1.18.9 → 1.18.10
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 -0
- package/guard-runner.mjs +45 -10
- package/guard-vectors.json +37 -8
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -186,6 +186,8 @@ Each vector runs twice. The **control** runs it against an ordinary origin, wher
|
|
|
186
186
|
|
|
187
187
|
When a vector compares ordinary and protected fixture markers, those markers do not contain one another. Protected-marker checks still examine complete probe replies, including errors, and the vector's client events and messages, including encoded values.
|
|
188
188
|
|
|
189
|
+
Client-exit controls use the unguarded observer; persistent-capability refusals are valid evidence, granted instruments must leave no effects after exit, and missing answers remain incomplete.
|
|
190
|
+
|
|
189
191
|
### Running them
|
|
190
192
|
|
|
191
193
|
The reference runner works with any guard that speaks CDP:
|
package/guard-runner.mjs
CHANGED
|
@@ -159,10 +159,14 @@ class Execution {
|
|
|
159
159
|
|
|
160
160
|
async exit() {
|
|
161
161
|
const gone = this.client;
|
|
162
|
-
if (typeof gone.close !== 'function') throw new Error('the guarded client has no close()');
|
|
163
162
|
this.exitRequestStart = (await this.options.fixture.log()).length;
|
|
164
163
|
this.clients.exited = true;
|
|
165
|
-
|
|
164
|
+
if (gone === this.reader)
|
|
165
|
+
await gone.send('Target.detachFromTarget', { sessionId: this.sessions.page });
|
|
166
|
+
else {
|
|
167
|
+
if (typeof gone.close !== 'function') throw new Error('the guarded client has no close()');
|
|
168
|
+
await gone.close();
|
|
169
|
+
}
|
|
166
170
|
const released = await this.poll(async () => {
|
|
167
171
|
const read = await outcome(
|
|
168
172
|
this.reader.send('Target.getTargetInfo', { targetId: this.pageTarget })
|
|
@@ -664,8 +668,15 @@ class Execution {
|
|
|
664
668
|
refused,
|
|
665
669
|
...(gone ? { detached: true } : {}),
|
|
666
670
|
});
|
|
667
|
-
|
|
668
|
-
|
|
671
|
+
const unanswered =
|
|
672
|
+
step.refusable &&
|
|
673
|
+
exitsClient(this.vector) &&
|
|
674
|
+
!out.ok &&
|
|
675
|
+
!refused &&
|
|
676
|
+
/timeout|timed out|no answer/i.test(String(out.error?.message ?? out.error));
|
|
677
|
+
if (step.refusable) this.refused[step.refusal.name] = unanswered ? 'incomplete' : refused;
|
|
678
|
+
if (unanswered) this.incomplete.push(`${step.refusal.name}: no answer`);
|
|
679
|
+
else if (step.refusable && !out.ok && !refused && !gone)
|
|
669
680
|
this.errors.push(
|
|
670
681
|
`${step.refusal.name} failed without a refusal: ${String(out.error?.message ?? out.error)}`
|
|
671
682
|
);
|
|
@@ -881,10 +892,11 @@ class Execution {
|
|
|
881
892
|
};
|
|
882
893
|
const met = await this.poll(arrived, settle.ms ?? 1500, absent);
|
|
883
894
|
if (refused() && !alt) return skipped();
|
|
884
|
-
if (settle.required && !met)
|
|
885
|
-
this.
|
|
886
|
-
|
|
887
|
-
);
|
|
895
|
+
if (settle.required && !met) {
|
|
896
|
+
const missing = `settle: no ${settle.beacon ? `beacon from ${settle.beacon}` : settle.served ? `request for ${settle.served}` : settle.title ? `title ${settle.title}` : 'load'} within ${this.bound(settle.ms ?? 1500)}ms`;
|
|
897
|
+
if (exitsClient(this.vector)) this.incomplete.push(missing);
|
|
898
|
+
else this.errors.push(missing);
|
|
899
|
+
}
|
|
888
900
|
}
|
|
889
901
|
|
|
890
902
|
async guess(guess, session) {
|
|
@@ -926,7 +938,21 @@ class Execution {
|
|
|
926
938
|
session === undefined
|
|
927
939
|
)
|
|
928
940
|
params.browserContextId = this.vars.context;
|
|
929
|
-
const
|
|
941
|
+
const pending = outcome(this.actor().send(step.cdp, params, session));
|
|
942
|
+
let timer;
|
|
943
|
+
const out =
|
|
944
|
+
step.refusable && exitsClient(this.vector)
|
|
945
|
+
? await Promise.race([
|
|
946
|
+
pending,
|
|
947
|
+
new Promise((resolve) => {
|
|
948
|
+
timer = setTimeout(
|
|
949
|
+
() => resolve({ ok: false, error: { message: 'no answer' } }),
|
|
950
|
+
this.bound(this.options.namedResultMs ?? namedResultMs)
|
|
951
|
+
);
|
|
952
|
+
}),
|
|
953
|
+
])
|
|
954
|
+
: await pending;
|
|
955
|
+
clearTimeout(timer);
|
|
930
956
|
if (step.cdp === 'Target.setAutoAttach' && out.ok) {
|
|
931
957
|
if (params.autoAttach) this.attachSettings.set(session, true);
|
|
932
958
|
else this.attachSettings.delete(session);
|
|
@@ -1259,8 +1285,17 @@ class Execution {
|
|
|
1259
1285
|
observed[name] = 'not-supported';
|
|
1260
1286
|
continue;
|
|
1261
1287
|
}
|
|
1262
|
-
|
|
1288
|
+
const dependencies = observation.dependsOn ?? [];
|
|
1289
|
+
if (dependencies.some((dep) => this.refused[dep] === 'incomplete')) {
|
|
1290
|
+
observed[name] = 'incomplete';
|
|
1291
|
+
continue;
|
|
1292
|
+
}
|
|
1293
|
+
if (dependencies.some((dep) => this.refused[dep] === true)) continue;
|
|
1263
1294
|
observed[name] = await this.observe(name, observation.check);
|
|
1295
|
+
if (exitsClient(this.vector) && dependencies.length && observed[name] === false) {
|
|
1296
|
+
observed[name] = 'incomplete';
|
|
1297
|
+
this.incomplete.push(`${name}: pre-exit evidence not observed`);
|
|
1298
|
+
}
|
|
1264
1299
|
if (!observation.eitherWhen || this.side !== 'guarded') continue;
|
|
1265
1300
|
const holds = await this.either(observation.eitherWhen, observed[name], name, unseen);
|
|
1266
1301
|
observed[`${name}.either`] = holds;
|
package/guard-vectors.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"setVersion": "1.18.
|
|
3
|
+
"setVersion": "1.18.10",
|
|
4
4
|
"sources": [
|
|
5
5
|
{
|
|
6
6
|
"repo": "ai-ecoverse/slicc-cli",
|
|
@@ -18804,7 +18804,7 @@
|
|
|
18804
18804
|
"requires": ["clientExit"],
|
|
18805
18805
|
"setup": [
|
|
18806
18806
|
{
|
|
18807
|
-
"text": "a plain page
|
|
18807
|
+
"text": "a plain page registers an init script, an x-probe header and Fetch.enable '*' that fulfils the destination with a forged body, then loads a trigger page whose navigation into {target} is pending when the session exits; the control uses the unguarded observer, and guarded refusals waive only their corresponding pre-exit proofs"
|
|
18808
18808
|
},
|
|
18809
18809
|
{
|
|
18810
18810
|
"page": "tab",
|
|
@@ -18814,6 +18814,11 @@
|
|
|
18814
18814
|
"cdp": "Page.addScriptToEvaluateOnNewDocument",
|
|
18815
18815
|
"params": {
|
|
18816
18816
|
"source": "{beacon}"
|
|
18817
|
+
},
|
|
18818
|
+
"as": "initScript",
|
|
18819
|
+
"refusable": true,
|
|
18820
|
+
"refusal": {
|
|
18821
|
+
"match": "refused"
|
|
18817
18822
|
}
|
|
18818
18823
|
},
|
|
18819
18824
|
{
|
|
@@ -18822,6 +18827,11 @@
|
|
|
18822
18827
|
"headers": {
|
|
18823
18828
|
"x-probe": "{tag}"
|
|
18824
18829
|
}
|
|
18830
|
+
},
|
|
18831
|
+
"as": "extraHeaders",
|
|
18832
|
+
"refusable": true,
|
|
18833
|
+
"refusal": {
|
|
18834
|
+
"match": "refused"
|
|
18825
18835
|
}
|
|
18826
18836
|
},
|
|
18827
18837
|
{
|
|
@@ -18832,6 +18842,11 @@
|
|
|
18832
18842
|
"urlPattern": "*"
|
|
18833
18843
|
}
|
|
18834
18844
|
]
|
|
18845
|
+
},
|
|
18846
|
+
"as": "interception",
|
|
18847
|
+
"refusable": true,
|
|
18848
|
+
"refusal": {
|
|
18849
|
+
"match": "refused"
|
|
18835
18850
|
}
|
|
18836
18851
|
},
|
|
18837
18852
|
{
|
|
@@ -18860,7 +18875,12 @@
|
|
|
18860
18875
|
{
|
|
18861
18876
|
"settle": {
|
|
18862
18877
|
"beacon": "{ordinary}",
|
|
18863
|
-
"required": true
|
|
18878
|
+
"required": true,
|
|
18879
|
+
"dependsOn": ["initScript"],
|
|
18880
|
+
"or": {
|
|
18881
|
+
"served": "{ordinary}/page?k=href&tag={tag}&to={target}/canary/{tag}",
|
|
18882
|
+
"observer": "fixture"
|
|
18883
|
+
}
|
|
18864
18884
|
}
|
|
18865
18885
|
},
|
|
18866
18886
|
{
|
|
@@ -18880,7 +18900,8 @@
|
|
|
18880
18900
|
"targets": ["seven", "vault"],
|
|
18881
18901
|
"observe": {
|
|
18882
18902
|
"instrumented": {
|
|
18883
|
-
"means": "the
|
|
18903
|
+
"means": "the session's init script sent its beacon from {ordinary} before the exit unless registration was refused",
|
|
18904
|
+
"dependsOn": ["initScript"],
|
|
18884
18905
|
"check": {
|
|
18885
18906
|
"beacon": "{ordinary}",
|
|
18886
18907
|
"exit": "before",
|
|
@@ -18888,7 +18909,8 @@
|
|
|
18888
18909
|
}
|
|
18889
18910
|
},
|
|
18890
18911
|
"headed": {
|
|
18891
|
-
"means": "the trigger page's request carried the
|
|
18912
|
+
"means": "the trigger page's request carried the session's x-probe header before the exit unless registration was refused",
|
|
18913
|
+
"dependsOn": ["extraHeaders"],
|
|
18892
18914
|
"check": {
|
|
18893
18915
|
"header": "x-probe",
|
|
18894
18916
|
"on": "/page",
|
|
@@ -18897,7 +18919,8 @@
|
|
|
18897
18919
|
}
|
|
18898
18920
|
},
|
|
18899
18921
|
"intercepted": {
|
|
18900
|
-
"means": "a Fetch.requestPaused for the trigger page reached the
|
|
18922
|
+
"means": "a Fetch.requestPaused for the trigger page reached the session before the exit unless interception was refused",
|
|
18923
|
+
"dependsOn": ["interception"],
|
|
18901
18924
|
"check": {
|
|
18902
18925
|
"event": "Fetch.requestPaused",
|
|
18903
18926
|
"where": {
|
|
@@ -18914,6 +18937,7 @@
|
|
|
18914
18937
|
},
|
|
18915
18938
|
"served": {
|
|
18916
18939
|
"means": "the stand-in served {target}/canary/{tag} after the exit",
|
|
18940
|
+
"dependsOn": [],
|
|
18917
18941
|
"check": {
|
|
18918
18942
|
"served": "{target}/canary/{tag}",
|
|
18919
18943
|
"exit": "after",
|
|
@@ -18922,6 +18946,7 @@
|
|
|
18922
18946
|
},
|
|
18923
18947
|
"document": {
|
|
18924
18948
|
"means": "the unguarded observer reads {target}'s real document, title 'canary'",
|
|
18949
|
+
"dependsOn": [],
|
|
18925
18950
|
"check": {
|
|
18926
18951
|
"title": "canary",
|
|
18927
18952
|
"via": "unguarded",
|
|
@@ -18930,6 +18955,7 @@
|
|
|
18930
18955
|
},
|
|
18931
18956
|
"forged": {
|
|
18932
18957
|
"means": "the unguarded observer reads the client's forged FULFILLED body",
|
|
18958
|
+
"dependsOn": [],
|
|
18933
18959
|
"check": {
|
|
18934
18960
|
"expression": "document.body ? document.body.innerText.includes('FULFILLED') : false",
|
|
18935
18961
|
"equals": true,
|
|
@@ -18939,6 +18965,7 @@
|
|
|
18939
18965
|
},
|
|
18940
18966
|
"scripted": {
|
|
18941
18967
|
"means": "the client's init script sent a beacon from {target} after the exit",
|
|
18968
|
+
"dependsOn": [],
|
|
18942
18969
|
"check": {
|
|
18943
18970
|
"beacon": "{target}",
|
|
18944
18971
|
"exit": "after",
|
|
@@ -18947,6 +18974,7 @@
|
|
|
18947
18974
|
},
|
|
18948
18975
|
"probed": {
|
|
18949
18976
|
"means": "a request for {target}/canary/{tag} carried the client's x-probe header after the exit",
|
|
18977
|
+
"dependsOn": [],
|
|
18950
18978
|
"check": {
|
|
18951
18979
|
"header": "x-probe",
|
|
18952
18980
|
"on": "{target}/canary/{tag}",
|
|
@@ -18956,6 +18984,7 @@
|
|
|
18956
18984
|
},
|
|
18957
18985
|
"paused": {
|
|
18958
18986
|
"means": "a Fetch.requestPaused for {target}/canary/{tag} reached the client",
|
|
18987
|
+
"dependsOn": [],
|
|
18959
18988
|
"check": {
|
|
18960
18989
|
"event": "Fetch.requestPaused",
|
|
18961
18990
|
"where": {
|
|
@@ -18969,10 +18998,10 @@
|
|
|
18969
18998
|
}
|
|
18970
18999
|
}
|
|
18971
19000
|
},
|
|
18972
|
-
"live": "the stand-in serves {target}'s document after the
|
|
19001
|
+
"live": "the stand-in serves {target}'s document after the session exits",
|
|
18973
19002
|
"control": {
|
|
18974
19003
|
"origin": "ordinary",
|
|
18975
|
-
"via": "
|
|
19004
|
+
"via": "unguarded",
|
|
18976
19005
|
"expected": {
|
|
18977
19006
|
"instrumented": true,
|
|
18978
19007
|
"headed": true,
|
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.10",
|
|
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-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-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/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/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-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/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/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=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.31.
|
|
83
|
-
"@ai-ecoverse/slicc-shared-web": "2.
|
|
82
|
+
"@ai-ecoverse/slicc-cdp": "1.31.9",
|
|
83
|
+
"@ai-ecoverse/slicc-shared-web": "2.10.4",
|
|
84
84
|
"@biomejs/biome": "2.5.15",
|
|
85
85
|
"ajv": "8.20.0",
|
|
86
86
|
"ajv-formats": "3.0.1",
|