fix: do not trigger CSP violations when checking eval (#30991)
* fix: do not trigger CSP violations when checking eval * Update shell/renderer/api/electron_api_web_frame.cc Co-authored-by: Cheng Zhao <zcbenz@gmail.com> Co-authored-by: Cheng Zhao <zcbenz@gmail.com>
This commit is contained in:
parent
add94f5fe6
commit
63eed52626
4 changed files with 35 additions and 26 deletions
|
@ -77,15 +77,8 @@ const isLocalhost = function () {
|
||||||
*
|
*
|
||||||
* @returns {boolean} Is a CSP with `unsafe-eval` set?
|
* @returns {boolean} Is a CSP with `unsafe-eval` set?
|
||||||
*/
|
*/
|
||||||
const isUnsafeEvalEnabled: () => Promise<boolean> = function () {
|
const isUnsafeEvalEnabled = () => {
|
||||||
return webFrame.executeJavaScript(`(${(() => {
|
return webFrame._isEvalAllowed();
|
||||||
try {
|
|
||||||
eval(window.trustedTypes.emptyScript); // eslint-disable-line no-eval
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}).toString()})()`, false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const moreInformation = `\nFor more information and help, consult
|
const moreInformation = `\nFor more information and help, consult
|
||||||
|
@ -174,8 +167,7 @@ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPref
|
||||||
* Logs a warning message about unset or insecure CSP
|
* Logs a warning message about unset or insecure CSP
|
||||||
*/
|
*/
|
||||||
const warnAboutInsecureCSP = function () {
|
const warnAboutInsecureCSP = function () {
|
||||||
isUnsafeEvalEnabled().then((enabled) => {
|
if (!isUnsafeEvalEnabled()) return;
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
const warning = `This renderer process has either no Content Security
|
const warning = `This renderer process has either no Content Security
|
||||||
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
|
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
|
||||||
|
@ -183,7 +175,6 @@ const warnAboutInsecureCSP = function () {
|
||||||
|
|
||||||
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
|
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
|
||||||
'font-weight: bold;', warning);
|
'font-weight: bold;', warning);
|
||||||
}).catch(() => {});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,6 +49,8 @@
|
||||||
#include "third_party/blink/public/web/web_script_execution_callback.h"
|
#include "third_party/blink/public/web/web_script_execution_callback.h"
|
||||||
#include "third_party/blink/public/web/web_script_source.h"
|
#include "third_party/blink/public/web/web_script_source.h"
|
||||||
#include "third_party/blink/public/web/web_view.h"
|
#include "third_party/blink/public/web/web_view.h"
|
||||||
|
#include "third_party/blink/renderer/core/execution_context/execution_context.h" // nogncheck
|
||||||
|
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" // nogncheck
|
||||||
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" // nogncheck
|
#include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h" // nogncheck
|
||||||
#include "ui/base/ime/ime_text_span.h"
|
#include "ui/base/ime/ime_text_span.h"
|
||||||
#include "url/url_util.h"
|
#include "url/url_util.h"
|
||||||
|
@ -369,6 +371,7 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
.SetMethod("insertText", &WebFrameRenderer::InsertText)
|
.SetMethod("insertText", &WebFrameRenderer::InsertText)
|
||||||
.SetMethod("insertCSS", &WebFrameRenderer::InsertCSS)
|
.SetMethod("insertCSS", &WebFrameRenderer::InsertCSS)
|
||||||
.SetMethod("removeInsertedCSS", &WebFrameRenderer::RemoveInsertedCSS)
|
.SetMethod("removeInsertedCSS", &WebFrameRenderer::RemoveInsertedCSS)
|
||||||
|
.SetMethod("_isEvalAllowed", &WebFrameRenderer::IsEvalAllowed)
|
||||||
.SetMethod("executeJavaScript", &WebFrameRenderer::ExecuteJavaScript)
|
.SetMethod("executeJavaScript", &WebFrameRenderer::ExecuteJavaScript)
|
||||||
.SetMethod("executeJavaScriptInIsolatedWorld",
|
.SetMethod("executeJavaScriptInIsolatedWorld",
|
||||||
&WebFrameRenderer::ExecuteJavaScriptInIsolatedWorld)
|
&WebFrameRenderer::ExecuteJavaScriptInIsolatedWorld)
|
||||||
|
@ -637,6 +640,16 @@ class WebFrameRenderer : public gin::Wrappable<WebFrameRenderer>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsEvalAllowed(v8::Isolate* isolate) {
|
||||||
|
content::RenderFrame* render_frame;
|
||||||
|
if (!MaybeGetRenderFrame(isolate, "isEvalAllowed", &render_frame))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto* context = blink::ExecutionContext::From(
|
||||||
|
render_frame->GetWebFrame()->MainWorldScriptContext());
|
||||||
|
return !context->GetContentSecurityPolicy()->ShouldCheckEval();
|
||||||
|
}
|
||||||
|
|
||||||
v8::Local<v8::Promise> ExecuteJavaScript(gin::Arguments* gin_args,
|
v8::Local<v8::Promise> ExecuteJavaScript(gin::Arguments* gin_args,
|
||||||
const std::u16string& code) {
|
const std::u16string& code) {
|
||||||
gin_helper::Arguments* args = static_cast<gin_helper::Arguments*>(gin_args);
|
gin_helper::Arguments* args = static_cast<gin_helper::Arguments*>(gin_args);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { BrowserWindow, WebPreferences } from 'electron/main';
|
||||||
import { closeWindow } from './window-helpers';
|
import { closeWindow } from './window-helpers';
|
||||||
import { AddressInfo } from 'net';
|
import { AddressInfo } from 'net';
|
||||||
import { emittedUntil } from './events-helpers';
|
import { emittedUntil } from './events-helpers';
|
||||||
|
import { delay } from './spec-helpers';
|
||||||
|
|
||||||
const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
|
const messageContainsSecurityWarning = (event: Event, level: number, message: string) => {
|
||||||
return message.indexOf('Electron Security Warning') > -1;
|
return message.indexOf('Electron Security Warning') > -1;
|
||||||
|
@ -22,7 +23,6 @@ describe('security warnings', () => {
|
||||||
let server: http.Server;
|
let server: http.Server;
|
||||||
let w: BrowserWindow;
|
let w: BrowserWindow;
|
||||||
let useCsp = true;
|
let useCsp = true;
|
||||||
let useTrustedTypes = false;
|
|
||||||
let serverUrl: string;
|
let serverUrl: string;
|
||||||
|
|
||||||
before((done) => {
|
before((done) => {
|
||||||
|
@ -50,8 +50,7 @@ describe('security warnings', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const cspHeaders = [
|
const cspHeaders = [
|
||||||
...(useCsp ? ['script-src \'self\' \'unsafe-inline\''] : []),
|
...(useCsp ? ['script-src \'self\' \'unsafe-inline\''] : [])
|
||||||
...(useTrustedTypes ? ['require-trusted-types-for \'script\'; trusted-types *'] : [])
|
|
||||||
];
|
];
|
||||||
response.writeHead(200, { 'Content-Security-Policy': cspHeaders });
|
response.writeHead(200, { 'Content-Security-Policy': cspHeaders });
|
||||||
response.write(file, 'binary');
|
response.write(file, 'binary');
|
||||||
|
@ -72,7 +71,6 @@ describe('security warnings', () => {
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
useCsp = true;
|
useCsp = true;
|
||||||
useTrustedTypes = false;
|
|
||||||
await closeWindow(w);
|
await closeWindow(w);
|
||||||
w = null as unknown as any;
|
w = null as unknown as any;
|
||||||
});
|
});
|
||||||
|
@ -132,17 +130,20 @@ describe('security warnings', () => {
|
||||||
expect(message).to.include('Insecure Content-Security-Policy');
|
expect(message).to.include('Insecure Content-Security-Policy');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should warn about insecure Content-Security-Policy (Trusted Types)', async () => {
|
it('should not warn about secure Content-Security-Policy', async () => {
|
||||||
w = new BrowserWindow({
|
w = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
webPreferences
|
webPreferences
|
||||||
});
|
});
|
||||||
|
|
||||||
useCsp = false;
|
useCsp = true;
|
||||||
useTrustedTypes = true;
|
|
||||||
w.loadURL(`${serverUrl}/base-page-security.html`);
|
w.loadURL(`${serverUrl}/base-page-security.html`);
|
||||||
const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
|
let didNotWarn = true;
|
||||||
expect(message).to.include('Insecure Content-Security-Policy');
|
w.webContents.on('console-message', () => {
|
||||||
|
didNotWarn = false;
|
||||||
|
});
|
||||||
|
await delay(500);
|
||||||
|
expect(didNotWarn).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should warn about allowRunningInsecureContent', async () => {
|
it('should warn about allowRunningInsecureContent', async () => {
|
||||||
|
|
4
typings/internal-electron.d.ts
vendored
4
typings/internal-electron.d.ts
vendored
|
@ -90,6 +90,10 @@ declare namespace Electron {
|
||||||
_postMessage(channel: string, message: any, transfer?: any[]): void;
|
_postMessage(channel: string, message: any, transfer?: any[]): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WebFrame {
|
||||||
|
_isEvalAllowed(): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface WebPreferences {
|
interface WebPreferences {
|
||||||
openerId?: number | null;
|
openerId?: number | null;
|
||||||
disablePopups?: boolean;
|
disablePopups?: boolean;
|
||||||
|
|
Loading…
Reference in a new issue