Apply additional security recommendations (#2050)

* enable sandbox

* enable CSP (umami tentatively works?) and reduce amount of ipc APIs exposed

* remove csp from index
This commit is contained in:
Kendall Garner
2026-05-23 05:09:22 +00:00
committed by GitHub
parent 0de1e1aa3e
commit 7befd70e21
19 changed files with 179 additions and 190 deletions
+15
View File
@@ -0,0 +1,15 @@
export const disableAutoUpdates = () => {
return process.env['DISABLE_AUTO_UPDATES'];
};
export const isMacOS = () => {
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
};
+2 -1
View File
@@ -7,9 +7,10 @@ import { pid } from 'node:process';
import process from 'process';
import { getMainWindow, sendToastToRenderer } from '../../../index';
import { createLog, isMacOS, isWindows } from '../../../utils';
import { createLog } from '../../../utils';
import { store } from '../settings';
import { isMacOS, isWindows } from '/@/main/env';
import { PlayerData } from '/@/shared/types/domain-types';
declare module 'node-mpv';
+1 -1
View File
@@ -1,8 +1,8 @@
import { BrowserWindow, globalShortcut, systemPreferences } from 'electron';
import { isLinux, isMacOS } from '../../../utils';
import { store } from '../settings';
import { isLinux, isMacOS } from '/@/main/env';
import { PlayerType } from '/@/shared/types/types';
export const enableMediaKeys = (window: BrowserWindow | null) => {
+1 -1
View File
@@ -9,8 +9,8 @@ import { deflate, gzip } from 'zlib';
import manifest from './manifest.json';
import { isLinux } from '/@/main/env';
import { getMainWindow } from '/@/main/index';
import { isLinux } from '/@/main/utils';
import { QueueSong } from '/@/shared/types/domain-types';
import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types';
import { PlayerRepeat, PlayerStatus, SongState } from '/@/shared/types/types';
+30 -12
View File
@@ -16,6 +16,7 @@ import {
protocol,
Rectangle,
screen,
session,
shell,
Tray,
} from 'electron';
@@ -33,16 +34,9 @@ import { store } from './features/core/settings';
import { canHandleVisualizerDisplayMedia } from './features/core/visualizer';
import MenuBuilder, { MenuPlaybackState } from './menu';
import './features';
import {
autoUpdaterLogInterface,
createLog,
disableAutoUpdates,
hotkeyToElectronAccelerator,
isLinux,
isMacOS,
isWindows,
} from './utils';
import { autoUpdaterLogInterface, createLog, hotkeyToElectronAccelerator } from './utils';
import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '/@/main/env';
import { PlayerRepeat, PlayerStatus, PlayerType, TitleTheme } from '/@/shared/types/types';
const ALPHA_UPDATER_CONFIG: {
@@ -477,6 +471,15 @@ const createTray = () => {
tray.setContextMenu(contextMenu);
};
const validateUrl = (url: string): boolean => {
// Minor security, really. Enforce only loading websites (http/https). file://
// URLs and the like should've already been blocked, but this is another check.
// Note that arbitrary web URLs are still allowed under this scheme, although
// that should really only be hit by Subsonic share url (or if artist homepage
// is allowed for ND extensions)
return url.startsWith('http://') || url.startsWith('https://');
};
async function createWindow(first = true): Promise<void> {
if (isDevelopment) {
await installExtensions().catch(console.log);
@@ -518,7 +521,7 @@ async function createWindow(first = true): Promise<void> {
devTools: true,
nodeIntegration: false,
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
sandbox: true,
webSecurity: !store.get('ignore_cors'),
},
width: 1440,
@@ -730,7 +733,9 @@ async function createWindow(first = true): Promise<void> {
// Open URLs in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
if (validateUrl(edata.url)) {
shell.openExternal(edata.url);
}
return { action: 'deny' };
});
@@ -770,7 +775,9 @@ async function createWindow(first = true): Promise<void> {
nativeTheme.themeSource = theme || 'dark';
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
if (validateUrl(details.url)) {
shell.openExternal(details.url);
}
return { action: 'deny' };
});
@@ -1017,6 +1024,17 @@ if (!singleInstance) {
return response;
});
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"script-src 'self' 'unsafe-inline' https://umami.jeffvli.org; style-src 'self' 'unsafe-inline'; media-src 'self' http: https: data: blob:; img-src 'self' http: https: data: blob:; connect-src 'self' http: https: ws: wss:; default-src 'self';",
],
},
});
});
createWindow();
if (store.get('window_enable_tray', true)) {
createTray();
-16
View File
@@ -18,22 +18,6 @@ if (process.env.NODE_ENV === 'development') {
};
}
export const disableAutoUpdates = () => {
return process.env['DISABLE_AUTO_UPDATES'];
};
export const isMacOS = () => {
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
};
export const hotkeyToElectronAccelerator = (hotkey: string) => {
let accelerator = hotkey;