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