| ... | ... | @@ -13,7 +13,7 @@ const { fromPartition, sessions } = vi.hoisted(() => ({ |
| 13 | 13 | { |
| 14 | 14 | readonly clearCache: ReturnType<typeof vi.fn>; |
| 15 | 15 | readonly clearStorageData: ReturnType<typeof vi.fn>; |
| 16 | | readonly getUserAgent: ReturnType<typeof vi.fn>; |
| 16 | readonly getUserAgent: ReturnType<typeof vi.fn<() => string>>; |
| 17 | 17 | readonly setPermissionRequestHandler: ReturnType<typeof vi.fn>; |
| 18 | 18 | readonly setPermissionCheckHandler: ReturnType<typeof vi.fn>; |
| 19 | 19 | readonly setUserAgent: ReturnType<typeof vi.fn>; |
| ... | ... | @@ -102,6 +102,44 @@ describe("BrowserSession", () => { |
| 102 | 102 | }).pipe(Effect.provide(layer)), |
| 103 | 103 | ); |
| 104 | 104 | |
| 105 | // A rewritten session UA — any variant, even ones that keep the Electron |
| 106 | // token — makes Cloudflare Turnstile loop with error 600010 (#5002), so |
| 107 | // the guest must end up with Electron's native User-Agent. The mock applies |
| 108 | // setUserAgent calls, so this fails on any reintroduced rewrite while still |
| 109 | // permitting a harmless re-set of the unchanged native string. |
| 110 | it.effect("keeps the guest's effective User-Agent equal to Electron's native one", () => |
| 111 | Effect.gen(function* () { |
| 112 | // Electron's real UA shape: app token, then Chrome, then Electron, then |
| 113 | // Safari — the token order and casing matter to any strip regex. |
| 114 | const nativeUserAgent = |
| 115 | "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) T3Code(Alpha)/0.0.33 Chrome/146.0.7680.216 Electron/41.5.0 Safari/537.36"; |
| 116 | fromPartition.mockReset(); |
| 117 | fromPartition.mockImplementation((partition: string) => { |
| 118 | let userAgent = nativeUserAgent; |
| 119 | const browserSession = { |
| 120 | clearCache: vi.fn(() => Promise.resolve()), |
| 121 | clearStorageData: vi.fn(() => Promise.resolve()), |
| 122 | getUserAgent: vi.fn(() => userAgent), |
| 123 | setPermissionRequestHandler: vi.fn(), |
| 124 | setPermissionCheckHandler: vi.fn(), |
| 125 | setUserAgent: vi.fn((next: string) => { |
| 126 | userAgent = next; |
| 127 | }), |
| 128 | }; |
| 129 | sessions.set(partition, browserSession); |
| 130 | return browserSession; |
| 131 | }); |
| 132 | |
| 133 | const browserSessions = yield* BrowserSession.BrowserSession; |
| 134 | const partition = yield* browserSessions.getPartition("scope-a"); |
| 135 | yield* browserSessions.getSession("scope-a"); |
| 136 | |
| 137 | const browserSession = sessions.get(partition); |
| 138 | assert.isDefined(browserSession); |
| 139 | assert.strictEqual(browserSession.getUserAgent(), nativeUserAgent); |
| 140 | }).pipe(Effect.provide(layer)), |
| 141 | ); |
| 142 | |
| 105 | 143 | it.effect("grants clipboard-sanitized-write through both the request and check handlers", () => |
| 106 | 144 | Effect.gen(function* () { |
| 107 | 145 | const browserSessions = yield* BrowserSession.BrowserSession; |