Skip to content

Commit e7c6b15

Browse files
authored
Merge pull request #7 from dragonrealms-phoenix/develop
Increase Security, Refactor Menu, Refactor Build Script
2 parents a0ab270 + 4d03894 commit e7c6b15

File tree

16 files changed

+459
-771
lines changed

16 files changed

+459
-771
lines changed

.github/workflows/build.yml

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ jobs:
4848
run: |
4949
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> .env
5050
51-
echo "MAIN_VITE_SENTRY_DSN=${{ secrets.MAIN_VITE_SENTRY_DSN }}" >> .env
52-
echo "MAIN_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.MAIN_VITE_SENTRY_CRASH_REPORT_DSN }}" >> .env
51+
echo "MAIN_VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
52+
echo "MAIN_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.SENTRY_CRASH_REPORT_DSN }}" >> .env
5353
54-
echo "RENDERER_VITE_SENTRY_DSN=${{ secrets.RENDERER_VITE_SENTRY_DSN }}" >> .env
55-
echo "RENDERER_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.RENDERER_VITE_SENTRY_CRASH_REPORT_DSN }}" >> .env
54+
echo "RENDERER_VITE_SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env
55+
echo "RENDERER_VITE_SENTRY_CRASH_REPORT_DSN=${{ secrets.SENTRY_CRASH_REPORT_DSN }}" >> .env
5656
5757
- name: Install dependencies
5858
run: yarn install

electron.vite.config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dotenv/config';
12
import { execSync } from 'child_process';
23
import { sentryVitePlugin } from '@sentry/vite-plugin';
34
import react from '@vitejs/plugin-react';
@@ -13,6 +14,7 @@ const sentryPlugin = sentryVitePlugin({
1314
org: 'dragonrealms-phoenix',
1415
project: 'phoenix',
1516
telemetry: false,
17+
authToken: process.env.SENTRY_AUTH_TOKEN,
1618
disable: process.env.VITE_PLUGIN_SENTRY_ENABLE !== 'true',
1719
}) as PluginOption;
1820

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"lint:fix": "yarn lint --fix",
3636
"lint:staged": "lint-staged --concurrent 1",
3737
"format": "yarn prettier:fix && yarn lint:fix",
38-
"typecheck:preload": "tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/api.ts && yarn prettier:fix ./src/preload/api.d.ts",
38+
"typecheck:preload": "tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/index.ts && yarn prettier:fix ./src/preload/index.d.ts",
3939
"typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false",
4040
"typecheck:web": "tsc --noEmit -p tsconfig.web.json --composite false",
4141
"typecheck": "yarn typecheck:preload && yarn typecheck:node && yarn typecheck:web",
@@ -115,6 +115,7 @@
115115
"@typescript-eslint/parser": "^6.7.3",
116116
"@vitejs/plugin-react": "^4.1.0",
117117
"cross-env": "^7.0.3",
118+
"dotenv": "^16.3.1",
118119
"electron": "^26.2.4",
119120
"electron-builder": "^24.6.4",
120121
"electron-vite": "^1.0.28",

src/main/index.ts

+64-20
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
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';
5-
import { MenuBuilder } from './menu';
5+
import { initializeMenu } from './menu';
66
import { initializeSentry } from './sentry';
77

88
initializeSentry();
@@ -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']) {
@@ -52,14 +53,14 @@ function createWindow(): void {
5253
mainWindow.loadFile(join(__dirname, '../renderer/index.html'));
5354
}
5455

55-
new MenuBuilder(mainWindow).buildMenu();
56+
initializeMenu(mainWindow);
5657
}
5758

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.

0 commit comments

Comments
 (0)