wanami-browser
Version:
A simple, cybersecurity focused web browser.
1,633 lines (1,406 loc) • 55 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = "./main/background.js");
/******/ })
/************************************************************************/
/******/ ({
/***/ "./main/background.js":
/*!****************************!*\
!*** ./main/background.js ***!
\****************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = __webpack_require__(/*! path */ "path");
const os = __webpack_require__(/*! os */ "os");
const envPath = path.resolve(process.cwd(), 'config/.env');
__webpack_require__(/*! dotenv */ "dotenv").config({
path: envPath
});
const electron = __webpack_require__(/*! electron */ "electron");
const {
app
} = electron;
const serve = __webpack_require__(/*! electron-serve */ "electron-serve");
const {
createTaskbar
} = __webpack_require__(/*! ./views/taskbar */ "./main/views/taskbar/index.js");
const {
createContentView
} = __webpack_require__(/*! ./views/content */ "./main/views/content/index.js");
const {
createPasswordView
} = __webpack_require__(/*! ./views/password */ "./main/views/password/index.js");
const {
createMainWindow,
attachViews
} = __webpack_require__(/*! ./windows/main */ "./main/windows/main/index.js");
const {
attachIPCListeners
} = __webpack_require__(/*! ./communication/ipc */ "./main/communication/ipc/index.js");
const {
isProduction,
platform
} = __webpack_require__(/*! ./utility/env_config */ "./main/utility/env_config.js");
const {
passwords
} = __webpack_require__(/*! ./services */ "./main/services/index.js");
const {
log
} = __webpack_require__(/*! ./utility/logger */ "./main/utility/logger.js"); // References to prevent garbage collection
global.electronRefs = {
windows: {
mainWindow: null,
taskbarWindow: null
},
views: {
taskbarView: null,
contentView: null,
passwordView: null
}
};
global.appName = app.name;
if (isProduction) {
serve({
directory: 'app'
});
} else {
app.setPath('userData', `${app.getPath('userData')} (development)`);
}
const initialize = () => {
app.dock.setIcon(electron.nativeImage.createFromPath(app.getAppPath() + "/resources/icons/linux/icon_256x256.png"));
createTaskbar(app);
createContentView(app);
createPasswordView(app);
createMainWindow(app);
attachViews([global.electronRefs.views.contentView, global.electronRefs.views.taskbarView, global.electronRefs.views.passwordView]);
attachIPCListeners();
loadWindowContents();
};
const clearViewContent = () => {
global.electronRefs.views.taskbarView.webContents.loadFile('');
global.electronRefs.views.contentView.webContents.loadFile('');
};
const loadPasswordPage = () => {
const port = process.argv[2];
if (isProduction) {
global.electronRefs.views.passwordView.webContents.loadURL('app://./password.html');
} else {
global.electronRefs.views.passwordView.webContents.loadURL(`http://localhost:${port}/password`);
}
};
const loadCreatePasswordPage = () => {
const port = process.argv[2];
if (isProduction) {
global.electronRefs.views.passwordView.webContents.loadURL('app://./create-password.html');
} else {
global.electronRefs.views.passwordView.webContents.loadURL(`http://localhost:${port}/create-password`);
}
};
const loadTaskbar = () => {
const port = process.argv[2];
if (isProduction) {
global.electronRefs.views.taskbarView.webContents.loadURL('app://./home.html');
} else {
global.electronRefs.views.taskbarView.webContents.loadURL(`http://localhost:${port}/home`);
}
};
const loadHomepage = () => {
global.electronRefs.views.contentView.webContents.loadURL('https://www.google.com/'); // if(!isProduction){
// global.electronRefs.views.contentView.webContents.openDevTools({ mode: 'detach' });
// }
};
const loadWindowContents = () => {
global.electronRefs.views.contentView.setBackgroundColor('#2B2E3B');
passwords.getPassword('app:global').then(data => data ? loadPasswordPage() : loadCreatePasswordPage()).catch(error => {
loadCreatePasswordPage();
});
global.electronRefs.views.passwordView.webContents.once('did-finish-load', () => {
setTimeout(() => {
loadTaskbar();
global.electronRefs.views.taskbarView.webContents.once('did-finish-load', () => {
// if(!isProduction){
// global.electronRefs.views.taskbarView.webContents.openDevTools({ mode: 'detach' });
// }
loadHomepage();
});
}, 2000);
});
};
app.on('ready', () => initialize()); // (macOS) app icon clicked in dock
app.on('activate', () => initialize());
app.on('window-all-closed', () => {
global.electronRefs.views.contentView.webContents.clearHistory();
clearViewContent();
if (platform !== 'darwin') app.quit();
});
app.on('browser-window-blur', e => log.info('app::', 'Focus Lost'));
app.on('browser-window-focus', e => log.info('app::', 'Focus Gained'));
app.on('before-quit', e => log.info('app::', 'About to Quit'));
/***/ }),
/***/ "./main/communication/ipc/index.js":
/*!*****************************************!*\
!*** ./main/communication/ipc/index.js ***!
\*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
ipcMain
} = __webpack_require__(/*! electron */ "electron");
const domainParser = __webpack_require__(/*! psl */ "psl");
const {
loadNewPage
} = __webpack_require__(/*! ../../views/content/controller */ "./main/views/content/controller.js");
const {
uplead,
otp,
passwords
} = __webpack_require__(/*! ../../services */ "./main/services/index.js");
const {
sleep,
validURL,
checkForUpdates
} = __webpack_require__(/*! ../../utility/helper */ "./main/utility/helper.js");
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const attachIPCListeners = () => {
if (attachIPCListeners.singleton) return;
ipcMain.handle('taskbar::navigate', (e, {
protocol,
url
}) => {
let toLoadURL = validURL(protocol, url) || validURL('https://', `duckduckgo.com/?q=${encodeURI(url)}`);
try {
log.info('taskbar::navigate', toLoadURL);
loadNewPage(toLoadURL);
} catch (err) {
console.error('app::navigate', protocol, url);
}
return {
toLoadURL
};
});
ipcMain.handle('taskbar::open', () => {
const {
width,
height
} = global.electronRefs.windows.mainWindow.getBounds();
global.electronRefs.views.taskbarView.setBounds({
x: 0,
y: 0,
width,
height
});
});
ipcMain.handle('taskbar::close', () => {
const {
width
} = global.electronRefs.windows.mainWindow.getBounds();
global.electronRefs.views.taskbarView.setBounds({
x: 0,
y: 0,
width,
height: 56
});
});
ipcMain.handle('taskbar::reload', () => {
global.electronRefs.views.contentView.webContents.reload();
});
ipcMain.handle('taskbar::back', () => {
if (global.electronRefs.views.contentView.webContents.canGoBack()) {
global.electronRefs.views.contentView.webContents.goBack();
}
});
ipcMain.handle('taskbar::forward', () => {
if (global.electronRefs.views.contentView.webContents.canGoForward()) {
global.electronRefs.views.contentView.webContents.goForward();
}
});
ipcMain.handle('taskbar::company-info', async (e, url) => {
try {
const justDomain = domainParser.get(url);
const {
data
} = await uplead.getCompanyInfo(justDomain);
log.info('app::getCompanyInfo', data);
return data;
} catch (error) {
log.info('app::getCompanyInfo', error);
}
});
ipcMain.handle('taskbar::otp-capture', async (e, navigation) => {
const nativeImage = await global.electronRefs.views.contentView.webContents.capturePage();
const jpegData = nativeImage.toJPEG(100);
const otpObj = otp.handleOTP(jpegData, navigation);
if (otpObj) {
otpObj.navigation = navigation;
const response = {
issuer: otpObj.issuer,
label: otpObj.label,
digits: otpObj.digits,
navigation
};
log.info(otpObj);
return response;
}
return null;
});
ipcMain.handle('taskbar::otp-codes', async e => {
return otp.getCodes();
});
ipcMain.handle('password::enter', async (e, input) => {
await sleep(2000);
if (await passwords.verifyPassword('app:global', input)) {
log.info('app::password', 'Password Success');
setTimeout(() => {
global.electronRefs.windows.mainWindow.removeBrowserView(global.electronRefs.views.passwordView);
global.electronRefs.views.passwordView.destroy();
global.electronRefs.views.passwordView = null;
}, 500);
otp.initializeStore(input);
checkForUpdates();
return true;
}
log.error('app::password', 'Password Failure');
return false;
});
ipcMain.handle('password::create', async (e, {
password,
confirmedPassword
}) => {
await sleep(2000);
if (password === confirmedPassword) {
return passwords.setPassword('app:global', password).then(data => {
log.info('app::password', 'Create Password Success');
setTimeout(() => {
global.electronRefs.windows.mainWindow.removeBrowserView(global.electronRefs.views.passwordView);
global.electronRefs.views.passwordView.destroy();
global.electronRefs.views.passwordView = null;
}, 500);
otp.initializeStore(password);
return true;
}).catch(error => {
console.error('app::password', error);
return false;
});
} else {
log.error('app::password', 'Create Password Failure');
return false;
}
});
attachIPCListeners.singleton = true;
};
module.exports = {
attachIPCListeners
};
/***/ }),
/***/ "./main/services/apivoid/index.js":
/*!****************************************!*\
!*** ./main/services/apivoid/index.js ***!
\****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const axios = __webpack_require__(/*! axios */ "axios");
const memo = __webpack_require__(/*! memoizee */ "memoizee");
const token = process.env['APIVOID_API_TOKEN'];
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const uplead = axios.create({
baseURL: 'https://endpoint.apivoid.com/urlrep/v1/pay-as-you-go',
timeout: 1500
});
const getSecurityInfo = async url => {
log.info('app::getSecurityInfo', `Fetching ${url}`);
const response = await uplead.get('/', {
params: {
key: token,
url
}
}).catch(e => {
console.error('app::getSecurityInfo', e);
});
return response && response.data;
};
module.exports = {
getSecurityInfo: memo(getSecurityInfo)
};
/***/ }),
/***/ "./main/services/index.js":
/*!********************************!*\
!*** ./main/services/index.js ***!
\********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const uplead = __webpack_require__(/*! ./uplead */ "./main/services/uplead/index.js");
const apivoid = __webpack_require__(/*! ./apivoid */ "./main/services/apivoid/index.js");
const otp = __webpack_require__(/*! ./otp */ "./main/services/otp/index.js");
const passwords = __webpack_require__(/*! ./passwords */ "./main/services/passwords/index.js");
module.exports = {
uplead,
apivoid,
otp,
passwords
};
/***/ }),
/***/ "./main/services/otp/index.js":
/*!************************************!*\
!*** ./main/services/otp/index.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const fs = __webpack_require__(/*! fs */ "fs");
const jsQR = __webpack_require__(/*! jsqr */ "jsqr");
const jpeg = __webpack_require__(/*! jpeg-js */ "jpeg-js");
const OTPAuth = __webpack_require__(/*! otpauth */ "otpauth");
const Store = __webpack_require__(/*! electron-store */ "electron-store");
let store = null;
let otp = [];
const handleOTP = (data, metadata = false) => {
const code = extractQRCode(data);
const otpConfig = code && parseOTPLink(code.data);
const otpItem = createOTP(otpConfig);
if (otpItem) {
store.set(`${otpItem.issuer}-${otpItem.label}`, {
otp: otpItem.toString(),
metadata
});
restoreItems();
}
return otpItem;
};
const extractQRCode = rawJpegData => {
try {
const {
data,
width,
height
} = jpeg.decode(rawJpegData, {
useTArray: true
});
return jsQR(data, width, height);
} catch (err) {
console.error(err);
return null;
}
};
const parseOTPLink = uri => {
try {
return OTPAuth.URI.parse(uri);
} catch (err) {
console.error(err);
return null;
}
};
const createOTP = otpObject => {
if (otpObject instanceof OTPAuth.TOTP) {
return new OTPAuth.TOTP(otpObject);
} else if (otpObject instanceof OTPAuth.HOTP) {
return OTPAuth.HOTP(otpObject);
} else {
return null;
}
};
const restoreItems = () => {
otp = [];
for (let x of store) {
if (x[1]) otp.push({
otp: createOTP(parseOTPLink(x[1].otp)),
metadata: x[1].metadata
});
}
};
const getCodes = () => {
return otp.map(x => {
return {
issuer: x.otp.issuer,
label: x.otp.label,
digits: x.otp.digits,
code: x.otp.generate(),
hostname: x.metadata.hostname
};
});
};
const seedStore = () => {
// Example of a TOTP URI
// otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
const sample = ['otpauth://totp/Google:alice@wanamibrowser.com?secret=JBSWY3DPEHPK3PXP&issuer=Google', 'otpauth://totp/Facebook:alice@wanamibrowser.com?secret=JBSWY3DPEHPK3PXW&issuer=Facebook', 'otpauth://totp/Twitter:alice@wanamibrowser.com?secret=JBSWY3DPEHPK3PXF&issuer=Twitter'];
for (let link of sample) {
const otpConfig = parseOTPLink(link);
const otpItem = createOTP(otpConfig);
if (otpItem) {
store.set(`${otpItem.issuer}-${otpItem.label}`, {
otp: otpItem.toString(),
metadata: {
hostname: 'Sample'
}
});
}
}
restoreItems();
};
const initializeStore = encryptionKey => {
store = new Store({
name: 'otp-mfa-store',
accessPropertiesByDotNotation: false,
encryptionKey
}); // seedStore(); // For demo purposes
restoreItems();
};
module.exports = {
handleOTP,
getCodes,
restoreItems,
initializeStore
};
/***/ }),
/***/ "./main/services/passwords/index.js":
/*!******************************************!*\
!*** ./main/services/passwords/index.js ***!
\******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const keytar = __webpack_require__(/*! keytar */ "keytar");
const argon2 = __webpack_require__(/*! argon2 */ "argon2");
const getPassword = async account => {
return await keytar.getPassword(global.appName, account);
};
const verifyPassword = async (account, password) => {
try {
const hash = await getPassword(account);
return await argon2.verify(hash, password);
} catch (error) {
console.error('passwords::verify', error);
return false;
}
};
const setPassword = async (account, password) => {
try {
const hash = await argon2.hash(password);
await keytar.setPassword(global.appName, account, hash);
return true;
} catch (error) {
console.error('passwords::set', error);
return false;
}
};
module.exports = {
getPassword,
verifyPassword,
setPassword
};
/***/ }),
/***/ "./main/services/uplead/index.js":
/*!***************************************!*\
!*** ./main/services/uplead/index.js ***!
\***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const axios = __webpack_require__(/*! axios */ "axios");
const memo = __webpack_require__(/*! memoizee */ "memoizee");
const token = process.env['UPLEAD_API_TOKEN'];
const uplead = axios.create({
baseURL: 'https://api.uplead.com/v2/',
timeout: 1000,
headers: {
'Authorization': token
}
});
const getCompanyInfo = async domain => {
if (process.env['ENABLE_COMPANY_DATA'] === 'true') {
const data = await uplead.get('company-search', {
params: {
domain
}
}).catch(e => {
console.error('app::getCompanyInfo', e);
});
return data && data.data;
}
return {};
};
module.exports = {
getCompanyInfo: memo(getCompanyInfo)
};
/***/ }),
/***/ "./main/utility/certificates.js":
/*!**************************************!*\
!*** ./main/utility/certificates.js ***!
\**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const https = __webpack_require__(/*! https */ "https");
const Joi = __webpack_require__(/*! joi */ "joi");
const certificateSchema = Joi.object({
subject: Joi.object({
CN: Joi.optional(),
O: Joi.optional(),
C: Joi.optional()
}).optional(),
subjectaltname: Joi.optional(),
valid_to: Joi.optional(),
issuer: Joi.object({
CN: Joi.optional(),
O: Joi.optional(),
C: Joi.optional()
}).optional()
}).options({
stripUnknown: true
});
const fetchCertificate = options => {
let config = {
agent: false,
ciphers: 'ALL',
method: 'GET',
rejectUnauthorized: true,
...options
};
return new Promise(function (resolve, reject) {
const req = https.request(config, function (res) {
const certificate = res.socket.getPeerCertificate();
if (typeof certificate === 'undefined' || certificate === null) {
reject({
message: 'The website did not provide a valid certificate',
error: ''
});
} else {
resolve(certificate);
}
});
req.end();
req.on('error', error => {
reject({
message: 'The website did not provide a valid certificate',
error
});
});
});
};
const parseCertificate = certificate => {
return certificateSchema.validate(certificate);
};
module.exports = {
fetchCertificate,
parseCertificate
};
/***/ }),
/***/ "./main/utility/env_config.js":
/*!************************************!*\
!*** ./main/utility/env_config.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const isProduction = "development" === 'production';
const platform = process.platform;
module.exports = {
isProduction,
platform
};
/***/ }),
/***/ "./main/utility/helper.js":
/*!********************************!*\
!*** ./main/utility/helper.js ***!
\********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const normalizeUrl = __webpack_require__(/*! normalize-url */ "normalize-url");
const versionCheck = __webpack_require__(/*! github-version-checker */ "github-version-checker");
const domainParser = __webpack_require__(/*! psl */ "psl");
const {
URL
} = __webpack_require__(/*! url */ "url");
const pkg = __webpack_require__(/*! ../../package.json */ "./package.json");
const {
navigateNotification
} = __webpack_require__(/*! ../windows/generic/notifications */ "./main/windows/generic/notifications.js");
const {
log
} = __webpack_require__(/*! ./logger */ "./main/utility/logger.js");
const getPropertySafe = (func, defaultValue) => {
try {
return func();
} catch (e) {
return defaultValue;
}
};
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};
const validURL = (protocol, url) => {
try {
log.info('app::url-check', url);
const urlObj = new URL(normalizeUrl(url, {
forceHttps: true
}));
const validDomain = domainParser.get(urlObj.hostname);
const remProto = normalizeUrl(url, {
stripProtocol: true
});
return validDomain && normalizeUrl(`${protocol}${remProto}`);
} catch (error) {
console.error('app::url-check', url, error);
return false;
}
};
const checkForUpdates = () => {
versionCheck({
repo: pkg.name,
owner: pkg.repository.owner,
currentVersion: pkg.version
}, function (error, update) {
if (error) {
log.error('app::', 'Unable to Check for Available', error);
} else if (update) {
log.info('app::', 'Update Available', JSON.stringify(update));
if (!update.isPrerelease) {
navigateNotification({
subtitle: 'Update Available!',
body: `Version ${update.name} is available on GitHub`,
urgency: 'normal'
}, update.url);
}
} else {
log.info('app::', 'No Update Available');
}
});
};
module.exports = {
getPropertySafe,
sleep,
validURL,
checkForUpdates
};
/***/ }),
/***/ "./main/utility/logger.js":
/*!********************************!*\
!*** ./main/utility/logger.js ***!
\********************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const log = __webpack_require__(/*! electron-log */ "electron-log");
module.exports = {
log
};
/***/ }),
/***/ "./main/views/content/controller.js":
/*!******************************************!*\
!*** ./main/views/content/controller.js ***!
\******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const urlParser = __webpack_require__(/*! url */ "url");
const {
fetchCertificate,
parseCertificate
} = __webpack_require__(/*! ../../utility/certificates */ "./main/utility/certificates.js");
const {
apivoid
} = __webpack_require__(/*! ../../services */ "./main/services/index.js");
const {
launchNotification
} = __webpack_require__(/*! ../../windows/generic/notifications */ "./main/windows/generic/notifications.js");
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const completedNavigation = url => {
let parsedURL = urlParser.parse(url);
let certificateError = null;
fetchCertificate({
hostname: parsedURL.hostname
}).then(function (certificate) {
const {
error,
value
} = parseCertificate(certificate);
log.info('app::certificate', error, value);
if (!error) {
global.electronRefs.views.taskbarView.webContents.send("organization::certificate", value);
}
}).catch(error => {
log.info('app::certificate', error);
certificateError = error;
}).finally(() => {
if (parsedURL.protocol === 'http:' || certificateError !== null) {
parsedURL.protocol = 'http:';
launchNotification({
subtitle: 'Security Warning!',
body: 'Review the secuity panel for more information.',
urgency: 'critical'
});
}
global.electronRefs.views.taskbarView.webContents.send('navigate::new', parsedURL);
});
if (process.env['ENABLE_URL_CHECK'] === 'true') {
log.info('app::getSecurityInfo', 'Fetching URL Info');
apivoid.getSecurityInfo(url).then(({
data
}) => {
log.info('app::getSecurityInfo', data);
const response = {
detections: data.report.domain_blacklist.detections,
risk: data.report.risk_score.result,
security_checks: data.report.security_checks
};
const securityScore = Object.values(response.security_checks).reduce((accumulator, currentValue) => {
if (!currentValue) {
return accumulator + 1;
}
});
response.warning = response.risk > 75 && response.detections > 10 && securityScore > 5;
if (response.warning) {
launchNotification({
subtitle: 'Security Warning!',
body: 'Review the secuity panel for more information.',
urgency: 'critical'
});
}
global.electronRefs.views.taskbarView.webContents.send("security::checkup", response);
}).catch(function (error) {
console.error('app::getSecurityInfo', error);
});
} else {
log.info('app::getSecurityInfo', 'Skipping Security Checks');
}
};
const loadNewPage = url => {
global.electronRefs.views.contentView.webContents.loadURL(url);
};
module.exports = {
completedNavigation,
loadNewPage
};
/***/ }),
/***/ "./main/views/content/index.js":
/*!*************************************!*\
!*** ./main/views/content/index.js ***!
\*************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
BrowserView
} = __webpack_require__(/*! electron */ "electron");
const urlParser = __webpack_require__(/*! url */ "url");
const {
completedNavigation,
loadNewPage
} = __webpack_require__(/*! ./controller */ "./main/views/content/controller.js");
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const setViewBoundsContent = (width, height) => {
global.electronRefs.views.contentView.setBounds({
x: 0,
y: 52,
width,
height: height - 72
});
};
const createContentView = app => {
if (global.electronRefs.views.contentView !== null) return null;
global.electronRefs.views.contentView = new BrowserView({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: true,
enableRemoteModule: false,
backgroundThrottling: false
}
});
attachListeners();
};
const attachListeners = () => {
if (attachListeners.singleton) return;
global.electronRefs.views.contentView.webContents.on('console-message', (e, level, message) => {
log.info('Content::console-message', level, message);
});
global.electronRefs.views.contentView.webContents.on('page-title-updated', (e, title) => {
log.info('Content::page-title-updated', title);
});
global.electronRefs.views.contentView.webContents.on('did-navigate', async (e, url) => {
log.info('Content::did-navigate', url);
completedNavigation(url);
});
global.electronRefs.views.contentView.webContents.on('did-navigate-in-page', async (e, url, isMainFrame) => {
log.info('Content::did-navigate-in-page', url, isMainFrame);
if (isMainFrame) {
completedNavigation(url);
}
});
global.electronRefs.views.contentView.webContents.on('did-stop-loading', () => {
log.info('Content::did-stop-loading');
});
global.electronRefs.views.contentView.webContents.on('did-start-loading', () => {
log.info('Content::did-start-loading');
});
global.electronRefs.views.contentView.webContents.on('did-start-navigation', async (e, ...args) => {
log.info('Content::did-start-navigation');
});
global.electronRefs.views.contentView.webContents.on('new-window', async (e, url, frameName, disposition) => {
e.preventDefault();
log.info('Content::new-window', url, frameName, disposition);
loadNewPage(url);
});
global.electronRefs.views.contentView.webContents.on('did-fail-load', (e, errorCode, errorDescription, validatedURL, isMainFrame) => {
log.info('Content::did-fail-load', errorCode, errorDescription, validatedURL, isMainFrame);
});
global.electronRefs.views.contentView.webContents.on('page-favicon-updated', async (e, favicons) => {
log.info('Content::page-favicon-updated', favicons);
});
global.electronRefs.views.contentView.webContents.on('certificate-error', (event, url, error, certificate, callback) => {
// log.info('Content::certificate-error', url, error, certificate);
event.preventDefault();
callback(true);
});
global.electronRefs.views.contentView.webContents.on('render-process-gone', (event, details) => {
log.info('Content::render-process-gone', details);
});
global.electronRefs.views.contentView.webContents.on('unresponsive', event => {
log.info('Content::unresponsive');
});
global.electronRefs.views.contentView.webContents.on('destroyed', event => {
log.info('Content::destroyed');
});
attachListeners.singleton = true;
};
module.exports = {
createContentView,
setViewBoundsContent
};
/***/ }),
/***/ "./main/views/password/index.js":
/*!**************************************!*\
!*** ./main/views/password/index.js ***!
\**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
BrowserView
} = __webpack_require__(/*! electron */ "electron");
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const setViewBoundsPassword = (width, height) => {
global.electronRefs.views.passwordView.setBounds({
x: 0,
y: 0,
width,
height
});
};
const createPasswordView = app => {
if (global.electronRefs.views.passwordView !== null) return null;
global.electronRefs.views.passwordView = new BrowserView({
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
backgroundThrottling: false
}
});
attachListeners();
};
const attachListeners = () => {
if (attachListeners.singleton) return;
global.electronRefs.views.passwordView.webContents.on('console-message', (e, level, message) => {
log.info('Taskbar::log', level, message);
});
attachListeners.singleton = true;
};
module.exports = {
createPasswordView,
setViewBoundsPassword
};
/***/ }),
/***/ "./main/views/taskbar/index.js":
/*!*************************************!*\
!*** ./main/views/taskbar/index.js ***!
\*************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
BrowserView
} = __webpack_require__(/*! electron */ "electron");
const {
isProduction
} = __webpack_require__(/*! ../../utility/env_config */ "./main/utility/env_config.js");
const {
log
} = __webpack_require__(/*! ../../utility/logger */ "./main/utility/logger.js");
const setViewBoundsTaskbar = (width, height) => {
global.electronRefs.views.taskbarView.setBounds({
x: 0,
y: 0,
width,
height: 56
});
};
const createTaskbar = app => {
if (global.electronRefs.views.taskbarView !== null) return null;
global.electronRefs.views.taskbarView = new BrowserView({
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
backgroundThrottling: false
}
});
attachListeners();
};
const attachListeners = () => {
if (attachListeners.singleton) return;
global.electronRefs.views.taskbarView.webContents.on('console-message', (e, level, message) => {
log.info('Taskbar::log', level, message);
});
attachListeners.singleton = true;
};
module.exports = {
createTaskbar,
setViewBoundsTaskbar
};
/***/ }),
/***/ "./main/windows/generic/create_window.js":
/*!***********************************************!*\
!*** ./main/windows/generic/create_window.js ***!
\***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
screen,
BrowserWindow,
Menu,
Tray
} = __webpack_require__(/*! electron */ "electron");
const Store = __webpack_require__(/*! electron-store */ "electron-store");
const {
menubar,
contextMenu
} = __webpack_require__(/*! ./menu */ "./main/windows/generic/menu.js");
let trayMenu = null;
function createWindow(windowName, options) {
const key = 'window-state';
const name = `window-state-${windowName}`;
const store = new Store({
name
});
const defaultSize = {
width: options.width,
height: options.height
};
let state = {};
let win;
const restore = () => store.get(key, defaultSize);
const getCurrentPosition = () => {
const position = win.getPosition();
const size = win.getSize();
return {
x: position[0],
y: position[1],
width: size[0],
height: size[1]
};
};
const windowWithinBounds = (windowState, bounds) => {
return windowState.x >= bounds.x && windowState.y >= bounds.y && windowState.x + windowState.width <= bounds.x + bounds.width && windowState.y + windowState.height <= bounds.y + bounds.height;
};
const resetToDefaults = () => {
const bounds = screen.getPrimaryDisplay().bounds;
return Object.assign({}, defaultSize, {
x: (bounds.width - defaultSize.width) / 2,
y: (bounds.height - defaultSize.height) / 2
});
};
const ensureVisibleOnSomeDisplay = windowState => {
const visible = screen.getAllDisplays().some(display => {
return windowWithinBounds(windowState, display.bounds);
});
if (!visible) {
// Window is partially or fully not visible now.
// Reset it to safe defaults.
return resetToDefaults();
}
return windowState;
};
const saveState = () => {
if (!win.isMinimized() && !win.isMaximized()) {
Object.assign(state, getCurrentPosition());
}
store.set(key, state);
};
state = ensureVisibleOnSomeDisplay(restore());
win = new BrowserWindow({ ...options,
...state,
webPreferences: { ...options.webPreferences
}
});
tray = new Tray(`${__dirname}/../resources/icons/linux/icon_16x16.png`);
tray.setToolTip(windowName);
tray.on('click', e => win.show());
tray.on('right-click', e => {
trayMenu = Menu.buildFromTemplate([{
role: 'quit'
}]);
tray.setContextMenu(trayMenu);
tray.popUpContextMenu(trayMenu);
});
const mainMenu = Menu.buildFromTemplate(menubar);
const context = Menu.buildFromTemplate(contextMenu);
Menu.setApplicationMenu(mainMenu);
win.on('close', saveState);
win.webContents.on('context-menu', e => {
context.popup();
});
return win;
}
;
module.exports = {
createWindow
};
/***/ }),
/***/ "./main/windows/generic/menu.js":
/*!**************************************!*\
!*** ./main/windows/generic/menu.js ***!
\**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
platform
} = __webpack_require__(/*! ../../utility/env_config */ "./main/utility/env_config.js");
const {
shell
} = __webpack_require__(/*! electron */ "electron");
const openAboutWindow = __webpack_require__(/*! electron-about-window */ "electron-about-window").default;
const {
join
} = __webpack_require__(/*! path */ "path");
const menubar = [...(platform === 'darwin' ? [{
label: 'Wanami Browser',
submenu: [{
label: 'About Wanami Browser',
click: () => {
// TODO: Figure out why this does not work.
openAboutWindow({
icon_path: join(__dirname, '../../../resources/icons/linux/icon_256x256.png'),
copyright: 'Copyright © 2020 Stephen Mendez',
package_json_dir: join(__dirname, '../../../'),
open_devtools: "development" !== 'production',
win_options: {
webPreferences: {
nodeIntegration: true
}
}
});
}
}, {
type: 'separator'
}, {
role: 'services'
}, {
type: 'separator'
}, {
role: 'hide'
}, {
role: 'hideothers'
}, {
role: 'unhide'
}, {
type: 'separator'
}, {
role: 'quit'
}]
}] : []), {
role: 'fileMenu'
}, {
role: 'editMenu'
}, {
role: 'viewMenu'
}, {
role: 'windowMenu'
}, {
role: 'help',
submenu: [{
label: 'Learn More',
click: () => {
shell.openExternal('https://github.com/401unauthorized/wanami-browser');
}
}]
}];
const contextMenu = [{
role: 'editMenu'
}];
module.exports = {
menubar,
contextMenu
};
/***/ }),
/***/ "./main/windows/generic/notifications.js":
/*!***********************************************!*\
!*** ./main/windows/generic/notifications.js ***!
\***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
Notification
} = __webpack_require__(/*! electron */ "electron");
const {
loadNewPage
} = __webpack_require__(/*! ../../views/content/controller */ "./main/views/content/controller.js");
const launchNotification = options => {
let notification = new Notification({
title: global.appName,
...options
});
notification.show();
notification.on('click', e => {
if (!global.electronRefs.windows.mainWindow.isVisible()) {
global.electronRefs.windows.mainWindow.show();
}
});
};
const navigateNotification = (options, url) => {
let notification = new Notification({
title: global.appName,
...options
});
notification.show();
notification.on('click', e => {
if (!global.electronRefs.windows.mainWindow.isVisible()) {
global.electronRefs.windows.mainWindow.show();
}
global.electronRefs.views.contentView.webContents.loadURL(url);
});
};
module.exports = {
launchNotification,
navigateNotification
};
/***/ }),
/***/ "./main/windows/main/index.js":
/*!************************************!*\
!*** ./main/windows/main/index.js ***!
\************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* Copyright (c) 2020 Stephen Mendez. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
createWindow
} = __webpack_require__(/*! ../generic/create_window */ "./main/windows/generic/create_window.js");
const {
setViewBoundsTaskbar
} = __webpack_require__(/*! ../../views/taskbar */ "./main/views/taskbar/index.js");
const {
setViewBoundsContent
} = __webpack_require__(/*! ../../views/content */ "./main/views/content/index.js");
const {
setViewBoundsPassword
} = __webpack_require__(/*! ../../views/password */ "./main/views/password/index.js");
const resizeInnerContents = () => {
const {
width,
height
} = global.electronRefs.windows.mainWindow.getBounds();
setViewBoundsTaskbar(width, height);
setViewBoundsContent(width, height);
setViewBoundsPassword(width, height);
};
const attachViews = views => {
for (let v of views) {
global.electronRefs.windows.mainWindow.addBrowserView(v);
}
resizeInnerContents();
};
const createMainWindow = app => {
if (global.electronRefs.windows.mainWindow !== null) return null;
global.electronRefs.windows.mainWindow = createWindow(app.name, {
title: app.name,
icon: `${__dirname}/../../../resources/icons/mac/wanami.icns`,
resizable: true,
backgroundColor: '#2B2E3B',
opacity: 1,
width: 1000,
height: 800,
minWidth: 520,
minHeight: 360,
webPreferences: {
nodeIntegration: true,
backgroundThrottling: false
}
});
global.electronRefs.windows.mainWindow.on('resize', resizeInnerContents);
global.electronRefs.windows.mainWindow.on('closed', () => {
global.electronRefs.windows.mainWindow = null;
});
};
module.exports = {
createMainWindow,
attachViews
};
/***/ }),
/***/ "./package.json":
/*!**********************!*\
!*** ./package.json ***!
\**********************/
/*! exports provided: name, productName, description, version, keywords, author, bugs, repository, license, main, scripts, dependencies, devDependencies, funding, default */
/***/ (function(module) {
module.exports = JSON.parse("{\"name\":\"wanami-browser\",\"productName\":\"Wanami Browser\",\"description\":\"A simple, cybersecurity focused web browser.\",\"version\":\"1.0.0\",\"keywords\":[\"wanami\",\"wahoo\",\"namaste\",\"web\",\"browser\",\"cybersecurity\",\"phishing\",\"electron\",\"react\"],\"author\":\"Stephen Mendez <git@stephenmendez.dev> (https://www.stephenmendez.dev)\",\"bugs\":\"https://github.com/401unauthorized/wanami-browser/issues\",\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/401unauthorized/wanami-browser.git\",\"owner\":\"401Unauthorized\"},\"license\":\"MPL-2.0\",\"main\":\"app/background.js\",\"scripts\":{\"dev\":\"nextron\",\"build\":\"nextron build\",\"postinstall\":\"electron-builder install-app-deps\",\"package-mac\":\"electron-packager ./main --overwrite --platform=darwin --arch=x64 --icon=resources/icons/mac/icon.icns --prune=true --out=release-builds\",\"package-win\":\"electron-packager ./main --overwrite --platform=win32 --arch=ia32 --icon=resources/icons/win/icon.ico --prune=false --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\\\"Wanami Browser\\\"\",\"package-linux\":\"electron-packager ./main --overwrite --platform=linux --arch=x64 --icon=resources/icons/linux/icon.png --prune=false --out=release-builds\"},\"dependencies\":{\"argon2\":\"^0.27.0\",\"axios\":\"^0.21.0\",\"copy-to-clipboard\":\"^3.3.1\",\"dotenv\":\"^8.2.0\",\"electron-about-window\":\"^1.13.4\",\"electron-log\":\"^4.3.0\",\"electron-serve\":\"^1.0.0\",\"electron-store\":\"^6.0.1\",\"framer-motion\":\"^2.9.4\",\"github-version-checker\":\"^2.2.0\",\"joi\":\"^17.3.0\",\"jpeg-js\":\"^0.4.2\",\"jsqr\":\"^1.3.1\",\"keytar\":\"^7.0.0\",\"memoizee\":\"^0.4.14\",\"moment\":\"^2.29.1\",\"normalize-url\":\"^5.3.0\",\"otpauth\":\"^6.1.0\",\"psl\":\"^1.8.0\",\"semantic-ui-css\":\"^2.4.1\",\"semantic-ui-react\":\"^2.0.1\"},\"devDependencies\":{\"@babel/core\":\"^7.11.6\",\"babel-loader\":\"^8.1.0\",\"electron\":\"^10.1.5\",\"electron-builder\":\"^22.9.1\",\"electron-packager\":\"^15.1.0\",\"next\":\"^10.0.2\",\"nextron\":\"^5.15.11\",\"react\":\"^16.14.0\",\"react-dom\":\"^16.14.0\",\"webpack\":\"^4.44.2\"},\"funding\":{\"type\":\"individual\",\"url\":\"https://paypal.me/stephenmendez401\"}}");
/***/ }),
/***/ "argon2":
/*!*************************!*\
!*** external "argon2" ***!
\*************************/
/*! no static exports found */
/***/