1
- import { BrowserWindow , app , ipcMain , shell } from 'electron' ;
1
+ import { BrowserWindow , Event , app , ipcMain , shell } from 'electron' ;
2
2
import { join } from 'path' ;
3
3
import { is , optimizer , platform } from '@electron-toolkit/utils' ;
4
4
import { createLogger } from './logger' ;
@@ -11,39 +11,40 @@ const logger = createLogger('main');
11
11
12
12
app . setName ( 'Phoenix' ) ;
13
13
app . setAppUserModelId ( 'com.github.dragonrealms-phoenix.phoenix' ) ;
14
- app . setAboutPanelOptions ( {
15
- applicationName : app . name ,
16
- applicationVersion : app . getVersion ( ) ,
17
- version : `${ app . getVersion ( ) } -${ import . meta. env . MAIN_VITE_GIT_SHORT_HASH } ` ,
18
- authors : [ 'Katoak' ] ,
19
- website : 'https://github.com/dragonrealms-phoenix/phoenix' ,
20
- } ) ;
21
14
22
15
function createWindow ( ) : void {
23
16
logger . info ( 'creating main window' ) ;
24
17
25
18
const mainWindow = new BrowserWindow ( {
26
19
width : 900 ,
27
20
height : 670 ,
28
- show : false ,
21
+ show : false , // to avoid a blank window until contents loaded
29
22
autoHideMenuBar : true ,
30
23
webPreferences : {
31
24
preload : join ( __dirname , '../preload/index.js' ) ,
32
- sandbox : false ,
33
- nodeIntegration : false ,
25
+ /**
26
+ * Security Best Practices
27
+ * https://www.electronjs.org/docs/latest/tutorial/security
28
+ * https://github.com/moloch--/reasonably-secure-electron
29
+ */
30
+ allowRunningInsecureContent : false ,
34
31
contextIsolation : true ,
32
+ experimentalFeatures : false ,
33
+ navigateOnDragDrop : false ,
34
+ nodeIntegration : false ,
35
+ nodeIntegrationInSubFrames : false ,
36
+ nodeIntegrationInWorker : false ,
37
+ safeDialogs : true ,
38
+ sandbox : true ,
39
+ webSecurity : true ,
40
+ webviewTag : false ,
35
41
} ,
36
42
} ) ;
37
43
38
44
mainWindow . on ( 'ready-to-show' , ( ) : void => {
39
45
mainWindow . show ( ) ;
40
46
} ) ;
41
47
42
- mainWindow . webContents . setWindowOpenHandler ( ( details ) => {
43
- shell . openExternal ( details . url ) ;
44
- return { action : 'deny' } ;
45
- } ) ;
46
-
47
48
// HMR for renderer base on electron-vite cli.
48
49
// Load the remote URL for development or the local html file for production.
49
50
if ( is . dev && process . env [ 'ELECTRON_RENDERER_URL' ] ) {
@@ -58,8 +59,8 @@ function createWindow(): void {
58
59
// This method will be called when Electron has finished
59
60
// initialization and is ready to create browser windows.
60
61
// Some APIs can only be used after this event occurs.
61
- app . whenReady ( ) . then ( ( ) : void => {
62
- createWindow ( ) ;
62
+ app . once ( 'ready' , ( ) => {
63
+ app . setAsDefaultProtocolClient ( 'app' ) ;
63
64
64
65
// Default open or close DevTools by F12 in development
65
66
// and ignore CommandOrControl + R in production.
@@ -76,11 +77,54 @@ app.whenReady().then((): void => {
76
77
}
77
78
} ) ;
78
79
80
+ // Disable or limit creation of new windows to protect app and users.
81
+ // https://www.electronjs.org/docs/latest/tutorial/security
82
+ app . on ( 'web-contents-created' , ( _ , contents ) => {
83
+ const allowedDomains = [
84
+ / ^ ( w w w .) ? g i t h u b \. c o m $ / i,
85
+ / ^ ( w w w .) ? p l a y \. n e t $ / i,
86
+ / ^ e l a n t h i p e d i a \. p l a y \. n e t $ / i,
87
+ ] ;
88
+
89
+ const isAllowedDomain = ( domain : string ) : boolean => {
90
+ return allowedDomains . some ( ( d ) => d . test ( domain ) ) ;
91
+ } ;
92
+
93
+ const blockOrOpenURL = (
94
+ event : Event < Electron . WebContentsWillNavigateEventParams > ,
95
+ url : string
96
+ ) : void => {
97
+ const domain = new URL ( url ) . hostname ;
98
+ // If the domain is allowed, open it in the user's default browser.
99
+ if ( isAllowedDomain ( domain ) ) {
100
+ logger . info ( 'opening url in default browser' , { url } ) ;
101
+ setImmediate ( ( ) => {
102
+ shell . openExternal ( url ) ;
103
+ } ) ;
104
+ } else {
105
+ logger . warn ( 'blocked window navigation' , { url } ) ;
106
+ }
107
+ event . preventDefault ( ) ;
108
+ } ;
109
+
110
+ contents . on ( 'will-navigate' , ( event , url ) => {
111
+ logger . info ( 'will-navigate' , { url } ) ;
112
+ blockOrOpenURL ( event , url ) ;
113
+ } ) ;
114
+
115
+ contents . on ( 'will-redirect' , ( event , url ) => {
116
+ logger . info ( 'will-redirect' , { url } ) ;
117
+ blockOrOpenURL ( event , url ) ;
118
+ } ) ;
119
+ } ) ;
120
+
79
121
// Listen for events emitted by the preload api
80
122
ipcMain . handle ( 'ping' , async ( ) : Promise < string > => {
81
123
// Return response to renderer
82
124
return 'pong' ;
83
125
} ) ;
126
+
127
+ createWindow ( ) ;
84
128
} ) ;
85
129
86
130
// Quit when all windows are closed, except on macOS.
0 commit comments