security-warnings.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. let shouldLog = null
  2. /**
  3. * This method checks if a security message should be logged.
  4. * It does so by determining whether we're running as Electron,
  5. * which indicates that a developer is currently looking at the
  6. * app.
  7. *
  8. * @returns {boolean} - Should we log?
  9. */
  10. const shouldLogSecurityWarnings = function () {
  11. if (shouldLog !== null) {
  12. return shouldLog
  13. }
  14. const { platform, execPath, env } = process
  15. switch (platform) {
  16. case 'darwin':
  17. shouldLog = execPath.endsWith('MacOS/Electron') ||
  18. execPath.includes('Electron.app/Contents/Frameworks/')
  19. break
  20. case 'freebsd':
  21. case 'linux':
  22. shouldLog = execPath.endsWith('/electron')
  23. break
  24. case 'win32':
  25. shouldLog = execPath.endsWith('\\electron.exe')
  26. break
  27. default:
  28. shouldLog = false
  29. }
  30. if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) ||
  31. (window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) {
  32. shouldLog = false
  33. }
  34. if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) ||
  35. (window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) {
  36. shouldLog = true
  37. }
  38. return shouldLog
  39. }
  40. /**
  41. * Check's if the current window is remote.
  42. *
  43. * @returns {boolean} - Is this a remote protocol?
  44. */
  45. const getIsRemoteProtocol = function () {
  46. if (window && window.location && window.location.protocol) {
  47. return /^(http|ftp)s?/gi.test(window.location.protocol)
  48. }
  49. }
  50. /**
  51. * Tries to determine whether a CSP without `unsafe-eval` is set.
  52. *
  53. * @returns {boolean} Is a CSP with `unsafe-eval` set?
  54. */
  55. const isUnsafeEvalEnabled = function () {
  56. try {
  57. //eslint-disable-next-line
  58. new Function('');
  59. return true
  60. } catch (error) {
  61. return false
  62. }
  63. }
  64. /**
  65. * Attempts to get the current webContents. Returns null
  66. * if that fails
  67. *
  68. * @returns {Object|null} webPreferences
  69. */
  70. let webPreferences
  71. const getWebPreferences = function () {
  72. try {
  73. if (webPreferences) {
  74. return webPreferences
  75. }
  76. const { remote } = require('electron')
  77. webPreferences = remote.getCurrentWebContents().getLastWebPreferences()
  78. return webPreferences
  79. } catch (error) {
  80. return null
  81. }
  82. }
  83. const moreInformation = `\nFor more information and help, consult
  84. https://electronjs.org/docs/tutorial/security.\n This warning will not show up
  85. once the app is packaged.`
  86. module.exports = {
  87. shouldLogSecurityWarnings,
  88. /**
  89. * #1 Only load secure content
  90. *
  91. * Checks the loaded resources on the current page and logs a
  92. * message about all resources loaded over HTTP or FTP.
  93. */
  94. warnAboutInsecureResources: () => {
  95. if (!window || !window.performance || !window.performance.getEntriesByType) {
  96. return
  97. }
  98. const resources = window.performance
  99. .getEntriesByType('resource')
  100. .filter(({ name }) => /^(http|ftp):/gi.test(name || ''))
  101. .map(({ name }) => `- ${name}`)
  102. .join('\n')
  103. if (!resources || resources.length === 0) {
  104. return
  105. }
  106. const warning = `This renderer process loads resources using insecure
  107. protocols.This exposes users of this app to unnecessary security risks.
  108. Consider loading the following resources over HTTPS or FTPS. \n ${resources}
  109. \n ${moreInformation}`
  110. console.warn('%cElectron Security Warning (Insecure Resources)',
  111. 'font-weight: bold;', warning)
  112. },
  113. /**
  114. * #2 on the checklist: Disable the Node.js integration in all renderers that
  115. * display remote content
  116. *
  117. * Logs a warning message about Node integration.
  118. */
  119. warnAboutNodeWithRemoteContent: () => {
  120. if (getIsRemoteProtocol()) {
  121. const warning = `This renderer process has Node.js integration enabled
  122. and attempted to load remote content. This exposes users of this app to
  123. severe security risks.\n ${moreInformation}`
  124. console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)',
  125. 'font-weight: bold;', warning)
  126. }
  127. },
  128. // Currently missing since it has ramifications and is still experimental:
  129. // #3 Enable context isolation in all renderers that display remote content
  130. //
  131. // Currently missing since we can't easily programmatically check for those cases:
  132. // #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content
  133. /**
  134. * #5 on the checklist: Do not disable websecurity
  135. *
  136. * Logs a warning message about disabled webSecurity.
  137. */
  138. warnAboutDisabledWebSecurity: () => {
  139. const webPreferences = getWebPreferences()
  140. if (!webPreferences || webPreferences.webSecurity !== false) return
  141. const warning = `This renderer process has "webSecurity" disabled. This
  142. exposes users of this app to severe security risks.\n ${moreInformation}`
  143. console.warn('%cElectron Security Warning (Disabled webSecurity)',
  144. 'font-weight: bold;', warning)
  145. },
  146. /**
  147. * #6 on the checklist: Define a Content-Security-Policy and use restrictive
  148. * rules (i.e. script-src 'self')
  149. *
  150. * #7 on the checklist: Disable eval
  151. *
  152. * Logs a warning message about unset or insecure CSP
  153. */
  154. warnAboutInsecureCSP: () => {
  155. if (isUnsafeEvalEnabled()) {
  156. const warning = `This renderer process has either no Content Security
  157. Policy set or a policy with "unsafe-eval" enabled. This exposes users of
  158. this app to unnecessary security risks.\n ${moreInformation}`
  159. console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
  160. 'font-weight: bold;', warning)
  161. }
  162. },
  163. /**
  164. * #8 on the checklist: Do not set allowRunningInsecureContent to true
  165. *
  166. * Logs a warning message about disabled webSecurity.
  167. */
  168. warnAboutInsecureContentAllowed: () => {
  169. const webPreferences = getWebPreferences()
  170. if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
  171. const warning = `This renderer process has "allowRunningInsecureContent"
  172. enabled. This exposes users of this app to severe security risks.\n
  173. ${moreInformation}`
  174. console.warn('%cElectron Security Warning (allowRunningInsecureContent)',
  175. 'font-weight: bold;', warning)
  176. },
  177. /**
  178. * #9 on the checklist: Do not enable experimental features
  179. *
  180. * Logs a warning message about experimental features.
  181. */
  182. warnAboutExperimentalFeatures: () => {
  183. const webPreferences = getWebPreferences()
  184. if (!webPreferences || (!webPreferences.experimentalFeatures &&
  185. !webPreferences.experimentalCanvasFeatures)) {
  186. return
  187. }
  188. const warning = `This renderer process has "experimentalFeatures" enabled.
  189. This exposes users of this app to some security risk. If you do not need
  190. this feature, you should disable it.\n ${moreInformation}`
  191. console.warn('%cElectron Security Warning (experimentalFeatures)',
  192. 'font-weight: bold;', warning)
  193. },
  194. /**
  195. * #10 on the checklist: Do not use enableBlinkFeatures
  196. *
  197. * Logs a warning message about enableBlinkFeatures
  198. */
  199. warnAboutEnableBlinkFeatures: () => {
  200. const webPreferences = getWebPreferences()
  201. if (webPreferences === null ||
  202. !webPreferences.hasOwnProperty('enableBlinkFeatures') ||
  203. webPreferences.enableBlinkFeatures.length === 0) {
  204. return
  205. }
  206. const warning = `This renderer process has additional "enableBlinkFeatures"
  207. enabled. This exposes users of this app to some security risk. If you do not
  208. need this feature, you should disable it.\n ${moreInformation}`
  209. console.warn('%cElectron Security Warning (enableBlinkFeatures)',
  210. 'font-weight: bold;', warning)
  211. },
  212. /**
  213. * #11 on the checklist: Do Not Use allowpopups
  214. *
  215. * Logs a warning message about allowed popups
  216. */
  217. warnAboutAllowedPopups: () => {
  218. if (document && document.querySelectorAll) {
  219. const domElements = document.querySelectorAll('[allowpopups]')
  220. if (!domElements || domElements.length === 0) {
  221. return
  222. }
  223. const warning = `A <webview> has "allowpopups" set to true. This exposes
  224. users of this app to some security risk, since popups are just
  225. BrowserWindows. If you do not need this feature, you should disable it.\n
  226. ${moreInformation}`
  227. console.warn('%cElectron Security Warning (allowpopups)',
  228. 'font-weight: bold;', warning)
  229. }
  230. }
  231. // Currently missing since we can't easily programmatically check for it:
  232. // #12WebViews: Verify the options and params of all `<webview>` tags
  233. }