123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- import { expect } from 'chai';
- import * as path from 'node:path';
- import * as http from 'node:http';
- import { emittedNTimes } from './lib/events-helpers';
- import { closeWindow } from './lib/window-helpers';
- import { app, BrowserWindow, ipcMain } from 'electron/main';
- import { ifdescribe, listen } from './lib/spec-helpers';
- import { once } from 'node:events';
- describe('renderer nodeIntegrationInSubFrames', () => {
- const generateTests = (description: string, webPreferences: any) => {
- describe(description, () => {
- const fixtureSuffix = webPreferences.webviewTag ? '-webview' : '';
- let w: BrowserWindow;
- beforeEach(async () => {
- await closeWindow(w);
- w = new BrowserWindow({
- show: false,
- width: 400,
- height: 400,
- webPreferences
- });
- });
- afterEach(async () => {
- await closeWindow(w);
- w = null as unknown as BrowserWindow;
- });
- it('should load preload scripts in top level iframes', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const [event1, event2] = await detailsPromise;
- expect(event1[0].frameId).to.not.equal(event2[0].frameId);
- expect(event1[0].frameId).to.equal(event1[2]);
- expect(event2[0].frameId).to.equal(event2[2]);
- expect(event1[0].senderFrame.routingId).to.equal(event1[2]);
- expect(event2[0].senderFrame.routingId).to.equal(event2[2]);
- });
- it('should load preload scripts in nested iframes', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`));
- const [event1, event2, event3] = await detailsPromise;
- expect(event1[0].frameId).to.not.equal(event2[0].frameId);
- expect(event1[0].frameId).to.not.equal(event3[0].frameId);
- expect(event2[0].frameId).to.not.equal(event3[0].frameId);
- expect(event1[0].frameId).to.equal(event1[2]);
- expect(event2[0].frameId).to.equal(event2[2]);
- expect(event3[0].frameId).to.equal(event3[2]);
- expect(event1[0].senderFrame.routingId).to.equal(event1[2]);
- expect(event2[0].senderFrame.routingId).to.equal(event2[2]);
- expect(event3[0].senderFrame.routingId).to.equal(event3[2]);
- });
- it('should correctly reply to the main frame with using event.reply', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const [event1] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event1[0].reply('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event1[0].frameId);
- });
- it('should correctly reply to the main frame with using event.senderFrame.send', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const [event1] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event1[0].senderFrame.send('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event1[0].frameId);
- });
- it('should correctly reply to the sub-frames with using event.reply', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const [, event2] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event2[0].reply('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event2[0].frameId);
- });
- it('should correctly reply to the sub-frames with using event.senderFrame.send', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const [, event2] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event2[0].senderFrame.send('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event2[0].frameId);
- });
- it('should correctly reply to the nested sub-frames with using event.reply', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`));
- const [, , event3] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event3[0].reply('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event3[0].frameId);
- });
- it('should correctly reply to the nested sub-frames with using event.senderFrame.send', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`));
- const [, , event3] = await detailsPromise;
- const pongPromise = once(ipcMain, 'preload-pong');
- event3[0].senderFrame.send('preload-ping');
- const [, frameId] = await pongPromise;
- expect(frameId).to.equal(event3[0].frameId);
- });
- it('should not expose globals in main world', async () => {
- const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2);
- w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
- const details = await detailsPromise;
- const senders = details.map(event => event[0].sender);
- const isolatedGlobals = await Promise.all(senders.map(sender => sender.executeJavaScript('window.isolatedGlobal')));
- for (const result of isolatedGlobals) {
- if (webPreferences.contextIsolation === undefined || webPreferences.contextIsolation) {
- expect(result).to.be.undefined();
- } else {
- expect(result).to.equal(true);
- }
- }
- });
- });
- };
- const generateConfigs = (webPreferences: any, ...permutations: {name: string, webPreferences: any}[]) => {
- const configs = [{ webPreferences, names: [] as string[] }];
- for (const permutation of permutations) {
- const length = configs.length;
- for (let j = 0; j < length; j++) {
- const newConfig = Object.assign({}, configs[j]);
- newConfig.webPreferences = Object.assign({},
- newConfig.webPreferences, permutation.webPreferences);
- newConfig.names = newConfig.names.slice(0);
- newConfig.names.push(permutation.name);
- configs.push(newConfig);
- }
- }
- return configs.map((config: any) => {
- if (config.names.length > 0) {
- config.title = `with ${config.names.join(', ')} on`;
- } else {
- config.title = 'without anything special turned on';
- }
- delete config.names;
- return config as {title: string, webPreferences: any};
- });
- };
- const configs = generateConfigs(
- {
- preload: path.resolve(__dirname, 'fixtures/sub-frames/preload.js'),
- nodeIntegrationInSubFrames: true
- },
- {
- name: 'sandbox',
- webPreferences: { sandbox: true }
- },
- {
- name: 'context isolation disabled',
- webPreferences: { contextIsolation: false }
- },
- {
- name: 'webview',
- webPreferences: { webviewTag: true, preload: false }
- }
- );
- for (const config of configs) {
- generateTests(config.title, config.webPreferences);
- }
- describe('internal <iframe> inside of <webview>', () => {
- let w: BrowserWindow;
- beforeEach(async () => {
- await closeWindow(w);
- w = new BrowserWindow({
- show: false,
- width: 400,
- height: 400,
- webPreferences: {
- preload: path.resolve(__dirname, 'fixtures/sub-frames/webview-iframe-preload.js'),
- nodeIntegrationInSubFrames: true,
- webviewTag: true,
- contextIsolation: false
- }
- });
- });
- afterEach(async () => {
- await closeWindow(w);
- w = null as unknown as BrowserWindow;
- });
- it('should not load preload scripts', async () => {
- const promisePass = once(ipcMain, 'webview-loaded');
- const promiseFail = once(ipcMain, 'preload-in-frame').then(() => {
- throw new Error('preload loaded in internal frame');
- });
- await w.loadURL('about:blank');
- return Promise.race([promisePass, promiseFail]);
- });
- });
- });
- // app.getAppMetrics() does not return sandbox information on Linux.
- ifdescribe(process.platform !== 'linux')('cross-site frame sandboxing', () => {
- let server: http.Server;
- let crossSiteUrl: string;
- let serverUrl: string;
- before(async function () {
- server = http.createServer((req, res) => {
- res.end(`<iframe name="frame" src="${crossSiteUrl}" />`);
- });
- serverUrl = (await listen(server)).url;
- crossSiteUrl = serverUrl.replace('127.0.0.1', 'localhost');
- });
- after(() => {
- server.close();
- server = null as unknown as http.Server;
- });
- let w: BrowserWindow;
- afterEach(async () => {
- await closeWindow(w);
- w = null as unknown as BrowserWindow;
- });
- const generateSpecs = (description: string, webPreferences: any) => {
- describe(description, () => {
- it('iframe process is sandboxed if possible', async () => {
- w = new BrowserWindow({
- show: false,
- webPreferences
- });
- await w.loadURL(serverUrl);
- const pidMain = w.webContents.getOSProcessId();
- const pidFrame = w.webContents.mainFrame.frames.find(f => f.name === 'frame')!.osProcessId;
- const metrics = app.getAppMetrics();
- const isProcessSandboxed = function (pid: number) {
- const entry = metrics.find(metric => metric.pid === pid);
- return entry && entry.sandboxed;
- };
- const sandboxMain = !!(webPreferences.sandbox || process.mas);
- const sandboxFrame = sandboxMain || !webPreferences.nodeIntegrationInSubFrames;
- expect(isProcessSandboxed(pidMain)).to.equal(sandboxMain);
- expect(isProcessSandboxed(pidFrame)).to.equal(sandboxFrame);
- });
- });
- };
- generateSpecs('nodeIntegrationInSubFrames = false, sandbox = false', {
- nodeIntegrationInSubFrames: false,
- sandbox: false
- });
- generateSpecs('nodeIntegrationInSubFrames = false, sandbox = true', {
- nodeIntegrationInSubFrames: false,
- sandbox: true
- });
- generateSpecs('nodeIntegrationInSubFrames = true, sandbox = false', {
- nodeIntegrationInSubFrames: true,
- sandbox: false
- });
- generateSpecs('nodeIntegrationInSubFrames = true, sandbox = true', {
- nodeIntegrationInSubFrames: true,
- sandbox: true
- });
- });
|