[Mm 37198] enable global sandboxing to increase security (#1667)

* prevent creating new windows from popup windows

* enable sandbox

* fix windows detection logic

* disable on testing environment

Co-authored-by: = <=>
This commit is contained in:
Guillermo Vayá
2021-07-23 16:07:48 +02:00
committed by GitHub
parent c567449854
commit 4e75007362
8 changed files with 24 additions and 10 deletions

View File

@@ -3,6 +3,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import fs from 'fs'; import fs from 'fs';
import os from 'os';
import path from 'path'; import path from 'path';
import {EventEmitter} from 'events'; import {EventEmitter} from 'events';
@@ -43,11 +44,17 @@ export default class Config extends EventEmitter {
defaultConfigData?: ConfigType; defaultConfigData?: ConfigType;
buildConfigData?: BuildConfig; buildConfigData?: BuildConfig;
localConfigData?: ConfigType; localConfigData?: ConfigType;
useNativeWindow: boolean;
constructor(configFilePath: string) { constructor(configFilePath: string) {
super(); super();
this.configFilePath = configFilePath; this.configFilePath = configFilePath;
this.registryConfig = new RegistryConfig(); this.registryConfig = new RegistryConfig();
try {
this.useNativeWindow = os.platform() === 'win32' && (parseInt(os.release().split('.')[0], 10) < 10);
} catch {
this.useNativeWindow = false;
}
} }
// separating constructor from init so main can setup event listeners // separating constructor from init so main can setup event listeners
@@ -315,7 +322,7 @@ export default class Config extends EventEmitter {
*/ */
regenerateCombinedConfigData = () => { regenerateCombinedConfigData = () => {
// combine all config data in the correct order // combine all config data in the correct order
this.combinedData = Object.assign({}, this.defaultConfigData, this.localConfigData, this.buildConfigData, this.registryConfigData); this.combinedData = Object.assign({}, this.defaultConfigData, this.localConfigData, this.buildConfigData, this.registryConfigData, {useNativeWindow: this.useNativeWindow});
// remove unecessary data pulled from default and build config // remove unecessary data pulled from default and build config
delete this.combinedData!.defaultTeams; delete this.combinedData!.defaultTeams;

View File

@@ -190,6 +190,9 @@ function initializeBeforeAppReady() {
log.error('No config loaded'); log.error('No config loaded');
return; return;
} }
if (process.env.NODE_ENV !== 'test') {
app.enableSandbox();
}
certificateStore = new CertificateStore(path.resolve(app.getPath('userData'), 'certificate.json')); certificateStore = new CertificateStore(path.resolve(app.getPath('userData'), 'certificate.json'));
trustedOriginsStore = new TrustedOriginsStore(path.resolve(app.getPath('userData'), 'trustedOrigins.json')); trustedOriginsStore = new TrustedOriginsStore(path.resolve(app.getPath('userData'), 'trustedOrigins.json'));
trustedOriginsStore.load(); trustedOriginsStore.load();

View File

@@ -4,7 +4,6 @@
'use strict'; 'use strict';
import os from 'os';
import {ipcRenderer, contextBridge} from 'electron'; import {ipcRenderer, contextBridge} from 'electron';
contextBridge.exposeInMainWorld('ipcRenderer', { contextBridge.exposeInMainWorld('ipcRenderer', {
@@ -13,10 +12,6 @@ contextBridge.exposeInMainWorld('ipcRenderer', {
invoke: ipcRenderer.invoke, invoke: ipcRenderer.invoke,
}); });
contextBridge.exposeInMainWorld('os', {
isWindows10: os.platform() === 'win32' && os.release().startsWith('10'),
});
contextBridge.exposeInMainWorld('process', { contextBridge.exposeInMainWorld('process', {
platform: process.platform, platform: process.platform,
env: { env: {

View File

@@ -82,6 +82,12 @@ const generateDidStartNavigation = (getServersFunction: () => TeamWithTabs[]) =>
}; };
}; };
const denyNewWindow = (event: Event, url: string) => {
event.preventDefault();
log.warn(`Prevented popup window to open a new window to ${url}.`);
return null;
};
const generateNewWindowListener = (getServersFunction: () => TeamWithTabs[], spellcheck?: boolean) => { const generateNewWindowListener = (getServersFunction: () => TeamWithTabs[], spellcheck?: boolean) => {
return (event: Event, url: string) => { return (event: Event, url: string) => {
const parsedURL = urlUtils.parseURL(url); const parsedURL = urlUtils.parseURL(url);
@@ -160,12 +166,14 @@ const generateNewWindowListener = (getServersFunction: () => TeamWithTabs[], spe
show: false, show: false,
center: true, center: true,
webPreferences: { webPreferences: {
nativeWindowOpen: true,
nodeIntegration: process.env.NODE_ENV === 'test', nodeIntegration: process.env.NODE_ENV === 'test',
contextIsolation: process.env.NODE_ENV !== 'test', contextIsolation: process.env.NODE_ENV !== 'test',
spellcheck: (typeof spellcheck === 'undefined' ? true : spellcheck), spellcheck: (typeof spellcheck === 'undefined' ? true : spellcheck),
enableRemoteModule: process.env.NODE_ENV === 'test', enableRemoteModule: process.env.NODE_ENV === 'test',
}, },
}); });
popupWindow.webContents.on('new-window', denyNewWindow);
popupWindow.once('ready-to-show', () => { popupWindow.once('ready-to-show', () => {
popupWindow!.show(); popupWindow!.show();
}); });

View File

@@ -64,6 +64,7 @@ type Props = {
openMenu: () => void; openMenu: () => void;
darkMode: boolean; darkMode: boolean;
appName: string; appName: string;
useNativeWindow: boolean;
}; };
type State = { type State = {
@@ -358,7 +359,7 @@ export default class MainPage extends React.PureComponent<Props, State> {
} }
let titleBarButtons; let titleBarButtons;
if (window.os.isWindows10) { if (window.process.platform === 'win32' && !this.props.useNativeWindow) {
titleBarButtons = ( titleBarButtons = (
<span className='title-bar-btns'> <span className='title-bar-btns'>
<div <div

View File

@@ -126,6 +126,7 @@ class Root extends React.PureComponent<Record<string, never>, State> {
openMenu={this.openMenu} openMenu={this.openMenu}
darkMode={config.darkMode} darkMode={config.darkMode}
appName={config.appName} appName={config.appName}
useNativeWindow={config.useNativeWindow}
/> />
); );
} }

View File

@@ -102,6 +102,8 @@ export type RegistryConfig = {
export type CombinedConfig = ConfigV3 & BuildConfig & { export type CombinedConfig = ConfigV3 & BuildConfig & {
registryTeams: Team[]; registryTeams: Team[];
appName: string; appName: string;
useNativeWindow: boolean;
} }
export type LocalConfiguration = Config & { export type LocalConfiguration = Config & {

View File

@@ -10,9 +10,6 @@ declare global {
on: (channel: string, listener: (...args: any[]) => void) => void; on: (channel: string, listener: (...args: any[]) => void) => void;
invoke: typeof ipcRenderer.invoke; invoke: typeof ipcRenderer.invoke;
}; };
os: {
isWindows10: boolean;
};
process: { process: {
platform: NodeJS.Platform; platform: NodeJS.Platform;
env: { env: {