security-warnings-spec.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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 { ifit } from './spec-helpers';
  11. const features = process._linkedBinding('electron_common_features');
  12. const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
  13. return message.indexOf('Electron Security Warning') > -1;
  14. };
  15. const isLoaded = (event: Event, level: number, message: string) => {
  16. return (message === 'loaded');
  17. };
  18. describe('security warnings', () => {
  19. let server: http.Server;
  20. let w: BrowserWindow;
  21. let useCsp = true;
  22. let useTrustedTypes = false;
  23. let serverUrl: string;
  24. before((done) => {
  25. // Create HTTP Server
  26. server = http.createServer((request, response) => {
  27. const uri = url.parse(request.url!).pathname!;
  28. let filename = path.join(__dirname, '..', 'spec', 'fixtures', 'pages', uri);
  29. fs.stat(filename, (error, stats) => {
  30. if (error) {
  31. response.writeHead(404, { 'Content-Type': 'text/plain' });
  32. response.end();
  33. return;
  34. }
  35. if (stats.isDirectory()) {
  36. filename += '/index.html';
  37. }
  38. fs.readFile(filename, 'binary', (err, file) => {
  39. if (err) {
  40. response.writeHead(404, { 'Content-Type': 'text/plain' });
  41. response.end();
  42. return;
  43. }
  44. const cspHeaders = [
  45. ...(useCsp ? ['script-src \'self\' \'unsafe-inline\''] : []),
  46. ...(useTrustedTypes ? ['require-trusted-types-for \'script\'; trusted-types *'] : [])
  47. ];
  48. response.writeHead(200, { 'Content-Security-Policy': cspHeaders });
  49. response.write(file, 'binary');
  50. response.end();
  51. });
  52. });
  53. }).listen(0, '127.0.0.1', () => {
  54. serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
  55. done();
  56. });
  57. });
  58. after(() => {
  59. // Close server
  60. server.close();
  61. server = null as unknown as any;
  62. });
  63. afterEach(async () => {
  64. useCsp = true;
  65. useTrustedTypes = false;
  66. await closeWindow(w);
  67. w = null as unknown as any;
  68. });
  69. it('should warn about Node.js integration with remote content', async () => {
  70. w = new BrowserWindow({
  71. show: false,
  72. webPreferences: {
  73. nodeIntegration: true,
  74. contextIsolation: false
  75. }
  76. });
  77. w.loadURL(`${serverUrl}/base-page-security.html`);
  78. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  79. expect(message).to.include('Node.js Integration with Remote Content');
  80. });
  81. it('should not warn about Node.js integration with remote content from localhost', async () => {
  82. w = new BrowserWindow({
  83. show: false,
  84. webPreferences: {
  85. nodeIntegration: true
  86. }
  87. });
  88. w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
  89. const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
  90. expect(message).to.not.include('Node.js Integration with Remote Content');
  91. });
  92. const generateSpecs = (description: string, webPreferences: WebPreferences) => {
  93. describe(description, () => {
  94. it('should warn about disabled webSecurity', async () => {
  95. w = new BrowserWindow({
  96. show: false,
  97. webPreferences: {
  98. webSecurity: false,
  99. ...webPreferences
  100. }
  101. });
  102. w.loadURL(`${serverUrl}/base-page-security.html`);
  103. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  104. expect(message).to.include('Disabled webSecurity');
  105. });
  106. it('should warn about insecure Content-Security-Policy', async () => {
  107. w = new BrowserWindow({
  108. show: false,
  109. webPreferences: {
  110. enableRemoteModule: false,
  111. ...webPreferences
  112. }
  113. });
  114. useCsp = false;
  115. w.loadURL(`${serverUrl}/base-page-security.html`);
  116. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  117. expect(message).to.include('Insecure Content-Security-Policy');
  118. });
  119. it('should warn about insecure Content-Security-Policy (Trusted Types)', async () => {
  120. w = new BrowserWindow({
  121. show: false,
  122. webPreferences: {
  123. enableRemoteModule: false,
  124. ...webPreferences
  125. }
  126. });
  127. useCsp = false;
  128. useTrustedTypes = true;
  129. w.loadURL(`${serverUrl}/base-page-security.html`);
  130. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  131. expect(message).to.include('Insecure Content-Security-Policy');
  132. });
  133. it('should warn about allowRunningInsecureContent', async () => {
  134. w = new BrowserWindow({
  135. show: false,
  136. webPreferences: {
  137. allowRunningInsecureContent: true,
  138. ...webPreferences
  139. }
  140. });
  141. w.loadURL(`${serverUrl}/base-page-security.html`);
  142. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  143. expect(message).to.include('allowRunningInsecureContent');
  144. });
  145. it('should warn about experimentalFeatures', async () => {
  146. w = new BrowserWindow({
  147. show: false,
  148. webPreferences: {
  149. experimentalFeatures: true,
  150. ...webPreferences
  151. }
  152. });
  153. w.loadURL(`${serverUrl}/base-page-security.html`);
  154. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  155. expect(message).to.include('experimentalFeatures');
  156. });
  157. it('should warn about enableBlinkFeatures', async () => {
  158. w = new BrowserWindow({
  159. show: false,
  160. webPreferences: {
  161. enableBlinkFeatures: 'my-cool-feature',
  162. ...webPreferences
  163. }
  164. });
  165. w.loadURL(`${serverUrl}/base-page-security.html`);
  166. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  167. expect(message).to.include('enableBlinkFeatures');
  168. });
  169. it('should warn about allowpopups', async () => {
  170. w = new BrowserWindow({
  171. show: false,
  172. webPreferences
  173. });
  174. w.loadURL(`${serverUrl}/webview-allowpopups.html`);
  175. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  176. expect(message).to.include('allowpopups');
  177. });
  178. it('should warn about insecure resources', async () => {
  179. w = new BrowserWindow({
  180. show: false,
  181. webPreferences: { ...webPreferences }
  182. });
  183. w.loadURL(`${serverUrl}/insecure-resources.html`);
  184. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  185. expect(message).to.include('Insecure Resources');
  186. });
  187. it('should not warn about loading insecure-resources.html from localhost', async () => {
  188. w = new BrowserWindow({
  189. show: false,
  190. webPreferences
  191. });
  192. w.loadURL(`${serverUrl}/insecure-resources.html`);
  193. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  194. expect(message).to.not.include('insecure-resources.html');
  195. });
  196. ifit(features.isRemoteModuleEnabled())('should warn about enabled remote module with remote content', async () => {
  197. w = new BrowserWindow({
  198. show: false,
  199. webPreferences: { ...webPreferences, enableRemoteModule: true }
  200. });
  201. w.loadURL(`${serverUrl}/base-page-security.html`);
  202. const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
  203. expect(message).to.include('enableRemoteModule');
  204. });
  205. ifit(features.isRemoteModuleEnabled())('should not warn about enabled remote module with remote content from localhost', async () => {
  206. w = new BrowserWindow({
  207. show: false,
  208. webPreferences: { ...webPreferences, enableRemoteModule: true }
  209. });
  210. w.loadURL(`${serverUrl}/base-page-security-onload-message.html`);
  211. const [,, message] = await emittedUntil(w.webContents, 'console-message', isLoaded);
  212. expect(message).to.not.include('enableRemoteModule');
  213. });
  214. });
  215. };
  216. generateSpecs('without sandbox', { contextIsolation: false });
  217. generateSpecs('with sandbox', { sandbox: true, contextIsolation: false });
  218. });