123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- 'use strict'
- 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
- }
- /**
- * Checks 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 () {
- const { webFrame } = require('electron')
- return new Promise((resolve) => {
- webFrame.executeJavaScript(`(${(() => {
- try {
- new Function('') // eslint-disable-line no-new,no-new-func
- } catch (err) {
- return false
- }
- return true
- }).toString()})()`, resolve)
- })
- }
- 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.`
- /**
- * #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.
- */
- const warnAboutInsecureResources = function () {
- 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
- }
- const 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.
- */
- const warnAboutNodeWithRemoteContent = function (nodeIntegration) {
- if (!nodeIntegration) return
- if (getIsRemoteProtocol()) {
- const warning = `This renderer process has Node.js integration enabled
- and attempted to load remote content from '${window.location}'. 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.
- */
- const warnAboutDisabledWebSecurity = function (webPreferences) {
- if (!webPreferences || webPreferences.webSecurity !== false) return
- const 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
- */
- const warnAboutInsecureCSP = function () {
- isUnsafeEvalEnabled().then((enabled) => {
- if (!enabled) return
- const 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.
- */
- const warnAboutInsecureContentAllowed = function (webPreferences) {
- if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
- const 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.
- */
- const warnAboutExperimentalFeatures = function (webPreferences) {
- if (!webPreferences || (!webPreferences.experimentalFeatures)) {
- return
- }
- const 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 enableBlinkFeatures
- *
- * Logs a warning message about enableBlinkFeatures
- */
- const warnAboutEnableBlinkFeatures = function (webPreferences) {
- if (webPreferences === null ||
- !webPreferences.hasOwnProperty('enableBlinkFeatures') ||
- webPreferences.enableBlinkFeatures.length === 0) {
- return
- }
- const warning = `This renderer process has additional "enableBlinkFeatures"
- 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 (enableBlinkFeatures)',
- 'font-weight: bold;', warning)
- }
- /**
- * #11 on the checklist: Do Not Use allowpopups
- *
- * Logs a warning message about allowed popups
- */
- const warnAboutAllowedPopups = function () {
- if (document && document.querySelectorAll) {
- const domElements = document.querySelectorAll('[allowpopups]')
- if (!domElements || domElements.length === 0) {
- return
- }
- const warning = `A <webview> 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)
- }
- }
- const warnAboutNodeIntegrationDefault = function (webPreferences) {
- if (webPreferences && webPreferences.nodeIntegration && !webPreferences.nodeIntegrationWasExplicitlyEnabled) {
- const warning = `This window has node integration enabled by default. In ` +
- `Electron 5.0.0, node integration will be disabled by default. To prepare ` +
- `for this change, set {nodeIntegration: true} in the webPreferences for ` +
- `this window, or ensure that this window does not rely on node integration ` +
- `and set {nodeIntegration: false}.`
- console.warn('%cElectron Deprecation Warning (nodeIntegration default change)', 'font-weight: bold;', warning)
- }
- }
- const warnAboutContextIsolationDefault = function (webPreferences) {
- if (webPreferences && webPreferences.preload && !webPreferences.contextIsolation && !webPreferences.contextIsolationWasExplicitlyDisabled) {
- const url = 'https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content'
- const warning = `This window has context isolation disabled by default. In ` +
- `Electron 5.0.0, context isolation will be enabled by default. To prepare ` +
- `for this change, set {contextIsolation: false} in the webPreferences for ` +
- `this window, or ensure that this window does not rely on context ` +
- `isolation being disabled, and set {contextIsolation: true}.\n\n` +
- `For more information, see ${url}`
- console.warn('%cElectron Deprecation Warning (contextIsolation default change)', 'font-weight: bold;', warning)
- }
- }
- const warnAboutDeprecatedWebviewTagDefault = function (webPreferences) {
- if (!webPreferences) {
- return
- }
- if (webPreferences.webviewTagWasExplicitlyEnabled) {
- return
- }
- if (!document || !document.getElementsByTagName) {
- return
- }
- const webviews = document.getElementsByTagName('webview')
- if (webviews && webviews.length > 0) {
- const url = 'https://github.com/electron/electron/blob/master/docs/api/breaking-changes.md#new-browserwindow-webpreferences-'
- const warning = `This window has the <webview> tag enabled by default. In ` +
- `Electron 5.0.0, <webview> tags will be disabled by default. To prepare ` +
- `for this change, set {webviewTag: true} in the webPreferences for ` +
- `this window.\n\n` +
- `For more information, see ${url}`
- console.warn('%cElectron Deprecation Warning (webviewTag default change)',
- 'font-weight: bold;', warning)
- }
- }
- // Currently missing since we can't easily programmatically check for it:
- // #12WebViews: Verify the options and params of all `<webview>` tags
- const logSecurityWarnings = function (webPreferences, nodeIntegration) {
- warnAboutNodeWithRemoteContent(nodeIntegration)
- warnAboutDisabledWebSecurity(webPreferences)
- warnAboutInsecureResources()
- warnAboutInsecureContentAllowed(webPreferences)
- warnAboutExperimentalFeatures(webPreferences)
- warnAboutEnableBlinkFeatures(webPreferences)
- warnAboutInsecureCSP()
- warnAboutAllowedPopups()
- warnAboutNodeIntegrationDefault(webPreferences)
- warnAboutContextIsolationDefault(webPreferences)
- warnAboutDeprecatedWebviewTagDefault(webPreferences)
- }
- const getWebPreferences = function () {
- const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
- const errorUtils = require('@electron/internal/common/error-utils')
- const [ error, result ] = ipcRenderer.sendSync('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
- if (error) {
- console.warn(`getLastWebPreferences() failed: ${errorUtils.deserialize(error)}`)
- return null
- } else {
- return result
- }
- }
- module.exports = function (nodeIntegration) {
- const loadHandler = function () {
- if (shouldLogSecurityWarnings()) {
- const webPreferences = getWebPreferences()
- logSecurityWarnings(webPreferences, nodeIntegration)
- }
- }
- window.addEventListener('load', loadHandler, { once: true })
- }
|