security-warnings.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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' +
  85. 'This warning will not show up 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. let warning = 'This renderer process loads resources using insecure protocols. ' +
  107. 'This exposes users of this app to unnecessary security risks. ' +
  108. 'Consider loading the following resources over HTTPS or FTPS. \n' +
  109. resources + '\n' +
  110. moreInformation
  111. console.warn('%cElectron Security Warning (Insecure Resources)',
  112. 'font-weight: bold;', warning)
  113. },
  114. /**
  115. * #2 on the checklist: Disable the Node.js integration in all renderers that
  116. * display remote content
  117. *
  118. * Logs a warning message about Node integration.
  119. */
  120. warnAboutNodeWithRemoteContent: () => {
  121. if (getIsRemoteProtocol()) {
  122. let warning = 'This renderer process has Node.js integration enabled ' +
  123. 'and attempted to load remote content. This exposes users of this app to severe ' +
  124. 'security risks.\n' +
  125. moreInformation
  126. console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)',
  127. 'font-weight: bold;', warning)
  128. }
  129. },
  130. // Currently missing since it has ramifications and is still experimental:
  131. // #3 Enable context isolation in all renderers that display remote content
  132. //
  133. // Currently missing since we can't easily programmatically check for those cases:
  134. // #4 Use ses.setPermissionRequestHandler() in all sessions that load remote content
  135. /**
  136. * #5 on the checklist: Do not disable websecurity
  137. *
  138. * Logs a warning message about disabled webSecurity.
  139. */
  140. warnAboutDisabledWebSecurity: () => {
  141. const webPreferences = getWebPreferences()
  142. if (!webPreferences || webPreferences.webSecurity !== false) return
  143. let warning = 'This renderer process has "webSecurity" disabled. ' +
  144. 'This exposes users of this app to severe security risks.\n' +
  145. moreInformation
  146. console.warn('%cElectron Security Warning (Disabled webSecurity)',
  147. 'font-weight: bold;', warning)
  148. },
  149. /**
  150. * #6 on the checklist: Define a Content-Security-Policy and use restrictive
  151. * rules (i.e. script-src 'self')
  152. *
  153. * #7 on the checklist: Disable eval
  154. *
  155. * Logs a warning message about unset or insecure CSP
  156. */
  157. warnAboutInsecureCSP: () => {
  158. if (isUnsafeEvalEnabled()) {
  159. let warning = 'This renderer process has either no Content Security Policy set ' +
  160. 'or a policy with "unsafe-eval" enabled. This exposes users of this ' +
  161. 'app to unnecessary security risks.\n' +
  162. moreInformation
  163. console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
  164. 'font-weight: bold;', warning)
  165. }
  166. },
  167. /**
  168. * #8 on the checklist: Do not set allowRunningInsecureContent to true
  169. *
  170. * Logs a warning message about disabled webSecurity.
  171. */
  172. warnAboutInsecureContentAllowed: () => {
  173. const webPreferences = getWebPreferences()
  174. if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
  175. let warning = 'This renderer process has "allowRunningInsecureContent" ' +
  176. 'enabled. This exposes users of this app to severe security risks.\n' +
  177. moreInformation
  178. console.warn('%cElectron Security Warning (allowRunningInsecureContent)',
  179. 'font-weight: bold;', warning)
  180. },
  181. /**
  182. * #9 on the checklist: Do not enable experimental features
  183. *
  184. * Logs a warning message about experimental features.
  185. */
  186. warnAboutExperimentalFeatures: () => {
  187. const webPreferences = getWebPreferences()
  188. if (!webPreferences || (!webPreferences.experimentalFeatures &&
  189. !webPreferences.experimentalCanvasFeatures)) {
  190. return
  191. }
  192. let warning = 'This renderer process has "experimentalFeatures" ' +
  193. 'enabled. This exposes users of this app to some security risk. ' +
  194. 'If you do not need this feature, you should disable it.\n' +
  195. moreInformation
  196. console.warn('%cElectron Security Warning (experimentalFeatures)',
  197. 'font-weight: bold;', warning)
  198. },
  199. /**
  200. * #10 on the checklist: Do not use blinkFeatures
  201. *
  202. * Logs a warning message about blinkFeatures
  203. */
  204. warnAboutBlinkFeatures: () => {
  205. const webPreferences = getWebPreferences()
  206. if (!webPreferences || !webPreferences.blinkFeatures ||
  207. (webPreferences.blinkFeatures.length && webPreferences.blinkFeatures.length === 0)) {
  208. return
  209. }
  210. let warning = 'This renderer process has additional "blinkFeatures" ' +
  211. 'enabled. This exposes users of this app to some security risk. ' +
  212. 'If you do not need this feature, you should disable it.\n' +
  213. moreInformation
  214. console.warn('%cElectron Security Warning (blinkFeatures)',
  215. 'font-weight: bold;', warning)
  216. },
  217. /**
  218. * #11 on the checklist: Do Not Use allowpopups
  219. *
  220. * Logs a warning message about allowed popups
  221. */
  222. warnAboutAllowedPopups: () => {
  223. if (document && document.querySelectorAll) {
  224. const domElements = document.querySelectorAll('[allowpopups]')
  225. if (!domElements || domElements.length === 0) {
  226. return
  227. }
  228. let warning = 'A <webview> has "allowpopups" set to true. ' +
  229. 'This exposes users of this app to some security risk, since popups are just ' +
  230. 'BrowserWindows. If you do not need this feature, you should disable it.\n' +
  231. moreInformation
  232. console.warn('%cElectron Security Warning (allowpopups)',
  233. 'font-weight: bold;', warning)
  234. }
  235. }
  236. // Currently missing since we can't easily programmatically check for it:
  237. // #12WebViews: Verify the options and params of all `<webview>` tags
  238. }