From 9a96da6ba26d50f9823ae4a3d929391615891fa4 Mon Sep 17 00:00:00 2001 From: khlam <4841220+khlam@users.noreply.github.com> Date: Sat, 19 Oct 2019 20:38:47 -0700 Subject: [PATCH] security rewrite --- css/style.css | 70 ++--------------------------------- index.html | 15 ++------ main.js | 67 ++++++++++++++++++++++++++------- preload.js | 56 ++++++++++++++++++++++------ renderer.js | 100 +++++++++++++++++++++++--------------------------- 5 files changed, 151 insertions(+), 157 deletions(-) diff --git a/css/style.css b/css/style.css index ec76102..2294fd6 100644 --- a/css/style.css +++ b/css/style.css @@ -4,82 +4,18 @@ html, body{ margin:0px; padding: 0; overflow: hidden; - font-family: 'Roboto', sans-serif; - } +} div { width:100%; - height:97%; + height:100%; } webview { height:100%; - height:97%; + height:100%; } ::-webkit-scrollbar { display: none; } - - -.title-bar { - -webkit-app-region: drag; - margin: 0; - display: flex; - background-color: #212226; - width: 100%; - height: 3%; - } - -.menu-button-container { - background-color: #212226; - display: flex; - align-items: center; - flex-grow: 1; -} - -.status { - background-color: #212226; - color: #FFFFFF; - display: flex; - justify-content: center; - align-items: center; - flex-grow: 1; -} - -.window-controls-container { - background-color: #212226; - display: flex; - justify-content: flex-end; - align-items: center; - flex-grow: 1; -} - -.minimize-button { - border:none; - color: #FFFFFF; - -webkit-app-region: no-drag; - background-color: transparent; - margin: 1px; - width: 20px; - height: 20px; -} - -.minimize-button:hover { - background-color: #99AAB5; -} - -.close-button { - border:none; - color: #FFFFFF; - -webkit-app-region: no-drag; - background-color: transparent; - margin: 1px; - margin-right: 3px; - width: 20px; - height: 20px; -} - -.close-button:hover { - background-color: #B22222; -} \ No newline at end of file diff --git a/index.html b/index.html index ee9234d..f2b1a5b 100644 --- a/index.html +++ b/index.html @@ -2,21 +2,14 @@ + + -
-
-
-
- - -
-
- - - + + diff --git a/main.js b/main.js index 3243654..61e6265 100644 --- a/main.js +++ b/main.js @@ -1,7 +1,8 @@ // Modules to control application life and create native browser window -const {app, BrowserWindow, ipcMain} = require('electron') +const { app, BrowserWindow, ipcMain} = require('electron') const path = require('path') const ioHook = require('iohook') +const URL = require('url').URL // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. @@ -13,15 +14,17 @@ function createWindow () { // Create the browser window. mainWindow = new BrowserWindow({ width: 1230, - height: 730, + height: 800, icon: './assets/icon.ico', - frame: false, webPreferences: { preload: path.join(__dirname, 'preload.js'), - nodeIntegration: true, + nodeIntegration: false, // https://electronjs.org/docs/tutorial/security#2-do-not-enable-nodejs-integration-for-remote-content + enableRemoteModule: false, // https://electronjs.org/docs/tutorial/security#15-disable-the-remote-module webviewTag: true } }) + + // Set Dev mode if (process.argv.length === 3) { if (process.argv[2] === 'dev'){ devMode = true @@ -65,19 +68,47 @@ app.on('activate', function () { if (mainWindow === null) createWindow() }) -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and require them here. +/* Security Stuff */ +// https://electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation +app.on('web-contents-created', (event, contents) => { + contents.on('will-attach-webview', (event, webPreferences, params) => { + // Strip away preload scripts if unused or verify their location is legitimate + delete webPreferences.preload + delete webPreferences.preloadURL + + // Disable Node.js integration + webPreferences.nodeIntegration = false + + // Verify discordapp.com is being loaded + if (!params.src.startsWith('https://discordapp.com/')) { + event.preventDefault() + } + }) +}) + +// https://electronjs.org/docs/tutorial/security#12-disable-or-limit-navigation +app.on('web-contents-created', (event, contents) => { + contents.on('will-navigate', (event, navigationUrl) => { + const parsedUrl = new URL(navigationUrl) + + if (parsedUrl.origin !== 'https://discordapp.com/') { // Limit navigation to discordapp.com; not really relevant + event.preventDefault() + } + }) +}) + +// https://electronjs.org/docs/tutorial/security#13-disable-or-limit-creation-of-new-windows +app.on('web-contents-created', (event, contents) => { + contents.on('new-window', async (event, navigationUrl) => { + event.preventDefault() // External links just don't open + }) +}) +/* ---- */ 'use strict'; let selfMute = false let isConnected = false -function muteMic() { - console.log("Muted") - mainWindow.webContents.send('micClose', 'mic-closed') - mainWindow.setTitle("MUTED") -} - function unmuteMic() { if (isConnected === true && selfMute === false) { console.log("Talking") @@ -86,6 +117,12 @@ function unmuteMic() { } } +function muteMic() { + console.log("Muted") + mainWindow.webContents.send('micClose', 'mic-closed') + mainWindow.setTitle("MUTED") +} + app.on('ready', event => { ioHook.start(); console.log(`Dev Mode: ${devMode}`) @@ -105,21 +142,23 @@ ioHook.on('mouseup', event => { ipcMain.on('asynchronous-message', (event, msg) => { if (msg === 'connected') { + console.log("User connected to Discord VOIP server") isConnected = true muteMic() } if (msg === 'disconnected') { + console.log("User disconnected to Discord VOIP server") isConnected = false } if (msg === 'self-muted') { - console.log("self-muted") + console.log("User self-muted") selfMute = true } if (msg === 'self-unmuted') { - console.log("self-unmuted") + console.log("User self-unmuted") selfMute = false } diff --git a/preload.js b/preload.js index b8d217f..8d1de40 100644 --- a/preload.js +++ b/preload.js @@ -1,12 +1,46 @@ -// All of the Node.js APIs are available in the preload process. -// It has the same sandbox as a Chrome extension. -window.addEventListener('DOMContentLoaded', () => { - const replaceText = (selector, text) => { - const element = document.getElementById(selector) - if (element) element.innerText = text - } - - for (const type of ['chrome', 'node', 'electron']) { - replaceText(`${type}-version`, process.versions[type]) - } +const { ipcRenderer } = require('electron') + +window.addEventListener("DOMContentLoaded", () => { + ipcRenderer.send('asynchronous-message', 'DOMready') }) + +// Send commands from main to renderer +ipcRenderer.on('devMode', (event, msg) => { + console.log(`Dev Mode: ${msg}`) + window.postMessage({ type: "devMode", text: `${msg}` }, "*") +}) + +ipcRenderer.on('micOpen', (event, msg) => { + window.postMessage({ type: "micOpen"}, "*") +}) + +ipcRenderer.on('micClose', (event, msg) => { + window.postMessage({ type: "micClose"}, "*") +}) + +// Handle events sent from renderer, sends it to main +window.addEventListener( + "message", + event => { + if (event.origin === "file://" && event.source === window) { + + if (event.data.type === 'connected'){ + ipcRenderer.send('asynchronous-message', 'connected') + } + + if (event.data.type === 'disconnected'){ + ipcRenderer.send('asynchronous-message', 'disconnected') + } + + if (event.data.type === 'self-muted'){ + ipcRenderer.send('asynchronous-message', 'self-muted') + } + + if (event.data.type === 'self-unmuted'){ + ipcRenderer.send('asynchronous-message', 'self-unmuted') + } + + } + }, + false +) \ No newline at end of file diff --git a/renderer.js b/renderer.js index b550396..6215697 100644 --- a/renderer.js +++ b/renderer.js @@ -1,5 +1,3 @@ -const { remote, ipcRenderer } = require('electron') - function removeBloat(webview) { webview.executeJavaScript(`document.getElementsByClassName("anchor-3Z-8Bb anchorUnderlineOnHover-2ESHQB")[0].remove();`) // Remove top-right help button webview.executeJavaScript(`document.getElementsByClassName("contents-18-Yxp button-3AYNKb button-2vd_v_")[0].remove();`) // Remove gift from chat @@ -18,43 +16,41 @@ onload = () => { const webview = document.querySelector('webview') let muteTimeout = null - ipcRenderer.send('asynchronous-message', 'DOMready') - - // Execute JS into the webview to detect when logging in is complete + // Insert JS to detect when discord finishes loading webview.addEventListener('did-finish-load', function() { - webview.executeJavaScript(` - let dlButton = document.getElementsByClassName("listItem-2P_4kh"); - t = setInterval(function(){ - if(dlButton.length != 0) { - console.log("discord-load-complete") - clearInterval(t) - }else { - console.log("waiting for load") - } - }, 500); - `) - }); + webview.executeJavaScript(` + let dlButton = document.getElementsByClassName("listItem-2P_4kh"); + t = setInterval(function(){ + if(dlButton.length != 0) { + console.log("discord-load-complete") + clearInterval(t) + }else { + console.log("waiting for load") + } + }, 500); + `) + }); - - webview.addEventListener('console-message', (e) => { + // Send commands to preload.js + webview.addEventListener('console-message', (e) => { if (e.message === "Constructed RTCPeerConnection") { console.log("Connected to server") - ipcRenderer.send('asynchronous-message', 'connected') + window.postMessage({ type: "connected"}, "*") } if (e.message === "Close RTCPeerConnection") { console.log("Disconnected from server") - ipcRenderer.send('asynchronous-message', 'disconnected') + window.postMessage({ type: "disconnected"}, "*") } if (e.message === "muted") { console.log("Self Muted in Discord") - ipcRenderer.send('asynchronous-message', 'self-muted') + window.postMessage({ type: "self-muted"}, "*") } if (e.message === "unmuted") { - console.log("Self Muted in Discord") - ipcRenderer.send('asynchronous-message', 'self-unmuted') + console.log("Self Un-Muted in Discord") + window.postMessage({ type: "self-unmuted"}, "*") } if (e.message === "signalingState => stable, negotiation needed: false") { @@ -81,36 +77,32 @@ onload = () => { } }) - ipcRenderer.on('micOpen', (event, msg) => { - if (msg === 'mic-open'){ - clearTimeout(muteTimeout) // Cancel mic-off incase of accidental double-tap - console.log("talking") - webview.sendInputEvent({keyCode: 'Backspace', type: 'keyDown'}); - webview.sendInputEvent({keyCode: 'Backspace', type: 'char'}); - document.getElementById("title-bar-status").style.backgroundColor = "green" - document.getElementById("title-bar-controls").style.backgroundColor = "green" - document.getElementById("title-bar").style.backgroundColor = "green" - } - }) + // Accept commands from preload.js + window.addEventListener( + "message", + event => { + if (event.origin === "file://" && event.source === window) { - ipcRenderer.on('micClose', (event, msg) => { - if (msg === 'mic-closed'){ - muteTimeout = setTimeout(() => muteMic(webview), 1000); // 1 second threshold incase of accidental double-click or release so the user doesn't cut-out - } - }) + if (event.data.type === "devMode" && event.data.text === "true") { + webview.openDevTools() + } - ipcRenderer.on('devMode', (event, msg) => { - console.log(`Dev Mode: ${msg}`) - if (msg === true) { - webview.openDevTools() - } - }) -} + if (event.data.type === 'micOpen'){ + clearTimeout(muteTimeout) // Cancel mic-off incase of accidental double-tap + console.log("talking") + webview.sendInputEvent({keyCode: 'Backspace', type: 'keyDown'}); + webview.sendInputEvent({keyCode: 'Backspace', type: 'char'}); + document.getElementById("title-bar-status").style.backgroundColor = "green" + document.getElementById("title-bar-controls").style.backgroundColor = "green" + document.getElementById("title-bar").style.backgroundColor = "green" + } -document.getElementById('minimize-button').addEventListener('click', () => { - remote.getCurrentWindow().minimize() - }) - -document.getElementById('close-button').addEventListener('click', () => { - remote.app.quit() -}) \ No newline at end of file + if (event.data.type === 'micClose'){ + muteTimeout = setTimeout(() => muteMic(webview), 1000); // 1 second threshold incase of accidental double-click or release so the user doesn't cut-out + } + + } + }, + false + ) +} \ No newline at end of file