Skip to content
This repository was archived by the owner on May 24, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions packages/fether-electron/src/main/app/menu/template/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ const getPreferences = fetherApp => {
settings.set('fether-language', 'en');
fetherApp.setupMenu(fetherApp);
// Frontend change language
fetherApp.win.webContents.emit('set-language', 'en');
fetherApp.win.webContents.send('send-to-renderer', {
action: 'SET_LANGUAGE_RESPONSE',
from: 'fether:electron',
payload: 'en'
});
}
},
{
Expand All @@ -39,7 +43,11 @@ const getPreferences = fetherApp => {
settings.set('fether-language', 'de');
fetherApp.setupMenu(fetherApp);
// Frontend change language
fetherApp.win.webContents.emit('set-language', 'de');
fetherApp.win.webContents.send('send-to-renderer', {
action: 'SET_LANGUAGE_RESPONSE',
from: 'fether:electron',
payload: 'de'
});
}
}
]
Expand Down
63 changes: 53 additions & 10 deletions packages/fether-electron/src/main/app/messages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,81 @@
// SPDX-License-Identifier: BSD-3-Clause

import { checkClockSync, signerNewToken } from '@parity/electron';
import settings from 'electron-settings';

import Pino from '../utils/pino';
import { bundledParityPath } from '../utils/paths';
import cli from '../cli';
import Pino from '../utils/pino';
import { TRUSTED_LOOPBACK } from '../constants';

const pino = Pino();

/**
* Handle all asynchronous messages from renderer to main.
*/
export default async (fetherApp, event, action, ...args) => {
export default async (fetherApp, event, data) => {
try {
if (!action) {
pino.debug(
`Received IPC message from ${data.from}, with data ${JSON.stringify(
data
)}`
);
if (!data) {
return;
}
switch (action) {
case 'app-right-click': {

switch (data.action) {
case 'APP_RIGHT_CLICK_REQUEST': {
if (!fetherApp.win) {
return;
}
fetherApp.contextWindowMenu.getMenu().popup({ window: fetherApp.win });
break;
}
case 'check-clock-sync': {
checkClockSync().then(t => {
event.sender.send('check-clock-sync-reply', t);
case 'CHECK_CLOCK_SYNC_REQUEST': {
const payload = await checkClockSync();
event.sender.send('send-to-renderer', {
action: 'CHECK_CLOCK_SYNC_RESPONSE',
from: 'fether:electron',
payload
});

break;
}
case 'SET_LANGUAGE_REQUEST': {
event.sender.send('send-to-renderer', {
action: 'SET_LANGUAGE_RESPONSE',
from: 'fether:electron',
payload: settings.get('fether-language')
});
break;
}
case 'signer-new-token': {
case 'SIGNER_NEW_TOKEN_REQUEST': {
const token = await signerNewToken({ parityPath: bundledParityPath });
// Send back the token to the renderer process
event.sender.send('signer-new-token-reply', token);
event.sender.send('send-to-renderer', {
action: 'SIGNER_NEW_TOKEN_RESPONSE',
from: 'fether:electron',
payload: token
});
break;
}
case 'WS_INTERFACE_REQUEST': {
event.sender.send('send-to-renderer', {
action: 'WS_INTERFACE_RESPONSE',
from: 'fether:electron',
payload: TRUSTED_LOOPBACK
});

break;
}
case 'WS_PORT_REQUEST': {
event.sender.send('send-to-renderer', {
action: 'WS_PORT_RESPONSE',
from: 'fether:electron',
payload: cli.wsPort
});

break;
}
default:
Expand Down
8 changes: 2 additions & 6 deletions packages/fether-electron/src/main/app/methods/setupGlobals.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@
//
// SPDX-License-Identifier: BSD-3-Clause

import { DEFAULT_WS_PORT, IS_PROD, TRUSTED_LOOPBACK } from '../constants';
import cli from '../cli';
import { IS_PROD } from '../constants';

function setupGlobals () {
// Globals for fether-react parityStore
// Globals for preload script
global.IS_PROD = IS_PROD;
global.defaultWsInterface = TRUSTED_LOOPBACK;
global.defaultWsPort = DEFAULT_WS_PORT;
global.wsPort = cli.wsPort;
}

export default setupGlobals;
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@

import electron from 'electron';

import { IS_PROD } from '../constants';
import { CSP } from '../utils/csp';
import messages from '../messages';
import Pino from '../utils/pino';

const pino = Pino();
const { ipcMain, session } = electron;

function setupRequestListeners (fetherApp) {
// Listen to messages from renderer process
ipcMain.on('asynchronous-message', (...args) => {
ipcMain.on('send-to-main', (...args) => {
return messages(fetherApp, ...args);
});

Expand All @@ -35,27 +31,6 @@ function setupRequestListeners (fetherApp) {
callback({ requestHeaders: details.requestHeaders }); // eslint-disable-line
}
);

// Content Security Policy (CSP)
// Note: `onHeadersReceived` will not be called in prod, because we use the
// file:// protocol: https://electronjs.org/docs/tutorial/security#csp-meta-tag
// Instead, the CSP are the ones in the meta tag inside index.html
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
pino.debug(
`Configuring Content-Security-Policy for environment ${
IS_PROD ? 'production' : 'development'
}`
);

/* eslint-disable */
callback({
responseHeaders: {
...details.responseHeaders,
"Content-Security-Policy": [CSP]
}
});
/* eslint-enable */
});
}

export default setupRequestListeners;
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ pino.info(
* fether-electron/src/main/index.js, which is where we only
* permit requests from trusted paths.
*
* WARNING: DO NOT add the custom CLI interface `cli.wsInterface` as a
* trusted host. This may avoid Fether being launched with a
* malicious remote `cli.wsInterface` and sending sensitive user information
* (i.e. account password) over RPC.
* See https://github.com/paritytech/fether/pull/451#discussion_r268732256
*
* Note: We also disallows users from using Fether
* with a remote node.
* WARNING: SSH tunnels from an attacker are still possible.
Expand Down Expand Up @@ -164,7 +158,7 @@ const SECURITY_OPTIONS = {
* https://stackoverflow.com/questions/55164360/with-contextisolation-true-is-it-possible-to-use-ipcrenderer
* Currently experimental and may change or be removed in future Electron releases.
*/
contextIsolation: false, // Should be enabled
contextIsolation: true,
/**
* Isolate access to Electron/Node.js from the Fether web app, by creating
* a bridge which plays the role of an API between main and renderer
Expand Down
10 changes: 2 additions & 8 deletions packages/fether-electron/src/main/app/parityEthereum/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const pino = Pino();
let hasCalledInitParityEthereum = false;

class ParityEthereum {
constructor (fetherAppWindow) {
constructor () {
if (hasCalledInitParityEthereum) {
throw new Error('Unable to initialise Parity Ethereum more than once');
}
Expand Down Expand Up @@ -50,13 +50,7 @@ class ParityEthereum {
await this.run();
pino.info('Running Parity Ethereum');
resolve(true);
})
.then(isRunning => {
// Notify the renderers
fetherAppWindow.webContents.send('parity-running', isRunning);
global.isParityRunning = isRunning; // Send this variable to renderers via IPC
})
.catch(handleError);
}).catch(handleError);
}

isRunning = async () => {
Expand Down
2 changes: 0 additions & 2 deletions packages/fether-electron/src/main/app/utils/csp.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ const CSP_CONFIG = {
mediaSrc: "media-src 'none';",
// Disallow fonts and `<webview>` objects
objectSrc: "object-src 'none';",
// Disallow prefetching.
prefetchSrc: "prefetch-src 'none';",
scriptSrc: !IS_PROD
? // Only allow `http:` and `unsafe-eval` in dev mode (required by create-react-app)
"script-src 'self' file: http: blob: 'unsafe-inline' 'unsafe-eval';"
Expand Down
85 changes: 46 additions & 39 deletions packages/fether-electron/static/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,55 @@
* network stack).
*
* Reference: https://slack.engineering/interops-labyrinth-sharing-code-between-web-electron-apps-f9474d62eccc
*
* This preload script handles communication between main and renderer processes.
* https://github.com/electron/electron/issues/13130
*/

const { ipcRenderer, remote } = require('electron');

const IS_PROD = remote.getGlobal('IS_PROD');

function init () {
console.log(
`Initialising Electron Preload Script in environment: ${
IS_PROD ? 'production' : 'development'
}`
);

/**
* Expose only a bridging API to the Fether web app.
* Set methods on global `window`. Additional methods added later by web app
*
* Do not expose functionality or APIs that could compromise the computer
* such as core Electron (i.e. `electron`, `remote`), IPC (`ipcRenderer`)
* or Node.js modules like `require`.
*
* Note however that we require `ipcRenderer` to be exposed for communication
* between the main process and the renderer process. Hence why
* we have had no other choice but to set `contextIsolation: false`
*
* Example 1: Do not expose as `window.bridge.electron` or `window.bridge.remote`.
* Example 2: `require` should not be defined in Chrome Developer Tools Console.
*/
window.bridge = {
currentWindowWebContentsAddListener: remote.getCurrentWindow().webContents
.addListener,
currentWindowWebContentsRemoveListener: remote.getCurrentWindow()
.webContents.removeListener,
currentWindowWebContentsReload: remote.getCurrentWindow().webContents
.reload,
defaultWsInterface: remote.getGlobal('defaultWsInterface'),
defaultWsPort: remote.getGlobal('defaultWsPort'),
ipcRenderer,
isParityRunningStatus: remote.getGlobal('isParityRunning'),
IS_PROD,
wsPort: remote.getGlobal('wsPort')
};
const RENDERER_ORIGIN =
remote.getGlobal('IS_PROD') === true ? 'file://' : 'http://localhost:3000';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone has an idea how not to hardcode these origins, I'm all ears


/**
* Handler that receives an IPC message from the main process, and passes it
* down to the renderer process.
*
* @param {*} _event The IPC event we receive from the main process.
* @param {*} data The data of the IPC message.
*/
function receiveIpcMessage (_event, data) {
window.postMessage(data, RENDERER_ORIGIN);
}

/**
* Handler that receives a post message from the renderer process, and passes
* it down to the main process.
*
* @param {*} _event The post message event we receive from the renderer process.
*/
function receivePostMessage (event) {
const { data, origin } = event;

if (origin !== RENDERER_ORIGIN) {
return;
}

if (!data) {
return;
}

const { from } = data;

if (from === 'fether:electron') {
// Since `payload` and `frontend` have the same origin, we use the `from`
// field to differentiate who's sending the postMessage to whom. If the
// message has been sent by `electron`, we ignore.
return;
}

ipcRenderer.send('send-to-main', data);
}

init();
ipcRenderer.on('send-to-renderer', receiveIpcMessage);
window.addEventListener('message', receivePostMessage);
Loading