Skip to content

Commit f8eabae

Browse files
committed
feat(security): restrict apis and validate urls
1 parent 1905e59 commit f8eabae

File tree

2 files changed

+70
-19
lines changed

2 files changed

+70
-19
lines changed

src/main/index.ts

+62-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BrowserWindow, app, ipcMain, shell } from 'electron';
1+
import { BrowserWindow, Event, app, ipcMain, shell } from 'electron';
22
import { join } from 'path';
33
import { is, optimizer, platform } from '@electron-toolkit/utils';
44
import { createLogger } from './logger';
@@ -11,39 +11,40 @@ const logger = createLogger('main');
1111

1212
app.setName('Phoenix');
1313
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-
});
2114

2215
function createWindow(): void {
2316
logger.info('creating main window');
2417

2518
const mainWindow = new BrowserWindow({
2619
width: 900,
2720
height: 670,
28-
show: false,
21+
show: false, // to avoid a blank window until contents loaded
2922
autoHideMenuBar: true,
3023
webPreferences: {
3124
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,
3431
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,
3541
},
3642
});
3743

3844
mainWindow.on('ready-to-show', (): void => {
3945
mainWindow.show();
4046
});
4147

42-
mainWindow.webContents.setWindowOpenHandler((details) => {
43-
shell.openExternal(details.url);
44-
return { action: 'deny' };
45-
});
46-
4748
// HMR for renderer base on electron-vite cli.
4849
// Load the remote URL for development or the local html file for production.
4950
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
@@ -58,8 +59,8 @@ function createWindow(): void {
5859
// This method will be called when Electron has finished
5960
// initialization and is ready to create browser windows.
6061
// 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');
6364

6465
// Default open or close DevTools by F12 in development
6566
// and ignore CommandOrControl + R in production.
@@ -76,11 +77,54 @@ app.whenReady().then((): void => {
7677
}
7778
});
7879

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+
/^(www.)?github\.com$/i,
85+
/^(www.)?play\.net$/i,
86+
/^elanthipedia\.play\.net$/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+
79121
// Listen for events emitted by the preload api
80122
ipcMain.handle('ping', async (): Promise<string> => {
81123
// Return response to renderer
82124
return 'pong';
83125
});
126+
127+
createWindow();
84128
});
85129

86130
// Quit when all windows are closed, except on macOS.
+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
function HelloWorld(): JSX.Element {
2-
return <p>Hello World</p>;
2+
return (
3+
<div>
4+
<p>Hello World</p>
5+
<p>
6+
<a href="http://play.net/dr">DragonRealms</a>
7+
</p>
8+
</div>
9+
);
310
}
411

512
export default HelloWorld;

0 commit comments

Comments
 (0)