const { contextBridge, ipcRenderer } = require('electron'); // Strict channel allow-lists. Only the channels the renderer actually uses are forwarded, // so injected/XSS content cannot reach arbitrary main-process IPC handlers (e.g. read-file). const INVOKE_CHANNELS = ['show-about', 'close-app']; // mirror:frame-request/-response: the phone remote's live mirror. Main asks the renderer for a JPEG // of the stage screen (captured renderer-side via getDisplayMedia, far faster than capturePage) and // the renderer answers with the encoded bytes. const RECEIVE_CHANNELS = ['server-log', 'splash:update', 'control:perform', 'mirror:frame-request']; const SEND_CHANNELS = ['mirror:frame-response']; contextBridge.exposeInMainWorld('electron', { ipcRenderer: { invoke: (channel, ...args) => { if (INVOKE_CHANNELS.includes(channel)) return ipcRenderer.invoke(channel, ...args); return Promise.reject(new Error(`Blocked IPC invoke on disallowed channel: ${channel}`)); }, on: (channel, listener) => { if (RECEIVE_CHANNELS.includes(channel)) ipcRenderer.on(channel, listener); }, removeListener: (channel, listener) => { if (RECEIVE_CHANNELS.includes(channel)) ipcRenderer.removeListener(channel, listener); }, send: (channel, ...args) => { if (SEND_CHANNELS.includes(channel)) ipcRenderer.send(channel, ...args); } } });