security-warnings-spec.ts 7.3 KB

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