security-warnings-spec.ts 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. import { expect } from 'chai';
  2. import * as http from 'node:http';
  3. import * as fs from 'node:fs/promises';
  4. import * as path from 'node:path';
  5. import * as url from 'node:url';
  6. import { BrowserWindow, WebPreferences } from 'electron/main';
  7. import { closeWindow } from './lib/window-helpers';
  8. import { emittedUntil } from './lib/events-helpers';
  9. import { listen } from './lib/spec-helpers';
  10. import { setTimeout } from 'node:timers/promises';
  11. const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
  12. return message.includes('Electron Security Warning');
  13. };
  14. const isLoaded = (event: Event, level: number, message: string) => {
  15. return (message === 'loaded');
  16. };
  17. describe('security warnings', () => {
  18. let server: http.Server;
  19. let w: BrowserWindow;
  20. let useCsp = true;
  21. let serverUrl: string;
  22. before(async () => {
  23. // Create HTTP Server
  24. server = http.createServer(async (request, response) => {
  25. const uri = url.parse(request.url!).pathname!;
  26. let filename = path.join(__dirname, 'fixtures', 'pages', uri);
  27. try {
  28. const stats = await fs.stat(filename);
  29. if (stats.isDirectory()) {
  30. filename += '/index.html';
  31. }
  32. const file = await fs.readFile(filename, 'binary');
  33. const cspHeaders = [
  34. ...(useCsp ? ['script-src \'self\' \'unsafe-inline\''] : [])
  35. ];
  36. response.writeHead(200, { 'Content-Security-Policy': cspHeaders });
  37. response.write(file, 'binary');
  38. } catch {
  39. response.writeHead(404, { 'Content-Type': 'text/plain' });
  40. }
  41. response.end();
  42. });
  43. serverUrl = `http://localhost2:${(await listen(server)).port}`;
  44. });
  45. after(() => {
  46. // Close server
  47. server.close();
  48. server = null as unknown as any;
  49. });
  50. afterEach(async () => {
  51. useCsp = true;
  52. await closeWindow(w);
  53. w = null as unknown as any;
  54. });
  55. it('should warn about Node.js integration with remote content', async () => {
  56. w = new BrowserWindow({
  57. show: false,
  58. webPreferences: {
  59. nodeIntegration: true,
  60. contextIsolation: false
  61. }
  62. });
  63. w.loadURL(`${serverUrl}/base-page-security.html`);
  64. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  65. expect(message).to.include('Node.js Integration with Remote Content');
  66. });
  67. it('should not warn about Node.js integration with remote content from localhost', async () => {
  68. w = new BrowserWindow({
  69. show: false,
  70. webPreferences: {
  71. nodeIntegration: true
  72. }
  73. });
  74. w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
  75. const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
  76. expect(message).to.not.include('Node.js Integration with Remote Content');
  77. });
  78. const generateSpecs = (description: string, webPreferences: WebPreferences) => {
  79. describe(description, () => {
  80. it('should warn about disabled webSecurity', async () => {
  81. w = new BrowserWindow({
  82. show: false,
  83. webPreferences: {
  84. webSecurity: false,
  85. ...webPreferences
  86. }
  87. });
  88. w.loadURL(`${serverUrl}/base-page-security.html`);
  89. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  90. expect(message).to.include('Disabled webSecurity');
  91. });
  92. it('should warn about insecure Content-Security-Policy', async () => {
  93. w = new BrowserWindow({
  94. show: false,
  95. webPreferences
  96. });
  97. useCsp = false;
  98. w.loadURL(`${serverUrl}/base-page-security.html`);
  99. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  100. expect(message).to.include('Insecure Content-Security-Policy');
  101. });
  102. it('should not warn about secure Content-Security-Policy', async () => {
  103. w = new BrowserWindow({
  104. show: false,
  105. webPreferences
  106. });
  107. useCsp = true;
  108. w.loadURL(`${serverUrl}/base-page-security.html`);
  109. let didNotWarn = true;
  110. w.webContents.on('console-message', () => {
  111. didNotWarn = false;
  112. });
  113. await setTimeout(500);
  114. expect(didNotWarn).to.equal(true);
  115. });
  116. it('should warn about allowRunningInsecureContent', async () => {
  117. w = new BrowserWindow({
  118. show: false,
  119. webPreferences: {
  120. allowRunningInsecureContent: true,
  121. ...webPreferences
  122. }
  123. });
  124. w.loadURL(`${serverUrl}/base-page-security.html`);
  125. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  126. expect(message).to.include('allowRunningInsecureContent');
  127. });
  128. it('should warn about experimentalFeatures', async () => {
  129. w = new BrowserWindow({
  130. show: false,
  131. webPreferences: {
  132. experimentalFeatures: true,
  133. ...webPreferences
  134. }
  135. });
  136. w.loadURL(`${serverUrl}/base-page-security.html`);
  137. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  138. expect(message).to.include('experimentalFeatures');
  139. });
  140. it('should warn about enableBlinkFeatures', async () => {
  141. w = new BrowserWindow({
  142. show: false,
  143. webPreferences: {
  144. enableBlinkFeatures: 'my-cool-feature',
  145. ...webPreferences
  146. }
  147. });
  148. w.loadURL(`${serverUrl}/base-page-security.html`);
  149. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  150. expect(message).to.include('enableBlinkFeatures');
  151. });
  152. it('should warn about allowpopups', async () => {
  153. w = new BrowserWindow({
  154. show: false,
  155. webPreferences
  156. });
  157. w.loadURL(`${serverUrl}/webview-allowpopups.html`);
  158. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  159. expect(message).to.include('allowpopups');
  160. });
  161. it('should warn about insecure resources', async () => {
  162. w = new BrowserWindow({
  163. show: false,
  164. webPreferences
  165. });
  166. w.loadURL(`${serverUrl}/insecure-resources.html`);
  167. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  168. expect(message).to.include('Insecure Resources');
  169. });
  170. it('should not warn about loading insecure-resources.html from localhost', async () => {
  171. w = new BrowserWindow({
  172. show: false,
  173. webPreferences
  174. });
  175. w.loadURL(`${serverUrl}/insecure-resources.html`);
  176. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  177. expect(message).to.not.include('insecure-resources.html');
  178. });
  179. });
  180. };
  181. generateSpecs('without sandbox', { contextIsolation: false });
  182. generateSpecs('with sandbox', { sandbox: true, contextIsolation: false });
  183. });