security-warnings-spec.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import { expect } from 'chai';
  2. import * as http from 'http';
  3. import * as fs from 'fs';
  4. import * as path from 'path';
  5. import * as url from 'url';
  6. import { BrowserWindow, WebPreferences } from 'electron/main';
  7. import { closeWindow } from './window-helpers';
  8. import { AddressInfo } from 'net';
  9. import { emittedUntil } from './events-helpers';
  10. import { delay } from './spec-helpers';
  11. const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
  12. return message.indexOf('Electron Security Warning') > -1;
  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((done) => {
  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. }).listen(0, '127.0.0.1', () => {
  51. serverUrl = `http://localhost2:${(server.address() as AddressInfo).port}`;
  52. done();
  53. });
  54. });
  55. after(() => {
  56. // Close server
  57. server.close();
  58. server = null as unknown as any;
  59. });
  60. afterEach(async () => {
  61. useCsp = true;
  62. await closeWindow(w);
  63. w = null as unknown as any;
  64. });
  65. it('should warn about Node.js integration with remote content', async () => {
  66. w = new BrowserWindow({
  67. show: false,
  68. webPreferences: {
  69. nodeIntegration: true,
  70. contextIsolation: false
  71. }
  72. });
  73. w.loadURL(`${serverUrl}/base-page-security.html`);
  74. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  75. expect(message).to.include('Node.js Integration with Remote Content');
  76. });
  77. it('should not warn about Node.js integration with remote content from localhost', async () => {
  78. w = new BrowserWindow({
  79. show: false,
  80. webPreferences: {
  81. nodeIntegration: true
  82. }
  83. });
  84. w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
  85. const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
  86. expect(message).to.not.include('Node.js Integration with Remote Content');
  87. });
  88. const generateSpecs = (description: string, webPreferences: WebPreferences) => {
  89. describe(description, () => {
  90. it('should warn about disabled webSecurity', async () => {
  91. w = new BrowserWindow({
  92. show: false,
  93. webPreferences: {
  94. webSecurity: false,
  95. ...webPreferences
  96. }
  97. });
  98. w.loadURL(`${serverUrl}/base-page-security.html`);
  99. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  100. expect(message).to.include('Disabled webSecurity');
  101. });
  102. it('should warn about insecure Content-Security-Policy', async () => {
  103. w = new BrowserWindow({
  104. show: false,
  105. webPreferences
  106. });
  107. useCsp = false;
  108. w.loadURL(`${serverUrl}/base-page-security.html`);
  109. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  110. expect(message).to.include('Insecure Content-Security-Policy');
  111. });
  112. it('should not warn about secure Content-Security-Policy', async () => {
  113. w = new BrowserWindow({
  114. show: false,
  115. webPreferences
  116. });
  117. useCsp = true;
  118. w.loadURL(`${serverUrl}/base-page-security.html`);
  119. let didNotWarn = true;
  120. w.webContents.on('console-message', () => {
  121. didNotWarn = false;
  122. });
  123. await delay(500);
  124. expect(didNotWarn).to.equal(true);
  125. });
  126. it('should warn about allowRunningInsecureContent', async () => {
  127. w = new BrowserWindow({
  128. show: false,
  129. webPreferences: {
  130. allowRunningInsecureContent: true,
  131. ...webPreferences
  132. }
  133. });
  134. w.loadURL(`${serverUrl}/base-page-security.html`);
  135. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  136. expect(message).to.include('allowRunningInsecureContent');
  137. });
  138. it('should warn about experimentalFeatures', async () => {
  139. w = new BrowserWindow({
  140. show: false,
  141. webPreferences: {
  142. experimentalFeatures: true,
  143. ...webPreferences
  144. }
  145. });
  146. w.loadURL(`${serverUrl}/base-page-security.html`);
  147. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  148. expect(message).to.include('experimentalFeatures');
  149. });
  150. it('should warn about enableBlinkFeatures', async () => {
  151. w = new BrowserWindow({
  152. show: false,
  153. webPreferences: {
  154. enableBlinkFeatures: 'my-cool-feature',
  155. ...webPreferences
  156. }
  157. });
  158. w.loadURL(`${serverUrl}/base-page-security.html`);
  159. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  160. expect(message).to.include('enableBlinkFeatures');
  161. });
  162. it('should warn about allowpopups', async () => {
  163. w = new BrowserWindow({
  164. show: false,
  165. webPreferences
  166. });
  167. w.loadURL(`${serverUrl}/webview-allowpopups.html`);
  168. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  169. expect(message).to.include('allowpopups');
  170. });
  171. it('should warn about insecure resources', async () => {
  172. w = new BrowserWindow({
  173. show: false,
  174. webPreferences
  175. });
  176. w.loadURL(`${serverUrl}/insecure-resources.html`);
  177. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  178. expect(message).to.include('Insecure Resources');
  179. });
  180. it('should not warn about loading insecure-resources.html from localhost', async () => {
  181. w = new BrowserWindow({
  182. show: false,
  183. webPreferences
  184. });
  185. w.loadURL(`${serverUrl}/insecure-resources.html`);
  186. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  187. expect(message).to.not.include('insecure-resources.html');
  188. });
  189. });
  190. };
  191. generateSpecs('without sandbox', { contextIsolation: false });
  192. generateSpecs('with sandbox', { sandbox: true, contextIsolation: false });
  193. });