-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpreload.js
More file actions
163 lines (153 loc) · 5.25 KB
/
preload.js
File metadata and controls
163 lines (153 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* Preload script for Electron renderer processes
* Provides secure IPC communication via contextBridge
*
* SECURITY: Uses contextIsolation to prevent renderer from accessing Node.js APIs
* NOTE: channel-validator is inlined here because sandbox:true restricts require()
* to built-in modules only — local file requires are not allowed in sandboxed preloads.
*/
const { contextBridge, ipcRenderer } = require('electron');
// Inlined from channel-validator.js (cannot require local files in sandboxed preload)
const ALLOWED_CHANNELS = {
send: [
'get-timer-state',
'get-displays',
'timer-command',
'timer-control',
'colors-update',
'widget-colors-update',
'clock-colors-update',
'display-colors-update',
'widget-style-update',
'display-settings-update',
'open-widget',
'close-widget',
'open-display',
'close-display',
'open-clock-widget',
'close-clock-widget',
'clock-widget-resize',
'clock-widget-move',
'clock-widget-set-style',
'clock-widget-settings',
'resize-control-window',
'widget-set-opacity',
'widget-set-position',
'widget-resize',
'widget-move',
'display-move',
'minimize-window',
'toggle-fullscreen',
'close-window',
'quit-app',
'reset-and-relaunch',
'set-autostart',
'get-autostart',
'export-logs'
],
receive: [
'timer-state',
'colors-update',
'widget-colors-update',
'clock-colors-update',
'display-colors-update',
'widget-style-update',
'timer-minute',
'timer-reached-zero',
'timer-overrun-minute',
'display-settings-update',
'displays-list',
'set-clock-style',
'clock-settings',
'display-window-state',
'widget-window-state',
'clock-window-state',
'timer-recovery-available'
]
};
function isValidChannel(channel, direction) {
if (!channel || typeof channel !== 'string') { return false; }
if (!ALLOWED_CHANNELS[direction]) { return false; }
return ALLOWED_CHANNELS[direction].includes(channel);
}
// Expose protected methods to renderer process via contextBridge
contextBridge.exposeInMainWorld('electronAPI', {
/**
* Send IPC message to main process
* @param {string} channel - IPC channel name
* @param {any} data - Data to send
*/
send: (channel, data) => {
if (isValidChannel(channel, 'send')) {
ipcRenderer.send(channel, data);
} else {
console.error(`Blocked attempt to send to unauthorized channel: ${channel}`);
}
},
/**
* Invoke IPC handler (two-way request-response)
*/
invoke: (channel, data) => {
if (isValidChannel(channel, 'send')) {
return ipcRenderer.invoke(channel, data);
}
console.error(`Blocked attempt to invoke unauthorized channel: ${channel}`);
return Promise.reject(new Error(`Invalid channel: ${channel}`));
},
/**
* Register IPC event listener
* @param {string} channel - IPC channel name
* @param {Function} callback - Callback function
* @returns {Function} Cleanup function to remove listener
*/
on: (channel, callback) => {
if (isValidChannel(channel, 'receive')) {
const subscription = (event, ...args) => callback(...args);
ipcRenderer.on(channel, subscription);
// Return cleanup function
return () => {
ipcRenderer.removeListener(channel, subscription);
};
} else {
console.error(`Blocked attempt to listen to unauthorized channel: ${channel}`);
return () => {}; // Return no-op cleanup function
}
},
/**
* Register one-time IPC event listener
* @param {string} channel - IPC channel name
* @param {Function} callback - Callback function
*/
once: (channel, callback) => {
if (isValidChannel(channel, 'receive')) {
ipcRenderer.once(channel, (event, ...args) => callback(...args));
} else {
console.error(`Blocked attempt to listen to unauthorized channel: ${channel}`);
}
},
/**
* Remove specific IPC event listener
* NOTE: Due to contextBridge limitations, it's better to use the cleanup
* function returned by on() instead of this method
* @param {string} channel - IPC channel name
* @param {Function} callback - Callback function to remove
*/
removeListener: (channel, callback) => {
if (isValidChannel(channel, 'receive')) {
ipcRenderer.removeListener(channel, callback);
} else {
console.error(`Blocked attempt to remove listener from unauthorized channel: ${channel}`);
}
},
/**
* Remove all IPC event listeners for a channel
* @param {string} channel - IPC channel name
*/
removeAllListeners: (channel) => {
if (isValidChannel(channel, 'receive')) {
ipcRenderer.removeAllListeners(channel);
} else {
console.error(`Blocked attempt to remove all listeners from unauthorized channel: ${channel}`);
}
}
});