diff --git a/docs/tutorial/security.md b/docs/tutorial/security.md index f8d4123d4b3a..1ab7608217df 100644 --- a/docs/tutorial/security.md +++ b/docs/tutorial/security.md @@ -511,7 +511,7 @@ no circumstances should you enable features speculatively. // Bad const mainWindow = new BrowserWindow({ webPreferences: { - enableBlinkFeatures: ['ExecCommandInJavaScript'] + enableBlinkFeatures: 'ExecCommandInJavaScript' } }) ``` diff --git a/spec-main/security-warnings-spec.ts b/spec-main/security-warnings-spec.ts new file mode 100644 index 000000000000..a44dc52fe955 --- /dev/null +++ b/spec-main/security-warnings-spec.ts @@ -0,0 +1,235 @@ +import { expect } from 'chai' +import * as http from 'http' +import * as fs from 'fs' +import * as path from 'path' +import * as url from 'url' + +import { BrowserWindow, WebPreferences } from 'electron' + +import { closeWindow } from './window-helpers' +import { AddressInfo } from 'net'; +import { emittedOnce } from './events-helpers'; + +describe('security warnings', () => { + let server: http.Server + let w: BrowserWindow + let useCsp = true + let serverUrl: string + + before((done) => { + // Create HTTP Server + server = http.createServer((request, response) => { + const uri = url.parse(request.url!).pathname! + let filename = path.join(__dirname, '..', 'spec', 'fixtures', 'pages', uri) + + fs.stat(filename, (error, stats) => { + if (error) { + response.writeHead(404, { 'Content-Type': 'text/plain' }) + response.end() + return + } + + if (stats.isDirectory()) { + filename += '/index.html' + } + + fs.readFile(filename, 'binary', (err, file) => { + if (err) { + response.writeHead(404, { 'Content-Type': 'text/plain' }) + response.end() + return + } + + const cspHeaders = { 'Content-Security-Policy': `script-src 'self' 'unsafe-inline'` } + response.writeHead(200, useCsp ? cspHeaders : undefined) + response.write(file, 'binary') + response.end() + }) + }) + }).listen(0, '127.0.0.1', () => { + serverUrl = `http://127.0.0.1:${(server.address() as AddressInfo).port}` + done() + }) + }) + + after(() => { + // Close server + server.close() + server = null as unknown as any + }) + + afterEach(async () => { + useCsp = true + await closeWindow(w) + w = null as unknown as any + }) + + it('should warn about Node.js integration with remote content', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true + } + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('Node.js Integration with Remote Content') + }) + + it('should not warn about Node.js integration with remote content from localhost', (done) => { + w = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true + } + }) + w.webContents.once('console-message', (e, level, message) => { + expect(message).to.not.include('Node.js Integration with Remote Content') + + if (message === 'loaded') { + done() + } + }) + + w.loadURL(`${serverUrl}/base-page-security-onload-message.html`) + }) + + const generateSpecs = (description: string, webPreferences: WebPreferences) => { + describe(description, () => { + it('should warn about disabled webSecurity', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + webSecurity: false, + ...webPreferences + } + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('Disabled webSecurity') + }) + + it('should warn about insecure Content-Security-Policy', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + enableRemoteModule: false, + ...webPreferences + } + }) + + useCsp = false + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('Insecure Content-Security-Policy') + }) + + it('should warn about allowRunningInsecureContent', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + allowRunningInsecureContent: true, + ...webPreferences + } + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('allowRunningInsecureContent') + }) + + it('should warn about experimentalFeatures', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + experimentalFeatures: true, + ...webPreferences + } + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('experimentalFeatures') + }) + + it('should warn about enableBlinkFeatures', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { + enableBlinkFeatures: 'my-cool-feature', + ...webPreferences + } + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('enableBlinkFeatures') + }) + + it('should warn about allowpopups', async () => { + w = new BrowserWindow({ + show: false, + webPreferences + }) + + w.loadURL(`${serverUrl}/webview-allowpopups.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('allowpopups') + }) + + it('should warn about insecure resources', async () => { + w = new BrowserWindow({ + show: false, + webPreferences: { ...webPreferences } + }) + + w.loadURL(`${serverUrl}/insecure-resources.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('Insecure Resources') + }) + + it('should not warn about loading insecure-resources.html from localhost', async () => { + w = new BrowserWindow({ + show: false, + webPreferences + }) + + w.loadURL(`${serverUrl}/insecure-resources.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.not.include('insecure-resources.html') + }) + + it('should warn about enabled remote module with remote content', async () => { + w = new BrowserWindow({ + show: false, + webPreferences + }) + + w.loadURL(`${serverUrl}/base-page-security.html`) + const [,, message] = await emittedOnce(w.webContents, 'console-message') + expect(message).to.include('enableRemoteModule') + }) + + it('should not warn about enabled remote module with remote content from localhost', (done) => { + w = new BrowserWindow({ + show: false, + webPreferences + }) + w.webContents.once('console-message', (e, level, message) => { + expect(message).to.not.include('enableRemoteModule') + + if (message === 'loaded') { + done() + } + }) + + w.loadURL(`${serverUrl}/base-page-security-onload-message.html`) + }) + }) + } + + generateSpecs('without sandbox', {}) + generateSpecs('with sandbox', { sandbox: true }) +}) diff --git a/spec/fixtures/pages/insecure-resources.html b/spec/fixtures/pages/insecure-resources.html index 97f91b769eae..105911325383 100644 --- a/spec/fixtures/pages/insecure-resources.html +++ b/spec/fixtures/pages/insecure-resources.html @@ -1,6 +1,6 @@ - + diff --git a/spec/security-warnings-spec.js b/spec/security-warnings-spec.js deleted file mode 100644 index 45141b4dd966..000000000000 --- a/spec/security-warnings-spec.js +++ /dev/null @@ -1,257 +0,0 @@ -const chai = require('chai') -const dirtyChai = require('dirty-chai') - -const http = require('http') -const fs = require('fs') -const path = require('path') -const url = require('url') - -const { remote } = require('electron') -const { BrowserWindow } = remote - -const { closeWindow } = require('./window-helpers') - -const { expect } = chai -chai.use(dirtyChai) - -describe('security warnings', () => { - let server - let w = null - let useCsp = true - - before((done) => { - // Create HTTP Server - server = http.createServer((request, response) => { - const uri = url.parse(request.url).pathname - let filename = path.join(__dirname, './fixtures/pages', uri) - - fs.stat(filename, (error, stats) => { - if (error) { - response.writeHead(404, { 'Content-Type': 'text/plain' }) - response.end() - return - } - - if (stats.isDirectory()) { - filename += '/index.html' - } - - fs.readFile(filename, 'binary', (err, file) => { - if (err) { - response.writeHead(404, { 'Content-Type': 'text/plain' }) - response.end() - return - } - - const cspHeaders = { 'Content-Security-Policy': `script-src 'self' 'unsafe-inline'` } - response.writeHead(200, useCsp ? cspHeaders : undefined) - response.write(file, 'binary') - response.end() - }) - }) - }).listen(8881, () => done()) - }) - - after(() => { - // Close server - server.close() - server = null - }) - - afterEach(() => { - useCsp = true - return closeWindow(w).then(() => { w = null }) - }) - - it('should warn about Node.js integration with remote content', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - nodeIntegration: true - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('Node.js Integration with Remote Content') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should not warn about Node.js integration with remote content from localhost', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - nodeIntegration: true - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.not.include('Node.js Integration with Remote Content') - - if (message === 'loaded') { - done() - } - }) - - w.loadURL(`http://localhost:8881/base-page-security-onload-message.html`) - }) - - const generateSpecs = (description, webPreferences) => { - describe(description, () => { - it('should warn about disabled webSecurity', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - webSecurity: false, - ...webPreferences - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('Disabled webSecurity') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should warn about insecure Content-Security-Policy', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - enableRemoteModule: false, - ...webPreferences - } - }) - - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('Insecure Content-Security-Policy') - done() - }) - - useCsp = false - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should warn about allowRunningInsecureContent', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - allowRunningInsecureContent: true, - ...webPreferences - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('allowRunningInsecureContent') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should warn about experimentalFeatures', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - experimentalFeatures: true, - ...webPreferences - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('experimentalFeatures') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should warn about enableBlinkFeatures', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences: { - enableBlinkFeatures: ['my-cool-feature'], - ...webPreferences - } - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('enableBlinkFeatures') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should warn about allowpopups', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('allowpopups') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/webview-allowpopups.html`) - }) - - it('should warn about insecure resources', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('Insecure Resources') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/insecure-resources.html`) - w.webContents.openDevTools() - }) - - it('should not warn about loading insecure-resources.html from localhost', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.not.include('insecure-resources.html') - done() - }) - - w.loadURL(`http://localhost:8881/insecure-resources.html`) - w.webContents.openDevTools() - }) - - it('should warn about enabled remote module with remote content', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.include('enableRemoteModule') - done() - }) - - w.loadURL(`http://127.0.0.1:8881/base-page-security.html`) - }) - - it('should not warn about enabled remote module with remote content from localhost', (done) => { - w = new BrowserWindow({ - show: false, - webPreferences - }) - w.webContents.once('console-message', (e, level, message) => { - expect(message).to.not.include('enableRemoteModule') - - if (message === 'loaded') { - done() - } - }) - - w.loadURL(`http://localhost:8881/base-page-security-onload-message.html`) - }) - }) - } - - generateSpecs('without sandbox', {}) - generateSpecs('with sandbox', { sandbox: true }) -})