let shouldLog = null /** * This method checks if a security message should be logged. * It does so by determining whether we're running as Electron, * which indicates that a developer is currently looking at the * app. * * @returns {boolean} - Should we log? */ const shouldLogSecurityWarnings = function () { if (shouldLog !== null) { return shouldLog } const { platform, execPath, env } = process switch (platform) { case 'darwin': shouldLog = execPath.endsWith('MacOS/Electron') || execPath.includes('Electron.app/Contents/Frameworks/') break case 'freebsd': case 'linux': shouldLog = execPath.endsWith('/electron') break case 'win32': shouldLog = execPath.endsWith('\\electron.exe') break default: shouldLog = false } if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) { shouldLog = false } if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) || (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) { shouldLog = true } return shouldLog } /** * Check's if the current window is remote. * * @returns {boolean} - Is this a remote protocol? */ const getIsRemoteProtocol = function () { if (window && window.location && window.location.protocol) { return /^(http|ftp)s?/gi.test(window.location.protocol) } } /** * Tries to determine whether a CSP without `unsafe-eval` is set. * * @returns {boolean} Is a CSP with `unsafe-eval` set? */ const isUnsafeEvalEnabled = function () { try { //eslint-disable-next-line new Function(''); return true } catch (error) { return false } } /** * Attempts to get the current webContents. Returns null * if that fails * * @returns {Object|null} webPreferences */ let webPreferences const getWebPreferences = function () { try { if (webPreferences) { return webPreferences } const { remote } = require('electron') webPreferences = remote.getCurrentWindow().webContents.getWebPreferences() return webPreferences } catch (error) { return null } } const moreInformation = '\nFor more information and help, consult ' + 'https://electronjs.org/docs/tutorial/security.\n' + 'This warning will not show up once the app is packaged.' module.exports = { shouldLogSecurityWarnings, /** * #1 Only load secure content * * Checks the loaded resources on the current page and logs a * message about all resources loaded over HTTP or FTP. */ warnAboutInsecureResources: () => { if (!window || !window.performance || !window.performance.getEntriesByType) { return } const resources = window.performance .getEntriesByType('resource') .filter(({ name }) => /^(http|ftp):?/gi.test(name || '')) .map(({ name }) => `- ${name}`) .join('\n') if (!resources || resources.length === 0) { return } let warning = 'This renderer process loads resources using insecure protocols. ' + 'This exposes users of this app to unnecessary security risks. ' + 'Consider loading the following resources over HTTPS or FTPS. \n' + resources + '\n' + moreInformation console.warn('%cElectron Security Warning (Insecure Resources)', 'font-weight: bold;', warning) }, /** * #2 on the checklist: Disable the Node.js integration in all renderers that * display remote content * * Logs a warning message about Node integration. */ warnAboutNodeWithRemoteContent: () => { if (getIsRemoteProtocol()) { let warning = 'This renderer process has Node.js integration enabled ' + 'and attempted to load remote content. This exposes users of this app to severe ' + 'security risks.\n' + moreInformation console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)', 'font-weight: bold;', warning) } }, // Currently missing since it has ramifications and is still experimental: // #3 Enable context isolation in all renderers that display remote content // // Currently missing since we can't easily programmatically check for those cases: // #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content /** * #5 on the checklist: Do not disable websecurity * * Logs a warning message about disabled webSecurity. */ warnAboutDisabledWebSecurity: () => { const webPreferences = getWebPreferences() if (!webPreferences || webPreferences.webSecurity !== false) return let warning = 'This renderer process has "webSecurity" disabled. ' + 'This exposes users of this app to severe security risks.\n' + moreInformation console.warn('%cElectron Security Warning (Disabled webSecurity)', 'font-weight: bold;', warning) }, /** * #6 on the checklist: Define a Content-Security-Policy and use restrictive * rules (i.e. script-src 'self') * * #7 on the checklist: Disable eval * * Logs a warning message about unset or insecure CSP */ warnAboutInsecureCSP: () => { if (isUnsafeEvalEnabled()) { let warning = 'This renderer process has either no Content Security Policy set ' + 'or a policy with "unsafe-eval" enabled. This exposes users of this ' + 'app to unnecessary security risks.\n' + moreInformation console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)', 'font-weight: bold;', warning) } }, /** * #8 on the checklist: Do not set allowRunningInsecureContent to true * * Logs a warning message about disabled webSecurity. */ warnAboutInsecureContentAllowed: () => { const webPreferences = getWebPreferences() if (!webPreferences || !webPreferences.allowRunningInsecureContent) return let warning = 'This renderer process has "allowRunningInsecureContent" ' + 'enabled. This exposes users of this app to severe security risks.\n' + moreInformation console.warn('%cElectron Security Warning (allowRunningInsecureContent)', 'font-weight: bold;', warning) }, /** * #9 on the checklist: Do not enable experimental features * * Logs a warning message about experimental features. */ warnAboutExperimentalFeatures: () => { const webPreferences = getWebPreferences() if (!webPreferences || (!webPreferences.experimentalFeatures && !webPreferences.experimentalCanvasFeatures)) { return } let warning = 'This renderer process has "experimentalFeatures" ' + 'enabled. This exposes users of this app to some security risk. ' + 'If you do not need this feature, you should disable it.\n' + moreInformation console.warn('%cElectron Security Warning (experimentalFeatures)', 'font-weight: bold;', warning) }, /** * #10 on the checklist: Do not use blinkFeatures * * Logs a warning message about blinkFeatures */ warnAboutBlinkFeatures: () => { const webPreferences = getWebPreferences() if (!webPreferences || !webPreferences.blinkFeatures || (webPreferences.blinkFeatures.length && webPreferences.blinkFeatures.length === 0)) { return } let warning = 'This renderer process has additional "blinkFeatures" ' + 'enabled. This exposes users of this app to some security risk. ' + 'If you do not need this feature, you should disable it.\n' + moreInformation console.warn('%cElectron Security Warning (blinkFeatures)', 'font-weight: bold;', warning) }, /** * #11 on the checklist: Do Not Use allowpopups * * Logs a warning message about allowed popups */ warnAboutAllowedPopups: () => { if (document && document.querySelectorAll) { const domElements = document.querySelectorAll('[allowpopups]') if (!domElements || domElements.length === 0) { return } let warning = 'A has "allowpopups" set to true. ' + 'This exposes users of this app to some security risk, since popups are just ' + 'BrowserWindows. If you do not need this feature, you should disable it.\n' + moreInformation console.warn('%cElectron Security Warning (allowpopups)', 'font-weight: bold;', warning) } } // Currently missing since we can't easily programmatically check for it: // #12WebViews: Verify the options and params of all `` tags }