instar 1.3.1262 → 1.3.1263
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/dist/core/AnthropicReloginBrowserDriver.d.ts +17 -5
- package/dist/core/AnthropicReloginBrowserDriver.d.ts.map +1 -1
- package/dist/core/AnthropicReloginBrowserDriver.js +80 -4
- package/dist/core/AnthropicReloginBrowserDriver.js.map +1 -1
- package/dist/core/ChromeCdpReloginBrowser.d.ts +3 -3
- package/dist/core/ChromeCdpReloginBrowser.d.ts.map +1 -1
- package/dist/core/ChromeCdpReloginBrowser.js +51 -6
- package/dist/core/ChromeCdpReloginBrowser.js.map +1 -1
- package/dist/core/GooglePasskeyPageClasses.d.ts +97 -0
- package/dist/core/GooglePasskeyPageClasses.d.ts.map +1 -0
- package/dist/core/GooglePasskeyPageClasses.js +154 -0
- package/dist/core/GooglePasskeyPageClasses.js.map +1 -0
- package/dist/core/PasskeyBrowserPolicy.d.ts +9 -0
- package/dist/core/PasskeyBrowserPolicy.d.ts.map +1 -1
- package/dist/core/PasskeyBrowserPolicy.js +2 -0
- package/dist/core/PasskeyBrowserPolicy.js.map +1 -1
- package/dist/core/PasskeyWebAuthnFixture.d.ts.map +1 -1
- package/dist/core/PasskeyWebAuthnFixture.js +44 -1
- package/dist/core/PasskeyWebAuthnFixture.js.map +1 -1
- package/dist/data/standards-guard-index.json +1 -1
- package/dist/data/standards-guard-index.meta.json +2 -2
- package/dist/data/standards-registry.meta.json +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/src/data/standards-guard-index.json +1 -1
- package/src/data/standards-guard-index.meta.json +2 -2
- package/src/data/standards-registry.meta.json +1 -1
- package/upgrades/1.3.1263.md +38 -0
- package/upgrades/side-effects/passkey-inc7-page-classes.md +231 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GooglePasskeyPageClasses — the CLOSED set of Google pages the passkey work
|
|
3
|
+
* recognises (spec docs/specs/agent-held-google-passkey.md §3.6), each matched on
|
|
4
|
+
* STRUCTURE (exact origin, sign-in route, element ids / roles / exact control
|
|
5
|
+
* labels), evaluated BEFORE the parent driver's text-regex chain, and the outcome
|
|
6
|
+
* mapping a proof or repair derives from the page it ended on.
|
|
7
|
+
*
|
|
8
|
+
* Why structure first: the parent chain (`ChromeCdpReloginBrowser.snapshot`) reads
|
|
9
|
+
* body prose — "verification code", "password" — which Google's passkey pages also
|
|
10
|
+
* print in help text, so a passkey challenge would read as `totp` and a backup-code
|
|
11
|
+
* page as `password`. A structural match cannot be shadowed by prose, and an
|
|
12
|
+
* unmatched page is `null` here (⇒ the parent chain, ⇒ `unknown`): the safe
|
|
13
|
+
* direction. Every class is a SINGLE-source structural match and errs toward
|
|
14
|
+
* restriction (§11): a false `credential-rejected` costs one proof, a false
|
|
15
|
+
* `ready` is impossible from here because `ready` needs the observed assertion
|
|
16
|
+
* and the identity read as well (§3.8).
|
|
17
|
+
*
|
|
18
|
+
* Provenance of each predicate (`PAGE_CLASS_PROVENANCE`): `measured` = observed
|
|
19
|
+
* live in the 2026-09-20 prototype notes (identifier field id, the `pk/presend`
|
|
20
|
+
* challenge, the device-prompt / TOTP routes, the passkey speedbump, the
|
|
21
|
+
* Workspace refusal); `documented` = Google's long-stable sign-in routes and
|
|
22
|
+
* control ids that the prototype did not need to visit. The Rung-1 live step
|
|
23
|
+
* (§14 FD9) re-checks every `documented` row against real pages before any
|
|
24
|
+
* unattended use; until then a mismatch can only make a page `unknown`.
|
|
25
|
+
*
|
|
26
|
+
* `classifyGooglePasskeyPage` is SELF-CONTAINED (no module-level constants, no
|
|
27
|
+
* imports) so the browser can serialise it with `Function.prototype.toString`
|
|
28
|
+
* and run the SAME code inside the page, exactly as it does for
|
|
29
|
+
* `isClosedOpenAiDeviceApproval`. The unit tests exercise the function directly;
|
|
30
|
+
* the fixture test proves the in-page ordering through real Chrome.
|
|
31
|
+
*/
|
|
32
|
+
export declare const GOOGLE_PASSKEY_PAGE_CLASSES: readonly ["google-passkey-challenge", "google-passkey-create", "google-passkey-create-confirm", "google-already-enrolled", "google-passkey-throttled", "google-workspace-policy-blocked", "google-credential-not-recognized", "google-account-identity", "google-passkey-list", "google-totp-entry", "google-backup-code-entry", "google-risk-challenge"];
|
|
33
|
+
export type GooglePasskeyPageClass = typeof GOOGLE_PASSKEY_PAGE_CLASSES[number];
|
|
34
|
+
/** Where each predicate came from (see the header). */
|
|
35
|
+
export declare const PAGE_CLASS_PROVENANCE: Readonly<Record<GooglePasskeyPageClass, 'measured' | 'documented'>>;
|
|
36
|
+
/**
|
|
37
|
+
* The REDACTED structural facts the browser extracts from a page. No prose, no
|
|
38
|
+
* input values, no URL query, no email ever enters this object: only the origin,
|
|
39
|
+
* the path, bounded lists of element ids and exact lower-cased control labels,
|
|
40
|
+
* and a handful of role/landmark booleans.
|
|
41
|
+
*/
|
|
42
|
+
export interface GooglePageFacts {
|
|
43
|
+
origin: string;
|
|
44
|
+
pathname: string;
|
|
45
|
+
/** Element ids present on the page (bounded, lower-cased). */
|
|
46
|
+
ids: string[];
|
|
47
|
+
/** Exact, trimmed, lower-cased accessible labels of buttons/links (bounded). */
|
|
48
|
+
controlLabels: string[];
|
|
49
|
+
hasPasswordInput: boolean;
|
|
50
|
+
/** An open `[role="dialog"]` / `<dialog open>`. */
|
|
51
|
+
hasDialog: boolean;
|
|
52
|
+
/** A non-empty `[role="alert"]` / `[aria-live="assertive"]` region. */
|
|
53
|
+
hasAlert: boolean;
|
|
54
|
+
/** A reCAPTCHA frame or the classic captcha image. */
|
|
55
|
+
hasCaptchaWidget: boolean;
|
|
56
|
+
/** A link into Google Workspace admin help (the Workspace refusal cites it). */
|
|
57
|
+
hasAdminHelpLink: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface GooglePageOrigins {
|
|
60
|
+
/** accounts.google.com in production; the fixture's holder origin in tests. */
|
|
61
|
+
holderOrigin: string;
|
|
62
|
+
/** myaccount.google.com in production; the fixture's other origin in tests. */
|
|
63
|
+
accountOrigin: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const GOOGLE_ACCOUNT_ORIGIN = "https://myaccount.google.com";
|
|
66
|
+
/**
|
|
67
|
+
* Structural classification. Returns the matched class or `null` (⇒ the caller
|
|
68
|
+
* falls through to its own chain). Pure, self-contained, serialisable.
|
|
69
|
+
*/
|
|
70
|
+
export declare function classifyGooglePasskeyPage(facts: GooglePageFacts, origins: GooglePageOrigins): GooglePasskeyPageClass | null;
|
|
71
|
+
/** Classes on which NO action is ever allowed; the driver maps them to a result. */
|
|
72
|
+
export declare const TERMINAL_GOOGLE_PAGE_CLASSES: ReadonlySet<GooglePasskeyPageClass>;
|
|
73
|
+
export type PasskeySignInOutcome = 'ready' | 'credential-rejected' | 'removed-on-google' | 'failed' | 'security' | 'unknown';
|
|
74
|
+
export interface PasskeySignInObservation {
|
|
75
|
+
/** The last page class the sign-in ended on (`unknown` when the parent chain matched nothing). */
|
|
76
|
+
finalPageClass: string;
|
|
77
|
+
/** Result of the in-port identity read after sign-in: matched E, matched a DIFFERENT identity, or no signed-in identity. */
|
|
78
|
+
signedInIdentity: 'match' | 'mismatch' | 'none';
|
|
79
|
+
/** Google asked for and received a WebAuthn assertion from the STORED credential id (§3.8). */
|
|
80
|
+
observedAssertion: boolean;
|
|
81
|
+
/** The virtual authenticator held exactly the one stored credential. */
|
|
82
|
+
singleCredential: boolean;
|
|
83
|
+
/** Transport/outage/timeouts happened before a verdict could be read. */
|
|
84
|
+
transportError: boolean;
|
|
85
|
+
/** The cell's Google-side removal state (§3.2): a pending/attested cell never counts as rejected. */
|
|
86
|
+
googleSideRemoval: 'none' | 'pending' | 'attested' | 'verified';
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* `ready` requires ALL of: identity match, observed assertion for the stored id,
|
|
90
|
+
* a single-credential authenticator (§3.8). `security` = signed in as somebody
|
|
91
|
+
* else (custody is corrupt). `credential-rejected` = Google's not-recognized page
|
|
92
|
+
* (recorded as `removed-on-google` while the cell's removal is pending/attested/
|
|
93
|
+
* verified, so it never counts toward suspension). `failed` = reached sign-in,
|
|
94
|
+
* did not sign in, no rejection page. Everything else is `unknown`.
|
|
95
|
+
*/
|
|
96
|
+
export declare function mapPasskeySignInOutcome(obs: PasskeySignInObservation): PasskeySignInOutcome;
|
|
97
|
+
//# sourceMappingURL=GooglePasskeyPageClasses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GooglePasskeyPageClasses.d.ts","sourceRoot":"","sources":["../../src/core/GooglePasskeyPageClasses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,eAAO,MAAM,2BAA2B,2VAa9B,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,OAAO,2BAA2B,CAAC,MAAM,CAAC,CAAC;AAEhF,uDAAuD;AACvD,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,MAAM,CAAC,sBAAsB,EAAE,UAAU,GAAG,YAAY,CAAC,CAapG,CAAC;AAEH;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,gFAAgF;IAChF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gFAAgF;IAChF,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAEpE;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,GAAG,sBAAsB,GAAG,IAAI,CAgD3H;AAED,oFAAoF;AACpF,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,sBAAsB,CAG3E,CAAC;AAIH,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,qBAAqB,GAAG,mBAAmB,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7H,MAAM,WAAW,wBAAwB;IACvC,kGAAkG;IAClG,cAAc,EAAE,MAAM,CAAC;IACvB,4HAA4H;IAC5H,gBAAgB,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IAChD,+FAA+F;IAC/F,iBAAiB,EAAE,OAAO,CAAC;IAC3B,wEAAwE;IACxE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,cAAc,EAAE,OAAO,CAAC;IACxB,qGAAqG;IACrG,iBAAiB,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CACjE;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,wBAAwB,GAAG,oBAAoB,CAW3F"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GooglePasskeyPageClasses — the CLOSED set of Google pages the passkey work
|
|
3
|
+
* recognises (spec docs/specs/agent-held-google-passkey.md §3.6), each matched on
|
|
4
|
+
* STRUCTURE (exact origin, sign-in route, element ids / roles / exact control
|
|
5
|
+
* labels), evaluated BEFORE the parent driver's text-regex chain, and the outcome
|
|
6
|
+
* mapping a proof or repair derives from the page it ended on.
|
|
7
|
+
*
|
|
8
|
+
* Why structure first: the parent chain (`ChromeCdpReloginBrowser.snapshot`) reads
|
|
9
|
+
* body prose — "verification code", "password" — which Google's passkey pages also
|
|
10
|
+
* print in help text, so a passkey challenge would read as `totp` and a backup-code
|
|
11
|
+
* page as `password`. A structural match cannot be shadowed by prose, and an
|
|
12
|
+
* unmatched page is `null` here (⇒ the parent chain, ⇒ `unknown`): the safe
|
|
13
|
+
* direction. Every class is a SINGLE-source structural match and errs toward
|
|
14
|
+
* restriction (§11): a false `credential-rejected` costs one proof, a false
|
|
15
|
+
* `ready` is impossible from here because `ready` needs the observed assertion
|
|
16
|
+
* and the identity read as well (§3.8).
|
|
17
|
+
*
|
|
18
|
+
* Provenance of each predicate (`PAGE_CLASS_PROVENANCE`): `measured` = observed
|
|
19
|
+
* live in the 2026-09-20 prototype notes (identifier field id, the `pk/presend`
|
|
20
|
+
* challenge, the device-prompt / TOTP routes, the passkey speedbump, the
|
|
21
|
+
* Workspace refusal); `documented` = Google's long-stable sign-in routes and
|
|
22
|
+
* control ids that the prototype did not need to visit. The Rung-1 live step
|
|
23
|
+
* (§14 FD9) re-checks every `documented` row against real pages before any
|
|
24
|
+
* unattended use; until then a mismatch can only make a page `unknown`.
|
|
25
|
+
*
|
|
26
|
+
* `classifyGooglePasskeyPage` is SELF-CONTAINED (no module-level constants, no
|
|
27
|
+
* imports) so the browser can serialise it with `Function.prototype.toString`
|
|
28
|
+
* and run the SAME code inside the page, exactly as it does for
|
|
29
|
+
* `isClosedOpenAiDeviceApproval`. The unit tests exercise the function directly;
|
|
30
|
+
* the fixture test proves the in-page ordering through real Chrome.
|
|
31
|
+
*/
|
|
32
|
+
export const GOOGLE_PASSKEY_PAGE_CLASSES = [
|
|
33
|
+
'google-passkey-challenge',
|
|
34
|
+
'google-passkey-create',
|
|
35
|
+
'google-passkey-create-confirm',
|
|
36
|
+
'google-already-enrolled',
|
|
37
|
+
'google-passkey-throttled',
|
|
38
|
+
'google-workspace-policy-blocked',
|
|
39
|
+
'google-credential-not-recognized',
|
|
40
|
+
'google-account-identity',
|
|
41
|
+
'google-passkey-list',
|
|
42
|
+
'google-totp-entry',
|
|
43
|
+
'google-backup-code-entry',
|
|
44
|
+
'google-risk-challenge',
|
|
45
|
+
];
|
|
46
|
+
/** Where each predicate came from (see the header). */
|
|
47
|
+
export const PAGE_CLASS_PROVENANCE = Object.freeze({
|
|
48
|
+
'google-passkey-challenge': 'measured',
|
|
49
|
+
'google-passkey-create': 'measured',
|
|
50
|
+
'google-passkey-create-confirm': 'documented',
|
|
51
|
+
'google-already-enrolled': 'measured',
|
|
52
|
+
'google-passkey-throttled': 'documented',
|
|
53
|
+
'google-workspace-policy-blocked': 'measured',
|
|
54
|
+
'google-credential-not-recognized': 'documented',
|
|
55
|
+
'google-account-identity': 'measured',
|
|
56
|
+
'google-passkey-list': 'measured',
|
|
57
|
+
'google-totp-entry': 'measured',
|
|
58
|
+
'google-backup-code-entry': 'documented',
|
|
59
|
+
'google-risk-challenge': 'measured',
|
|
60
|
+
});
|
|
61
|
+
export const GOOGLE_ACCOUNT_ORIGIN = 'https://myaccount.google.com';
|
|
62
|
+
/**
|
|
63
|
+
* Structural classification. Returns the matched class or `null` (⇒ the caller
|
|
64
|
+
* falls through to its own chain). Pure, self-contained, serialisable.
|
|
65
|
+
*/
|
|
66
|
+
export function classifyGooglePasskeyPage(facts, origins) {
|
|
67
|
+
const path = String(facts.pathname || '');
|
|
68
|
+
const ids = new Set((facts.ids || []).map((v) => String(v).toLowerCase()));
|
|
69
|
+
const labels = new Set((facts.controlLabels || []).map((v) => String(v).trim().toLowerCase()));
|
|
70
|
+
// An empty origin on either side never matches (a data: page reports 'null', a
|
|
71
|
+
// policy without an account origin reports '').
|
|
72
|
+
const onHolder = !!facts.origin && facts.origin !== 'null' && facts.origin === origins.holderOrigin;
|
|
73
|
+
const onAccount = !!facts.origin && facts.origin !== 'null' && !!origins.accountOrigin && facts.origin === origins.accountOrigin;
|
|
74
|
+
// Google's sign-in routes: current `/v3/signin/...`, legacy `/signin/v2/...`, bare `/signin/...`.
|
|
75
|
+
const challenge = (segment) => new RegExp('^/(?:v3/signin|signin/v2|signin)/challenge/' + segment + '(?:/|$)').test(path);
|
|
76
|
+
const identifierRoute = /^\/(?:v3\/signin|signin\/v2|signin)\/identifier(?:\/|$)/.test(path);
|
|
77
|
+
const rejectedRoute = /^\/(?:v3\/signin|signin\/v2|signin)\/rejected(?:\/|$)/.test(path);
|
|
78
|
+
const speedbumpRoute = /^\/(?:signin\/v2\/)?speedbump\/passkeyenrollment(?:\/|$)/.test(path);
|
|
79
|
+
const passkeyOptionsRoute = /^\/signinoptions\/passkeys(?:\/|$)/.test(path);
|
|
80
|
+
if (onHolder) {
|
|
81
|
+
// Risk / CAPTCHA first: it can wear any other page's clothes and must never be acted on.
|
|
82
|
+
if (facts.hasCaptchaWidget || rejectedRoute || challenge('recaptcha') || challenge('ipp') || challenge('iap') || challenge('ipe'))
|
|
83
|
+
return 'google-risk-challenge';
|
|
84
|
+
if (challenge('pk(?:/presend)?')) {
|
|
85
|
+
// The passkey prompt: "Continue" (fires navigator.credentials.get) + "Try another way".
|
|
86
|
+
// A live prompt that still offers Continue is the CHALLENGE even if a live region
|
|
87
|
+
// is announcing something — the rejection classes need the prompt's action GONE
|
|
88
|
+
// plus a visible alert, because `credential-not-recognized` feeds suspension and
|
|
89
|
+
// a false read there is NOT the safe direction (second-pass review, 2026-09-23).
|
|
90
|
+
if (labels.has('continue'))
|
|
91
|
+
return 'google-passkey-challenge';
|
|
92
|
+
if (facts.hasAlert && labels.has('try again later'))
|
|
93
|
+
return 'google-passkey-throttled';
|
|
94
|
+
if (facts.hasAlert && labels.has('try another way'))
|
|
95
|
+
return 'google-credential-not-recognized';
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
if (challenge('totp') && ids.has('totppin'))
|
|
99
|
+
return 'google-totp-entry';
|
|
100
|
+
if (challenge('bc') && ids.has('backupcodepin'))
|
|
101
|
+
return 'google-backup-code-entry';
|
|
102
|
+
if (identifierRoute && ids.has('identifierid'))
|
|
103
|
+
return 'google-account-identity';
|
|
104
|
+
if (speedbumpRoute) {
|
|
105
|
+
if (facts.hasDialog && (labels.has('continue') || labels.has('create')))
|
|
106
|
+
return 'google-passkey-create-confirm';
|
|
107
|
+
if (labels.has('create a passkey'))
|
|
108
|
+
return 'google-passkey-create';
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (onAccount && passkeyOptionsRoute) {
|
|
114
|
+
if (facts.hasAlert && facts.hasAdminHelpLink)
|
|
115
|
+
return 'google-workspace-policy-blocked';
|
|
116
|
+
if (facts.hasDialog && (labels.has('continue') || labels.has('create')))
|
|
117
|
+
return 'google-passkey-create-confirm';
|
|
118
|
+
if (facts.hasDialog && (labels.has('done') || labels.has('ok')) && !labels.has('create a passkey'))
|
|
119
|
+
return 'google-already-enrolled';
|
|
120
|
+
if (labels.has('create a passkey'))
|
|
121
|
+
return 'google-passkey-create';
|
|
122
|
+
return 'google-passkey-list';
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
/** Classes on which NO action is ever allowed; the driver maps them to a result. */
|
|
127
|
+
export const TERMINAL_GOOGLE_PAGE_CLASSES = new Set([
|
|
128
|
+
'google-already-enrolled', 'google-passkey-throttled', 'google-workspace-policy-blocked',
|
|
129
|
+
'google-credential-not-recognized', 'google-passkey-list', 'google-risk-challenge',
|
|
130
|
+
]);
|
|
131
|
+
/**
|
|
132
|
+
* `ready` requires ALL of: identity match, observed assertion for the stored id,
|
|
133
|
+
* a single-credential authenticator (§3.8). `security` = signed in as somebody
|
|
134
|
+
* else (custody is corrupt). `credential-rejected` = Google's not-recognized page
|
|
135
|
+
* (recorded as `removed-on-google` while the cell's removal is pending/attested/
|
|
136
|
+
* verified, so it never counts toward suspension). `failed` = reached sign-in,
|
|
137
|
+
* did not sign in, no rejection page. Everything else is `unknown`.
|
|
138
|
+
*/
|
|
139
|
+
export function mapPasskeySignInOutcome(obs) {
|
|
140
|
+
if (obs.signedInIdentity === 'mismatch')
|
|
141
|
+
return 'security';
|
|
142
|
+
if (obs.transportError)
|
|
143
|
+
return 'unknown';
|
|
144
|
+
if (obs.finalPageClass === 'google-credential-not-recognized')
|
|
145
|
+
return obs.googleSideRemoval === 'none' ? 'credential-rejected' : 'removed-on-google';
|
|
146
|
+
if (obs.finalPageClass === 'google-risk-challenge' || obs.finalPageClass === 'google-passkey-throttled'
|
|
147
|
+
|| obs.finalPageClass === 'unknown')
|
|
148
|
+
return 'unknown';
|
|
149
|
+
if (obs.signedInIdentity === 'match') {
|
|
150
|
+
return obs.observedAssertion && obs.singleCredential ? 'ready' : 'unknown';
|
|
151
|
+
}
|
|
152
|
+
return 'failed';
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=GooglePasskeyPageClasses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GooglePasskeyPageClasses.js","sourceRoot":"","sources":["../../src/core/GooglePasskeyPageClasses.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,8 +28,17 @@ export interface OriginPolicy {
|
|
|
28
28
|
apexHost: string;
|
|
29
29
|
/** Fixture-only: accept http:// for the apex family (WebAuthn treats localhost as secure). */
|
|
30
30
|
allowInsecure?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The account-settings origin whose passkey pages the closed page classes
|
|
33
|
+
* recognise (spec §3.6): myaccount.google.com in production, the fixture's
|
|
34
|
+
* OTHER origin in tests. It is in the relying-party family, so the stored
|
|
35
|
+
* credential is REMOVED before any document there loads — classification
|
|
36
|
+
* there never coexists with the key. Absent ⇒ no account-origin class matches.
|
|
37
|
+
*/
|
|
38
|
+
accountOrigin?: string;
|
|
31
39
|
}
|
|
32
40
|
export declare const ACCOUNTS_GOOGLE_ORIGIN = "https://accounts.google.com";
|
|
41
|
+
export declare const MYACCOUNT_GOOGLE_ORIGIN = "https://myaccount.google.com";
|
|
33
42
|
export declare const GOOGLE_ORIGIN_POLICY: OriginPolicy;
|
|
34
43
|
/** Origin of a URL, or null when it cannot be parsed. */
|
|
35
44
|
export declare function originOf(url: string): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasskeyBrowserPolicy.d.ts","sourceRoot":"","sources":["../../src/core/PasskeyBrowserPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PasskeyBrowserPolicy.d.ts","sourceRoot":"","sources":["../../src/core/PasskeyBrowserPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,YAAY,EAAE,MAAM,CAAC;IACrB,yFAAyF;IACzF,QAAQ,EAAE,MAAM,CAAC;IACjB,8FAA8F;IAC9F,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,sBAAsB,gCAAgC,CAAC;AACpE,eAAO,MAAM,uBAAuB,iCAAiC,CAAC;AAEtE,eAAO,MAAM,oBAAoB,EAAE,YAIjC,CAAC;AAEH,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnD;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,YAAmC,GAAG,OAAO,CAYrG;AAED,kDAAkD;AAClD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,GAAE,YAAmC,GAAG,OAAO,CAMrH;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,EAAE,EAC/C,MAAM,GAAE,YAAmC,GAC1C,OAAO,CAIT;AAED,6EAA6E;AAC7E,eAAO,MAAM,2BAA2B,sLAK9B,CAAC"}
|
|
@@ -22,9 +22,11 @@
|
|
|
22
22
|
* Fetch interception handler.
|
|
23
23
|
*/
|
|
24
24
|
export const ACCOUNTS_GOOGLE_ORIGIN = 'https://accounts.google.com';
|
|
25
|
+
export const MYACCOUNT_GOOGLE_ORIGIN = 'https://myaccount.google.com';
|
|
25
26
|
export const GOOGLE_ORIGIN_POLICY = Object.freeze({
|
|
26
27
|
holderOrigin: ACCOUNTS_GOOGLE_ORIGIN,
|
|
27
28
|
apexHost: 'google.com',
|
|
29
|
+
accountOrigin: MYACCOUNT_GOOGLE_ORIGIN,
|
|
28
30
|
});
|
|
29
31
|
/** Origin of a URL, or null when it cannot be parsed. */
|
|
30
32
|
export function originOf(url) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasskeyBrowserPolicy.js","sourceRoot":"","sources":["../../src/core/PasskeyBrowserPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;
|
|
1
|
+
{"version":3,"file":"PasskeyBrowserPolicy.js","sourceRoot":"","sources":["../../src/core/PasskeyBrowserPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAmBH,MAAM,CAAC,MAAM,sBAAsB,GAAG,6BAA6B,CAAC;AACpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,8BAA8B,CAAC;AAEtE,MAAM,CAAC,MAAM,oBAAoB,GAAiB,MAAM,CAAC,MAAM,CAAC;IAC9D,YAAY,EAAE,sBAAsB;IACpC,QAAQ,EAAE,YAAY;IACtB,aAAa,EAAE,uBAAuB;CACvC,CAAC,CAAC;AAEH,yDAAyD;AACzD,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;QAC/E,6EAA6E;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,SAAuB,oBAAoB;IAC1F,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;YAAE,OAAO,KAAK,CAAC;QAC/F,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,qEAAqE;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,OAAO,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AACxD,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAoB,EAAE,SAAuB,oBAAoB;IAC1G,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,4BAA4B;IAC9D,IAAI,MAAM,KAAK,MAAM,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IACjD,IAAI,GAAG,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC9B,OAAO,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,MAA+C,EAC/C,SAAuB,oBAAoB;IAE3C,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC,YAAY;QAAE,OAAO,KAAK,CAAC;IAC7D,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,YAAY,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AACpH,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,yBAAyB;IACzB,sBAAsB;IACtB,sDAAsD;IACtD,8DAA8D;CACtD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasskeyWebAuthnFixture.d.ts","sourceRoot":"","sources":["../../src/core/PasskeyWebAuthnFixture.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"PasskeyWebAuthnFixture.d.ts","sourceRoot":"","sources":["../../src/core/PasskeyWebAuthnFixture.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAkHD,wDAAwD;AACxD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,cAAc,CAAC,CAsBnE"}
|
|
@@ -81,6 +81,49 @@ function handler(selfOrigin, otherOrigin) {
|
|
|
81
81
|
const safe = email.replace(/[<>&"]/g, '');
|
|
82
82
|
return send(200, page(`<h1>account</h1><div data-email="${safe}">${safe}</div>`));
|
|
83
83
|
}
|
|
84
|
+
// ── Redacted page-class fixtures (spec §3.6 / §8 "page-class ordering fixtures").
|
|
85
|
+
// Each page carries the STRUCTURE of one closed Google page AND prose the parent
|
|
86
|
+
// text-regex chain would classify differently, so a match here proves the
|
|
87
|
+
// structural layer runs first. No identities, no real copy beyond the markers.
|
|
88
|
+
case '/parent-chain':
|
|
89
|
+
// No structural class here: the parent prose/selector chain must answer (`email`).
|
|
90
|
+
return send(200, page(`<input type="email" autocomplete="username"><button>Next</button>`));
|
|
91
|
+
case '/v3/signin/identifier':
|
|
92
|
+
return send(200, page(`<p>enter a verification code</p><input type="text" id="identifierId" name="identifier"><button>Next</button>`));
|
|
93
|
+
case '/v3/signin/challenge/pk/presend': {
|
|
94
|
+
const v = url.searchParams.get('v') ?? '';
|
|
95
|
+
const rejected = v === 'alert' || v === 'throttled';
|
|
96
|
+
// The rejection states show the alert and LOSE the prompt's own Continue control.
|
|
97
|
+
const alert = rejected ? '<div role="alert">something went wrong</div>' : '';
|
|
98
|
+
const again = v === 'throttled' ? '<button>Try again later</button>' : '';
|
|
99
|
+
const cont = rejected ? '' : '<button id="continue" onclick="document.getElementById(\'picked\').textContent=\'continue\'">Continue</button>';
|
|
100
|
+
// A HIDDEN dialog + a hidden "Not now": present in the DOM, never rendered — must not count.
|
|
101
|
+
return send(200, page(`<p>use your passkey, or enter your password or a verification code</p>${alert}
|
|
102
|
+
<div role="dialog" style="display:none"><button>Not now</button></div>
|
|
103
|
+
${cont}${again}
|
|
104
|
+
<a href="#" onclick="document.getElementById('picked').textContent='another'">Try another way</a><span id="picked">none</span>`));
|
|
105
|
+
}
|
|
106
|
+
case '/v3/signin/challenge/totp':
|
|
107
|
+
return send(200, page(`<p>choose an account</p><input type="tel" id="totpPin" name="totpPin"><button>Next</button>`));
|
|
108
|
+
case '/v3/signin/challenge/bc':
|
|
109
|
+
return send(200, page(`<p>choose an account</p><input type="tel" id="backupCodePin" name="backupCodePin"><button>Next</button>`));
|
|
110
|
+
case '/v3/signin/challenge/recaptcha':
|
|
111
|
+
return send(200, page(`<div class="g-recaptcha" data-sitekey="fixture"></div><input type="password"><button>Next</button>`));
|
|
112
|
+
case '/speedbump/passkeyenrollment': {
|
|
113
|
+
const confirm = url.searchParams.get('v') === 'confirm';
|
|
114
|
+
return send(200, page(`<p>enter your password</p>${confirm ? '<div role="dialog"><button>Continue</button></div>' : ''}
|
|
115
|
+
<button onclick="document.getElementById('picked').textContent='create'">Create a passkey</button>
|
|
116
|
+
<button onclick="document.getElementById('picked').textContent='not-now'">Not now</button><span id="picked">none</span>`));
|
|
117
|
+
}
|
|
118
|
+
case '/signinoptions/passkeys': {
|
|
119
|
+
const v = url.searchParams.get('v') ?? 'list';
|
|
120
|
+
const body = v === 'create' ? '<button>Create a passkey</button>'
|
|
121
|
+
: v === 'confirm' ? '<div role="dialog"><button>Continue</button></div><button>Create a passkey</button>'
|
|
122
|
+
: v === 'done' ? '<div role="dialog"><button>Done</button></div>'
|
|
123
|
+
: v === 'blocked' ? '<div role="alert">passkeys are not enabled here</div><a href="https://support.google.com/a/answer/0">Learn more</a>'
|
|
124
|
+
: '<ul><li>a passkey</li></ul>';
|
|
125
|
+
return send(200, page(`<p>enter your password</p>${body}`));
|
|
126
|
+
}
|
|
84
127
|
default:
|
|
85
128
|
return send(404, page('<h1>not found</h1>'));
|
|
86
129
|
}
|
|
@@ -101,7 +144,7 @@ export async function startPasskeyFixture() {
|
|
|
101
144
|
return {
|
|
102
145
|
holderOrigin,
|
|
103
146
|
otherOrigin,
|
|
104
|
-
policy: { holderOrigin, apexHost: RP_ID, allowInsecure: true },
|
|
147
|
+
policy: { holderOrigin, apexHost: RP_ID, allowInsecure: true, accountOrigin: otherOrigin },
|
|
105
148
|
close: async () => {
|
|
106
149
|
await Promise.all([
|
|
107
150
|
new Promise((r) => holder.close(() => r())),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PasskeyWebAuthnFixture.js","sourceRoot":"","sources":["../../src/core/PasskeyWebAuthnFixture.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PasskeyWebAuthnFixture.js","sourceRoot":"","sources":["../../src/core/PasskeyWebAuthnFixture.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedFrom": "source-tree",
|
|
4
4
|
"registrySha256": "c55d201d822f8706b0dc50ce21b0b8298b4ac470543cfe46d8988da03b0d34b9",
|
|
5
|
-
"packageVersion": "1.3.
|
|
5
|
+
"packageVersion": "1.3.1263",
|
|
6
6
|
"guards": [
|
|
7
7
|
{
|
|
8
8
|
"ref": "docs/audits/phase-b/f10-triage.md",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sha256": "
|
|
2
|
+
"sha256": "ab76a4f6a4b8d97882686bf355e67b63bcc1d0ceb39d01fd72bf8c4cc5741f85",
|
|
3
3
|
"registrySha256": "c55d201d822f8706b0dc50ce21b0b8298b4ac470543cfe46d8988da03b0d34b9",
|
|
4
|
-
"packageVersion": "1.3.
|
|
4
|
+
"packageVersion": "1.3.1263"
|
|
5
5
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-09-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-09-23T15:50:11.164Z",
|
|
5
|
+
"instarVersion": "1.3.1263",
|
|
6
6
|
"entryCount": 203,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedFrom": "source-tree",
|
|
4
4
|
"registrySha256": "c55d201d822f8706b0dc50ce21b0b8298b4ac470543cfe46d8988da03b0d34b9",
|
|
5
|
-
"packageVersion": "1.3.
|
|
5
|
+
"packageVersion": "1.3.1263",
|
|
6
6
|
"guards": [
|
|
7
7
|
{
|
|
8
8
|
"ref": "docs/audits/phase-b/f10-triage.md",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sha256": "
|
|
2
|
+
"sha256": "ab76a4f6a4b8d97882686bf355e67b63bcc1d0ceb39d01fd72bf8c4cc5741f85",
|
|
3
3
|
"registrySha256": "c55d201d822f8706b0dc50ce21b0b8298b4ac470543cfe46d8988da03b0d34b9",
|
|
4
|
-
"packageVersion": "1.3.
|
|
4
|
+
"packageVersion": "1.3.1263"
|
|
5
5
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Seventh increment of the approved agent-held Google passkeys spec
|
|
9
|
+
(`docs/specs/agent-held-google-passkey.md` §3.6). The sign-in repair browser now recognises the twelve
|
|
10
|
+
Google pages the passkey work can meet — the account identifier, the passkey prompt and its
|
|
11
|
+
not-recognised / throttled states, authenticator-code and backup-code entry, the enrollment speedbump
|
|
12
|
+
and its confirm, CAPTCHA / risk review, and the passkey settings page's list / create / confirm /
|
|
13
|
+
already-enrolled / Workspace-refusal shapes — by structure (exact origin, sign-in route, stable ids,
|
|
14
|
+
roles, exact control labels), before its older text-based chain runs. Each page carries a fixed
|
|
15
|
+
outcome (`ready` only with an observed assertion, one credential and an identity match; rejected
|
|
16
|
+
only from the not-recognised page; uncertain is `unknown`) and a fixed list of actions the supervisor
|
|
17
|
+
may pick from. A credential-creating control enters that list only on its exact page and only in an
|
|
18
|
+
enrollment drive; a sign-in can decline the speedbump but never mint.
|
|
19
|
+
|
|
20
|
+
Still dark: nothing enrolls, proves, or uses a passkey yet, and these pages are only ever exercised
|
|
21
|
+
against the local fixture.
|
|
22
|
+
|
|
23
|
+
## What to Tell Your User
|
|
24
|
+
|
|
25
|
+
Nothing changes for you in this update. It's groundwork: your agent's sign-in repair now knows exactly
|
|
26
|
+
which Google passkey screens exist and what it is allowed to do on each, so the enrollment step that
|
|
27
|
+
comes next can be careful by construction.
|
|
28
|
+
|
|
29
|
+
## Summary of New Capabilities
|
|
30
|
+
|
|
31
|
+
- Structural recognition of the closed set of Google passkey pages, evaluated before text matching.
|
|
32
|
+
- A fixed outcome mapping and a structural floor on credential-creating actions in the repair driver.
|
|
33
|
+
|
|
34
|
+
## Evidence
|
|
35
|
+
|
|
36
|
+
- `tests/unit/google-passkey-page-classes.test.ts`, `tests/unit/anthropic-relogin-driver-page-classes.test.ts`;
|
|
37
|
+
`tests/integration/passkey-page-classes-fixture.test.ts` (real Chrome against the local fixture:
|
|
38
|
+
structure wins over prose on every page, unmatched pages fall through, new controls click by exact label).
|