@ai-ecoverse/slicc-browsers 1.10.0 → 1.10.1
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 +7 -7
- package/guard-driver.mjs +11 -2
- package/guard-runner.mjs +5 -1
- package/guard-vectors.json +3 -4
- package/package.json +1 -1
- package/schema/guard-vectors.schema.json +2 -6
- package/unparseable-url.mjs +42 -10
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ The `C9` foreign extension isolated-world family requires `extensionWorld`: a br
|
|
|
182
182
|
|
|
183
183
|
The integration launcher loads shared-web 1.25.2’s harness extension exactly once through `Extensions.loadUnpacked` (`ankmbodmgimjemnojmeddfcdlbocolob`), whose content script runs on HTTP(S) pages. shared-web 1.25.2 also loads it by default in its own harness. A transport unable to host a foreign content-script extension omits `extensionWorld` and reports `not-supported:extensionWorld`; an advertised capability with no observed world fails, rather than silently skipping.
|
|
184
184
|
|
|
185
|
-
`C8.unparseable` probes both raw and canonical space/U+200D/invalid-punycode URLs through creation and navigation. Every guarded destination needs a matched refusal. Its unguarded controls record `document` or `native-rejection` outcomes, including Chrome builds that reject raw U+200D instead of committing it. Both raw and encoded-space forms must commit real fixture documents, because that is the proven cookie leak. An independent observer loads the encoded-space document and confirms it shares a uniquely seeded `.sliccy.ai` cookie; the guarded client must neither list nor attach to it, read its cookie, query that cookie by URL, nor cause a cookie-bearing destination request. The guard does not erase the observer’s private browser state. The row
|
|
185
|
+
`C8.unparseable` probes both raw and canonical space/U+200D/invalid-punycode URLs through creation and navigation. Every guarded destination needs a matched refusal. Its unguarded controls record `document` or `native-rejection` outcomes, including Chrome builds that reject raw U+200D instead of committing it. Both raw and encoded-space forms must commit real fixture documents, because that is the proven cookie leak. An independent observer loads the encoded-space document and confirms it shares a uniquely seeded `.sliccy.ai` cookie; the guarded client must neither list nor attach to it, read its cookie, query that cookie by URL, nor cause a cookie-bearing destination request. The guard does not erase the observer’s private browser state. The row runs on both browser-session and page-session-only transports, uses the default context, and cleans up its own targets and cookie. Cookie seeding always uses the unguarded observer’s browser session, never the guarded client. Only guarded browser-session target listing requires `browserSession`; without it, `listed` records `not-supported` explicitly in probe and observation evidence, and the result lists that field in `notSupported`. Creation, navigation, attachment and `Network.getCookies` with `urls` still require matched refusals; filtering cookies without refusing fails `cookieRefused`. Fixture URLs omit the colon when the port is empty, and reported URLs are compared after canonicalisation, including Chrome’s WHATWG-unparseable encoded-space hosts. Launchers must resolve the raw/canonical space, U+200D/punycode and invalid-punycode `.sliccy.ai` fixture hosts to the fixture server, including for default-port HTTP or HTTPS origins. The HTTP fixture accepts Chrome’s encoded-space Host header without reparsing it as a WHATWG origin.
|
|
186
186
|
|
|
187
187
|
`C8.chrome-search` requests `chrome-search://most-visited/` through `Page.navigate` and requires an explicit guard refusal. Its unguarded control requires a CDP response, not a successful document commit: Chrome can answer with `errorText` when that internal data source is unavailable outside an Instant process. Browser-side load failure is not a guard refusal, and merely hiding the target must fail this row.
|
|
188
188
|
|
|
@@ -206,8 +206,8 @@ A client may register `Fetch.enable` patterns naming a protected origin in eithe
|
|
|
206
206
|
- `scope`: `any`, or the one guard the vector is for;
|
|
207
207
|
- `setup` and `trigger`, both lists of steps;
|
|
208
208
|
- `targets`: the protected or reachability targets it runs against;
|
|
209
|
-
- `requires?`: the capabilities
|
|
210
|
-
- `observe?`: each observed name, as `{ "means", "dependsOn"?, "requires"?, "eitherWhen"? }`. `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);
|
|
209
|
+
- `requires?`: the capabilities needed for the whole vector, including its steps' `requires`. Observation-only capabilities may be omitted here to keep independent legs runnable. A runner whose transport lacks one reports the vector as `skip` with the reason `not-supported:<capability>`. A row with a `page-or-browser` cookie step that would carry a `browserContextId` reports `not-supported:browserSession` without a browser session, because that parameter is only allowed on the browser target;
|
|
210
|
+
- `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);
|
|
211
211
|
- `live`: the liveness check;
|
|
212
212
|
- `control` (`origin: "ordinary"`, `via`, `expected`) and `expected`: an object of names and booleans, or text;
|
|
213
213
|
- `skip?` (`target`, `reason`): `reason` starts with a code, as in `not-applicable: …`.
|
|
@@ -247,7 +247,7 @@ Popups are observed by `Target.targetCreated` with the opener's `openerId`, or b
|
|
|
247
247
|
|
|
248
248
|
`{ "extensionWorld": "hidden" | "select" | "debugger" | "drivable", "as": "world", "requires": ["extensionWorld"] }` runs the C9 compound probe on the current ordinary page. The runner attaches an independent observer to discover actual context ids, unique ids and names, and sends probes through the client session. Discovery matches the expected fixture extension’s exact origin, never the first extension world: `run()`’s optional `extensionWorldId` defaults to shared-web’s harness id `ankmbodmgimjemnojmeddfcdlbocolob`. Multiple unrelated content-script worlds are ignored for fixture selection, while guards must still protect foreign worlds. A custom fixture must provide the same content-script marker contract. Object handles are minted and validated on the unguarded control session, then replayed as guesses on the guarded leg; Chrome object handles and DOM node ids are session-local, so observer handles must never be mistaken for client handles. Numeric ids from the control are also replayed alongside fresh observer-discovered selectors, but numeric ids are local to a tab: a control id may name the next tab’s exposed default context, which must work and return no extension marker. The same-tab hidden world’s actual numeric id and every extension unique-id guess still require a matched refusal. Unknown numeric ids and stale object handles (`not exposed`) need explicit refusals rather than Chrome’s invalid-id errors. `guessesRefused` aggregates only required refusals; `guessesReached` must be false on the guarded leg, and `exposedContextsWork` must remain true. Its result contains boolean evidence consumed by ordinary `result` checks. The unguarded leg validates those selectors against Chrome itself before guarded evidence can pass.
|
|
249
249
|
|
|
250
|
-
`{ "unparseable": true, "as": "unparseable"
|
|
250
|
+
`{ "unparseable": true, "as": "unparseable" }` runs the C8 malformed-URL compound probe on the current ordinary page: independent browser ground truth, raw/canonical destination commands, discovery/attach and cookie checks. Boolean evidence is consumed by ordinary `result` checks, with native Chrome outcomes retained in its diagnostics.
|
|
251
251
|
|
|
252
252
|
**The control rule.** Every vector has a control on an ordinary origin: the same setup and trigger against an unprotected origin must succeed. A missing or failing control fails the id. With `via: "guard"`, the control runs through the guard. With `via: "unguarded"`, the runner runs setup and trigger through its own CDP against the ordinary origin and against the protected stand-in, and both must show the effect. The runner reports the control's transport as `via`.
|
|
253
253
|
|
|
@@ -281,7 +281,7 @@ It exports:
|
|
|
281
281
|
- `refusals(vector)`: the vector's refusal names;
|
|
282
282
|
- `outcome`, `call(client, step, sessionId)` and `isRefusal(outcome, step)`;
|
|
283
283
|
- `instrument(client, sessionId, log, tag)`, `load(client, sessionId, url)` and `popup(client, openerId)`;
|
|
284
|
-
- `judge(vector, side, observed)`: `{ pass, refused, skipped, mismatches }`, where `refused` lists the leg's refusals and `skipped` the observations that depend on them;
|
|
284
|
+
- `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;
|
|
285
285
|
- `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;
|
|
286
286
|
- `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 …`.
|
|
287
287
|
|
|
@@ -294,7 +294,7 @@ It exports:
|
|
|
294
294
|
- a skip names one of the vector's targets;
|
|
295
295
|
- `observe` defines every expected name;
|
|
296
296
|
- in a vector with a refusable step, every observation, except an `as` of a step before the first refusable one, has `dependsOn`, and each name in it is a refusable step's refusal name;
|
|
297
|
-
- every `Target.setAutoAttach`, `children`, child-session, browser-session and browser-context step has the matching `requires`, and a vector's `requires`
|
|
297
|
+
- every `Target.setAutoAttach`, `children`, child-session, browser-session and browser-context step has the matching `requires`, and a vector's `requires` includes its steps' requirements; observation-only requirements can remain local;
|
|
298
298
|
- every `{name}` is in `placeholders`;
|
|
299
299
|
- no URL uses a port from 9222 to 9229.
|
|
300
300
|
|
|
@@ -362,7 +362,7 @@ With the `browserContexts` capability each leg runs in its own browser context.
|
|
|
362
362
|
|
|
363
363
|
`onStart(row)` receives each row's `id`, `target`, `variant`, `transport` and `via` as the row starts. `run()` returns a promise that also has `inFlight()`, and its result has `inFlight` too. `inFlight()` lists the rows that are running now, each as `{ id, target, variant, leg, phase, step, clients }`, where `step` is the index of the current step in its `phase` (`setup`, `trigger` or `child`), and `clients` lists each client's pending CDP calls as `{ method, session, ageMs }` (`client`, plus `unguarded` when it is a separate client). With `rowTimeoutMs`, a row still running after that many milliseconds fails on its own. Its `errors` hold `row timed out after <ms>ms: <the in-flight view>`, `inFlight` holds the view, and the clients that `clientFactory` made for it are closed. The run then goes on to the next row. It is off by default, and a client passed as `client` is never closed.
|
|
364
364
|
|
|
365
|
-
`log(row)` receives completed rows. Besides `control`, `guarded`, `live`, `why` and `errors`, a row may carry `ran` (steps that ran on one leg only) and `notes` (settles skipped because their registration was refused). Both are informational: they explain a row's verdict, and adopters (bios, slicc-cli, slicc-cdp) must not count them as failures, or as passes. The verdict is `control`, `guarded` and `live`. A variant supplies the wait before an
|
|
365
|
+
`log(row)` receives completed rows. Besides `control`, `guarded`, `live`, `why` and `errors`, a row may carry `notSupported` (unavailable observation fields), `ran` (steps that ran on one leg only) and `notes` (settles skipped because their registration was refused). Both are informational: they explain a row's verdict, and adopters (bios, slicc-cli, slicc-cdp) must not count them as failures, or as passes. The verdict is `control`, `guarded` and `live`. A variant supplies the wait before an
|
|
366
366
|
explicit `Target.detachFromTarget` step and for a `settle.variant` step.
|
|
367
367
|
|
|
368
368
|
Every runnable vector has a declarative `liveCheck`; every observation has a
|
package/guard-driver.mjs
CHANGED
|
@@ -139,8 +139,10 @@ export function judge(vector, side, observed) {
|
|
|
139
139
|
const mismatches = observed === true ? [] : [want];
|
|
140
140
|
return { pass: mismatches.length === 0, refused, skipped: [], mismatches };
|
|
141
141
|
}
|
|
142
|
-
const skipped = Object.keys(want).filter(
|
|
143
|
-
(
|
|
142
|
+
const skipped = Object.keys(want).filter(
|
|
143
|
+
(k) =>
|
|
144
|
+
(observed?.[k] === 'not-supported' && vector.observe?.[k]?.requires?.length > 0) ||
|
|
145
|
+
(vector.observe?.[k]?.dependsOn ?? []).some((d) => refused.includes(d))
|
|
144
146
|
);
|
|
145
147
|
const mismatches = Object.entries(want)
|
|
146
148
|
.filter(
|
|
@@ -227,6 +229,13 @@ export function result(vector, run) {
|
|
|
227
229
|
guarded,
|
|
228
230
|
live,
|
|
229
231
|
...(Object.keys(ran).length ? { ran } : {}),
|
|
232
|
+
...(Object.values(run.guarded ?? {}).includes('not-supported')
|
|
233
|
+
? {
|
|
234
|
+
notSupported: Object.keys(run.guarded).filter(
|
|
235
|
+
(name) => run.guarded[name] === 'not-supported'
|
|
236
|
+
),
|
|
237
|
+
}
|
|
238
|
+
: {}),
|
|
230
239
|
...(why.length ? { why: why.join('; ') } : {}),
|
|
231
240
|
};
|
|
232
241
|
}
|
package/guard-runner.mjs
CHANGED
|
@@ -664,6 +664,7 @@ class Execution {
|
|
|
664
664
|
origin: this.vars.ordinary,
|
|
665
665
|
tag: this.vars.tag,
|
|
666
666
|
fixture: this.options.fixture,
|
|
667
|
+
browserSession: supported(['browserSession'], this.options.capabilities),
|
|
667
668
|
})
|
|
668
669
|
);
|
|
669
670
|
if (step.extensionWorld)
|
|
@@ -1086,7 +1087,10 @@ class Execution {
|
|
|
1086
1087
|
await sleep(100);
|
|
1087
1088
|
const observed = { ...this.refused };
|
|
1088
1089
|
for (const [name, observation] of Object.entries(this.vector.observe ?? {})) {
|
|
1089
|
-
if (!supported(observation.requires, this.options.capabilities))
|
|
1090
|
+
if (!supported(observation.requires, this.options.capabilities)) {
|
|
1091
|
+
observed[name] = 'not-supported';
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1090
1094
|
if ((observation.dependsOn ?? []).some((dep) => this.refused[dep])) continue;
|
|
1091
1095
|
observed[name] = await this.observe(name, observation.check);
|
|
1092
1096
|
if (observation.eitherWhen && this.side === 'guarded')
|
package/guard-vectors.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"setVersion": "1.10.
|
|
3
|
+
"setVersion": "1.10.1",
|
|
4
4
|
"sources": [
|
|
5
5
|
{
|
|
6
6
|
"repo": "ai-ecoverse/slicc-cli",
|
|
@@ -133,12 +133,10 @@
|
|
|
133
133
|
"trigger": [
|
|
134
134
|
{
|
|
135
135
|
"unparseable": true,
|
|
136
|
-
"as": "unparseable"
|
|
137
|
-
"requires": ["browserSession"]
|
|
136
|
+
"as": "unparseable"
|
|
138
137
|
}
|
|
139
138
|
],
|
|
140
139
|
"targets": ["ordinary"],
|
|
141
|
-
"requires": ["browserSession"],
|
|
142
140
|
"observe": {
|
|
143
141
|
"live": {
|
|
144
142
|
"means": "unparseable URL protection live",
|
|
@@ -169,6 +167,7 @@
|
|
|
169
167
|
},
|
|
170
168
|
"listed": {
|
|
171
169
|
"means": "unparseable URL protection listed",
|
|
170
|
+
"requires": ["browserSession"],
|
|
172
171
|
"check": {
|
|
173
172
|
"result": "unparseable",
|
|
174
173
|
"path": "result.listed",
|
package/package.json
CHANGED
|
@@ -430,7 +430,7 @@
|
|
|
430
430
|
{
|
|
431
431
|
"type": "object",
|
|
432
432
|
"additionalProperties": false,
|
|
433
|
-
"required": ["unparseable", "as"
|
|
433
|
+
"required": ["unparseable", "as"],
|
|
434
434
|
"properties": {
|
|
435
435
|
"unparseable": {
|
|
436
436
|
"const": true
|
|
@@ -439,11 +439,7 @@
|
|
|
439
439
|
"$ref": "#/$defs/name"
|
|
440
440
|
},
|
|
441
441
|
"requires": {
|
|
442
|
-
"$ref": "#/$defs/requires"
|
|
443
|
-
"type": "array",
|
|
444
|
-
"contains": {
|
|
445
|
-
"const": "browserSession"
|
|
446
|
-
}
|
|
442
|
+
"$ref": "#/$defs/requires"
|
|
447
443
|
}
|
|
448
444
|
}
|
|
449
445
|
},
|
package/unparseable-url.mjs
CHANGED
|
@@ -10,7 +10,25 @@ const hosts = [
|
|
|
10
10
|
['xn--a.sliccy.ai', 'xn--a.sliccy.ai'],
|
|
11
11
|
];
|
|
12
12
|
|
|
13
|
+
export const rootURL = (root, host, path) =>
|
|
14
|
+
`${root.protocol}//${host}${root.port ? `:${root.port}` : ''}${path}`;
|
|
15
|
+
|
|
16
|
+
export function canonicalURL(value) {
|
|
17
|
+
try {
|
|
18
|
+
return new URL(value).href;
|
|
19
|
+
} catch {
|
|
20
|
+
const match = /^(https?:)\/\/([^/?#]+)(.*)$/.exec(value);
|
|
21
|
+
if (!match) return value;
|
|
22
|
+
const [, protocol, authority, path] = match;
|
|
23
|
+
const host = authority.replace(/:\d*$/, '').replaceAll(' ', '%20');
|
|
24
|
+
const port = /:(\d*)$/.exec(authority)?.[1];
|
|
25
|
+
const parsed = new URL(`${protocol}//example.test${port ? `:${port}` : ''}${path}`);
|
|
26
|
+
return rootURL(parsed, host, `${parsed.pathname}${parsed.search}${parsed.hash}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
async function document(reader, targetId, expected, allowNativeRejection = false) {
|
|
31
|
+
expected = canonicalURL(expected);
|
|
14
32
|
const { sessionId } = await reader.send('Target.attachToTarget', { targetId, flatten: true });
|
|
15
33
|
try {
|
|
16
34
|
const deadline = Date.now() + 5000;
|
|
@@ -19,7 +37,7 @@ async function document(reader, targetId, expected, allowNativeRejection = false
|
|
|
19
37
|
do {
|
|
20
38
|
const { targetInfo } = await reader.send('Target.getTargetInfo', { targetId });
|
|
21
39
|
targetURL = targetInfo.url;
|
|
22
|
-
if (targetInfo.url !== expected) {
|
|
40
|
+
if (canonicalURL(targetInfo.url) !== expected) {
|
|
23
41
|
last = { url: targetInfo.url, title: targetInfo.title };
|
|
24
42
|
await pause(25);
|
|
25
43
|
continue;
|
|
@@ -41,7 +59,7 @@ async function document(reader, targetId, expected, allowNativeRejection = false
|
|
|
41
59
|
);
|
|
42
60
|
const value = out.result?.result?.value;
|
|
43
61
|
last = value ?? out;
|
|
44
|
-
if (out.ok && value?.title === 'doc' && value.href === expected) return value;
|
|
62
|
+
if (out.ok && value?.title === 'doc' && canonicalURL(value.href) === expected) return value;
|
|
45
63
|
await pause(25);
|
|
46
64
|
} while (Date.now() < deadline);
|
|
47
65
|
if (allowNativeRejection && ['', 'about:blank', 'about:blank#blocked'].includes(targetURL))
|
|
@@ -75,10 +93,10 @@ async function destinations({ client, reader, root, tag, targets, session, targe
|
|
|
75
93
|
out.error?.code === -32000 && /invalid URL/i.test(out.error?.message ?? '');
|
|
76
94
|
for (const [index, [input, canonical]] of hosts.entries()) {
|
|
77
95
|
const strict = canonical === 'exa%20mple.sliccy.ai';
|
|
78
|
-
const base =
|
|
96
|
+
const base = rootURL(root, canonical, `/doc?n=${tag}`);
|
|
79
97
|
const created = await outcome(
|
|
80
98
|
client.send('Target.createTarget', {
|
|
81
|
-
url:
|
|
99
|
+
url: rootURL(root, input, `/doc?n=${tag}-create-${index}`),
|
|
82
100
|
background: true,
|
|
83
101
|
})
|
|
84
102
|
);
|
|
@@ -95,7 +113,7 @@ async function destinations({ client, reader, root, tag, targets, session, targe
|
|
|
95
113
|
const navigated = await outcome(
|
|
96
114
|
client.send(
|
|
97
115
|
'Page.navigate',
|
|
98
|
-
{ url:
|
|
116
|
+
{ url: rootURL(root, input, `/doc?n=${tag}-navigate-${index}`) },
|
|
99
117
|
session
|
|
100
118
|
)
|
|
101
119
|
);
|
|
@@ -116,7 +134,7 @@ async function destinations({ client, reader, root, tag, targets, session, targe
|
|
|
116
134
|
await outcome(
|
|
117
135
|
client.send(
|
|
118
136
|
'Page.navigate',
|
|
119
|
-
{ url:
|
|
137
|
+
{ url: rootURL(root, root.hostname, `/doc?n=${tag}-ordinary-${index}`) },
|
|
120
138
|
session
|
|
121
139
|
)
|
|
122
140
|
);
|
|
@@ -124,7 +142,16 @@ async function destinations({ client, reader, root, tag, targets, session, targe
|
|
|
124
142
|
return result;
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
export async function unparseableURLs({
|
|
145
|
+
export async function unparseableURLs({
|
|
146
|
+
client,
|
|
147
|
+
reader,
|
|
148
|
+
session,
|
|
149
|
+
targetId,
|
|
150
|
+
origin,
|
|
151
|
+
tag,
|
|
152
|
+
fixture,
|
|
153
|
+
browserSession,
|
|
154
|
+
}) {
|
|
128
155
|
const root = new URL(origin);
|
|
129
156
|
const cookie = `seven-unparseable-${tag}`;
|
|
130
157
|
const value = `secret-${tag}`;
|
|
@@ -134,7 +161,7 @@ export async function unparseableURLs({ client, reader, session, targetId, origi
|
|
|
134
161
|
await reader.send('Storage.setCookies', {
|
|
135
162
|
cookies: [{ name: cookie, value, domain: '.sliccy.ai', path: '/', secure: false }],
|
|
136
163
|
});
|
|
137
|
-
const url =
|
|
164
|
+
const url = rootURL(root, 'exa%20mple.sliccy.ai', `/doc?n=${tag}-observer`);
|
|
138
165
|
const { targetId: probeId } = await reader.send('Target.createTarget', {
|
|
139
166
|
url,
|
|
140
167
|
background: true,
|
|
@@ -143,7 +170,9 @@ export async function unparseableURLs({ client, reader, session, targetId, origi
|
|
|
143
170
|
const ground = await document(reader, probeId, url);
|
|
144
171
|
observer = (await reader.send('Target.attachToTarget', { targetId: probeId, flatten: true }))
|
|
145
172
|
.sessionId;
|
|
146
|
-
const listed =
|
|
173
|
+
const listed = browserSession
|
|
174
|
+
? await outcome(client.send('Target.getTargets', {}))
|
|
175
|
+
: 'not-supported';
|
|
147
176
|
const attached = await outcome(
|
|
148
177
|
client.send('Target.attachToTarget', { targetId: probeId, flatten: true })
|
|
149
178
|
);
|
|
@@ -168,7 +197,10 @@ export async function unparseableURLs({ client, reader, session, targetId, origi
|
|
|
168
197
|
const result = {
|
|
169
198
|
browserVersion: (await reader.send('Browser.getVersion', {})).product,
|
|
170
199
|
live: ground.cookie.includes(`${cookie}=${value}`),
|
|
171
|
-
listed:
|
|
200
|
+
listed:
|
|
201
|
+
listed === 'not-supported'
|
|
202
|
+
? listed
|
|
203
|
+
: listed.ok && listed.result.targetInfos?.some((info) => info.targetId === probeId),
|
|
172
204
|
attached: attached.ok,
|
|
173
205
|
attachRefused: refused(attached),
|
|
174
206
|
readCookie,
|