security-warnings-spec.ts 7.1 KB

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