Browse Source

fix: CSP with `unsafe-eval` detection with Trusted Types (#27446)

Milan Burda 4 years ago
parent
commit
64b7be751a

+ 1 - 1
lib/renderer/security-warnings.ts

@@ -81,7 +81,7 @@ const isUnsafeEvalEnabled: () => Promise<boolean> = function () {
   // Call _executeJavaScript to bypass the world-safe deprecation warning
   return (webFrame as any)._executeJavaScript(`(${(() => {
     try {
-      new Function(''); // eslint-disable-line no-new,no-new-func
+      eval(window.trustedTypes.emptyScript); // eslint-disable-line no-eval
     } catch {
       return false;
     }

+ 23 - 2
spec-main/security-warnings-spec.ts

@@ -22,6 +22,7 @@ describe('security warnings', () => {
   let server: http.Server;
   let w: BrowserWindow;
   let useCsp = true;
+  let useTrustedTypes = false;
   let serverUrl: string;
 
   before((done) => {
@@ -48,8 +49,11 @@ describe('security warnings', () => {
             return;
           }
 
-          const cspHeaders = { 'Content-Security-Policy': 'script-src \'self\' \'unsafe-inline\'' };
-          response.writeHead(200, useCsp ? cspHeaders : undefined);
+          const cspHeaders = [
+            ...(useCsp ? ['script-src \'self\' \'unsafe-inline\''] : []),
+            ...(useTrustedTypes ? ['require-trusted-types-for \'script\'; trusted-types *'] : [])
+          ];
+          response.writeHead(200, { 'Content-Security-Policy': cspHeaders });
           response.write(file, 'binary');
           response.end();
         });
@@ -68,6 +72,7 @@ describe('security warnings', () => {
 
   afterEach(async () => {
     useCsp = true;
+    useTrustedTypes = false;
     await closeWindow(w);
     w = null as unknown as any;
   });
@@ -129,6 +134,22 @@ describe('security warnings', () => {
         expect(message).to.include('Insecure Content-Security-Policy');
       });
 
+      it('should warn about insecure Content-Security-Policy (Trusted Types)', async () => {
+        w = new BrowserWindow({
+          show: false,
+          webPreferences: {
+            enableRemoteModule: false,
+            ...webPreferences
+          }
+        });
+
+        useCsp = false;
+        useTrustedTypes = true;
+        w.loadURL(`${serverUrl}/base-page-security.html`);
+        const [,, message] = await emittedUntil(w.webContents, 'console-message', messageContainsSecurityWarning);
+        expect(message).to.include('Insecure Content-Security-Policy');
+      });
+
       it('should warn about allowRunningInsecureContent', async () => {
         w = new BrowserWindow({
           show: false,

+ 39 - 0
typings/internal-ambient.d.ts

@@ -272,6 +272,7 @@ declare interface Window {
     }
   };
   ResizeObserver: ResizeObserver;
+  trustedTypes: TrustedTypePolicyFactory;
 }
 
 /**
@@ -323,3 +324,41 @@ interface ResizeObserverEntry {
    */
   readonly contentRect: DOMRectReadOnly;
 }
+
+// https://w3c.github.io/webappsec-trusted-types/dist/spec/#trusted-types
+
+type TrustedHTML = string;
+type TrustedScript = string;
+type TrustedScriptURL = string;
+type TrustedType = TrustedHTML | TrustedScript | TrustedScriptURL;
+type StringContext = 'TrustedHTML' | 'TrustedScript' | 'TrustedScriptURL';
+
+// https://w3c.github.io/webappsec-trusted-types/dist/spec/#typedef-trustedtypepolicy
+
+interface TrustedTypePolicy {
+  createHTML(input: string, ...arguments: any[]): TrustedHTML;
+  createScript(input: string, ...arguments: any[]): TrustedScript;
+  createScriptURL(input: string, ...arguments: any[]): TrustedScriptURL;
+}
+
+// https://w3c.github.io/webappsec-trusted-types/dist/spec/#typedef-trustedtypepolicyoptions
+
+interface TrustedTypePolicyOptions {
+  createHTML?: (input: string, ...arguments: any[]) => TrustedHTML;
+  createScript?: (input: string, ...arguments: any[]) => TrustedScript;
+  createScriptURL?: (input: string, ...arguments: any[]) => TrustedScriptURL;
+}
+
+// https://w3c.github.io/webappsec-trusted-types/dist/spec/#typedef-trustedtypepolicyfactory
+
+interface TrustedTypePolicyFactory {
+  createPolicy(policyName: string, policyOptions: TrustedTypePolicyOptions): TrustedTypePolicy
+  isHTML(value: any): boolean;
+  isScript(value: any): boolean;
+  isScriptURL(value: any): boolean;
+  readonly emptyHTML: TrustedHTML;
+  readonly emptyScript: TrustedScript;
+  getAttributeType(tagName: string, attribute: string, elementNs?: string, attrNs?: string): StringContext | null;
+  getPropertyType(tagName: string, property: string, elementNs?: string): StringContext | null;
+  readonly defaultPolicy: TrustedTypePolicy | null;
+}