| ... | @@ -15,6 +15,7 @@ const { fromPartition, sessions } = vi.hoisted(() => ({ | ... | @@ -15,6 +15,7 @@ const { fromPartition, sessions } = vi.hoisted(() => ({ |
| 15 | readonly clearStorageData: ReturnType<typeof vi.fn>; | 15 | readonly clearStorageData: ReturnType<typeof vi.fn>; |
| 16 | readonly getUserAgent: ReturnType<typeof vi.fn>; | 16 | readonly getUserAgent: ReturnType<typeof vi.fn>; |
| 17 | readonly setPermissionRequestHandler: ReturnType<typeof vi.fn>; | 17 | readonly setPermissionRequestHandler: ReturnType<typeof vi.fn>; |
| | 18 | readonly setPermissionCheckHandler: ReturnType<typeof vi.fn>; |
| 18 | readonly setUserAgent: ReturnType<typeof vi.fn>; | 19 | readonly setUserAgent: ReturnType<typeof vi.fn>; |
| 19 | } | 20 | } |
| 20 | >(), | 21 | >(), |
| ... | @@ -40,6 +41,7 @@ describe("BrowserSession", () => { | ... | @@ -40,6 +41,7 @@ describe("BrowserSession", () => { |
| 40 | clearStorageData: vi.fn(() => Promise.resolve()), | 41 | clearStorageData: vi.fn(() => Promise.resolve()), |
| 41 | getUserAgent: vi.fn(() => "Mozilla/5.0 Electron/41.5.0 t3code/0.0.27"), | 42 | getUserAgent: vi.fn(() => "Mozilla/5.0 Electron/41.5.0 t3code/0.0.27"), |
| 42 | setPermissionRequestHandler: vi.fn(), | 43 | setPermissionRequestHandler: vi.fn(), |
| | 44 | setPermissionCheckHandler: vi.fn(), |
| 43 | setUserAgent: vi.fn(), | 45 | setUserAgent: vi.fn(), |
| 44 | }; | 46 | }; |
| 45 | sessions.set(partition, browserSession); | 47 | sessions.set(partition, browserSession); |
| ... | @@ -61,6 +63,55 @@ describe("BrowserSession", () => { | ... | @@ -61,6 +63,55 @@ describe("BrowserSession", () => { |
| 61 | }).pipe(Effect.provide(layer)), | 63 | }).pipe(Effect.provide(layer)), |
| 62 | ); | 64 | ); |
| 63 | | 65 | |
| | 66 | it.effect("grants clipboard-sanitized-write through both the request and check handlers", () => |
| | 67 | Effect.gen(function* () { |
| | 68 | const browserSessions = yield* BrowserSession.BrowserSession; |
| | 69 | const partition = yield* browserSessions.getPartition("scope-a"); |
| | 70 | yield* browserSessions.getSession("scope-a"); |
| | 71 | |
| | 72 | const browserSession = sessions.get(partition); |
| | 73 | assert.isDefined(browserSession); |
| | 74 | |
| | 75 | const requestHandler = browserSession.setPermissionRequestHandler.mock.calls[0]?.[0]; |
| | 76 | const checkHandler = browserSession.setPermissionCheckHandler.mock.calls[0]?.[0]; |
| | 77 | assert.isFunction(requestHandler); |
| | 78 | assert.isFunction(checkHandler); |
| | 79 | |
| | 80 | const requestAllows = (permission: string): boolean => { |
| | 81 | let granted: boolean | undefined; |
| | 82 | requestHandler(null, permission, (value: boolean) => { |
| | 83 | granted = value; |
| | 84 | }); |
| | 85 | assert.isDefined(granted); |
| | 86 | return granted; |
| | 87 | }; |
| | 88 | |
| | 89 | for (const permission of [ |
| | 90 | "clipboard-read", |
| | 91 | "clipboard-sanitized-write", |
| | 92 | "notifications", |
| | 93 | "geolocation", |
| | 94 | ]) { |
| | 95 | assert.isTrue(requestAllows(permission), `request handler should allow ${permission}`); |
| | 96 | assert.isTrue( |
| | 97 | checkHandler(null, permission) as boolean, |
| | 98 | `check handler should allow ${permission}`, |
| | 99 | ); |
| | 100 | } |
| | 101 | |
| | 102 | // `clipboard-write` is not a real Electron permission — the async write API |
| | 103 | // uses `clipboard-sanitized-write` — so the stale name must not be granted, |
| | 104 | // and unrelated permissions stay denied. |
| | 105 | for (const permission of ["clipboard-write", "midi"]) { |
| | 106 | assert.isFalse(requestAllows(permission), `request handler should deny ${permission}`); |
| | 107 | assert.isFalse( |
| | 108 | checkHandler(null, permission) as boolean, |
| | 109 | `check handler should deny ${permission}`, |
| | 110 | ); |
| | 111 | } |
| | 112 | }).pipe(Effect.provide(layer)), |
| | 113 | ); |
| | 114 | |
| 64 | it.effect("preserves partition scope and the platform failure chain", () => { | 115 | it.effect("preserves partition scope and the platform failure chain", () => { |
| 65 | const nativeCause = new Error("native digest failed"); | 116 | const nativeCause = new Error("native digest failed"); |
| 66 | const platformCause = PlatformError.systemError({ | 117 | const platformCause = PlatformError.systemError({ |