@@ -34,6 +34,11 @@ vi.mock('@clerk/electron-passkeys', () => ({
3434 } ,
3535} ) ) ;
3636
37+ const mainFrame = { } ;
38+ const windowSender = { mainFrame, getType : ( ) => 'window' } ;
39+ const mainFrameEvent = { sender : windowSender , senderFrame : mainFrame } as unknown as Electron . IpcMainInvokeEvent ;
40+ const subframeEvent = { sender : windowSender , senderFrame : { } } as unknown as Electron . IpcMainInvokeEvent ;
41+
3742describe ( 'createClerkBridge' , ( ) => {
3843 const missingStorage = { } as Parameters < typeof createClerkBridge > [ 0 ] ;
3944 const storage : TokenStorage = {
@@ -264,7 +269,52 @@ describe('createClerkBridge', () => {
264269 return channel === OAUTH_TRANSPORT_CHANNELS . getRedirectUrl ;
265270 } ) ?. [ 1 ] ;
266271
267- expect ( getRedirectUrlHandler ?.( { } as Electron . IpcMainInvokeEvent ) ) . toBe ( 'my-app://renderer/' ) ;
272+ expect ( getRedirectUrlHandler ?.( mainFrameEvent ) ) . toBe ( 'my-app://renderer/' ) ;
273+ } ) ;
274+
275+ it ( 'rejects OAuth transport requests that do not originate from the main frame' , async ( ) => {
276+ vi . mocked ( shell . openExternal ) . mockResolvedValue ( undefined ) ;
277+ createClerkBridge ( {
278+ storage,
279+ renderer : {
280+ host : 'renderer' ,
281+ scheme : 'my-app' ,
282+ } ,
283+ } ) ;
284+
285+ const findHandler = ( channel : string ) =>
286+ vi . mocked ( ipcMain . handle ) . mock . calls . find ( ( [ registered ] ) => registered === channel ) ?. [ 1 ] ;
287+
288+ expect ( ( ) => findHandler ( OAUTH_TRANSPORT_CHANNELS . getRedirectUrl ) ?.( subframeEvent ) ) . toThrow ( 'main frame' ) ;
289+ await expect (
290+ findHandler ( OAUTH_TRANSPORT_CHANNELS . open ) ?.( subframeEvent , 'https://accounts.example.com/oauth' ) ,
291+ ) . rejects . toThrow ( 'main frame' ) ;
292+ expect ( shell . openExternal ) . not . toHaveBeenCalled ( ) ;
293+ } ) ;
294+
295+ it ( 'rejects OAuth transport requests from a <webview> whose top frame mimics the main frame' , async ( ) => {
296+ vi . mocked ( shell . openExternal ) . mockResolvedValue ( undefined ) ;
297+ createClerkBridge ( {
298+ storage,
299+ renderer : {
300+ host : 'renderer' ,
301+ scheme : 'my-app' ,
302+ } ,
303+ } ) ;
304+
305+ const findHandler = ( channel : string ) =>
306+ vi . mocked ( ipcMain . handle ) . mock . calls . find ( ( [ registered ] ) => registered === channel ) ?. [ 1 ] ;
307+
308+ const webviewEvent = {
309+ sender : { mainFrame, getType : ( ) => 'webview' } ,
310+ senderFrame : mainFrame ,
311+ } as unknown as Electron . IpcMainInvokeEvent ;
312+
313+ expect ( ( ) => findHandler ( OAUTH_TRANSPORT_CHANNELS . getRedirectUrl ) ?.( webviewEvent ) ) . toThrow ( 'main frame' ) ;
314+ await expect (
315+ findHandler ( OAUTH_TRANSPORT_CHANNELS . open ) ?.( webviewEvent , 'https://accounts.example.com/oauth' ) ,
316+ ) . rejects . toThrow ( 'main frame' ) ;
317+ expect ( shell . openExternal ) . not . toHaveBeenCalled ( ) ;
268318 } ) ;
269319
270320 it ( 'opens OAuth URLs externally and resolves with the matching deep-link callback URL' , async ( ) => {
@@ -280,7 +330,7 @@ describe('createClerkBridge', () => {
280330 const openHandler = vi . mocked ( ipcMain . handle ) . mock . calls . find ( ( [ channel ] ) => {
281331 return channel === OAUTH_TRANSPORT_CHANNELS . open ;
282332 } ) ?. [ 1 ] ;
283- const openPromise = openHandler ?.( { } as Electron . IpcMainInvokeEvent , 'https://accounts.example.com/oauth' ) ;
333+ const openPromise = openHandler ?.( mainFrameEvent , 'https://accounts.example.com/oauth' ) ;
284334 const openUrlListener = vi . mocked ( app . on ) . mock . calls . find ( ( [ event ] ) => event === 'open-url' ) ?. [ 1 ] as (
285335 event : Electron . Event ,
286336 url : string ,
0 commit comments