From dc44f3b70ab71643f0a3e956c37a55b335078dd0 Mon Sep 17 00:00:00 2001 From: martgil Date: Sun, 27 Oct 2024 13:39:45 +0800 Subject: [PATCH] add TrustedHTML flag in DomPurify --- .../pgp-block-print-module.ts | 14 ++-- extension/js/common/platform/xss.ts | 4 +- extension/types/purify.d.ts | 4 +- extension/types/trusted-types.d.ts | 67 ++++++++++++++++++- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/extension/chrome/elements/pgp_block_modules/pgp-block-print-module.ts b/extension/chrome/elements/pgp_block_modules/pgp-block-print-module.ts index d795b255ae7..6f55a710ad8 100644 --- a/extension/chrome/elements/pgp_block_modules/pgp-block-print-module.ts +++ b/extension/chrome/elements/pgp_block_modules/pgp-block-print-module.ts @@ -2,6 +2,7 @@ 'use strict'; +import { TrustedHTML, TrustedTypesWindow } from 'types/trusted-types.js'; import { Time } from '../../../js/common/browser/time.js'; import { Catch } from '../../../js/common/platform/catch.js'; import { Xss } from '../../../js/common/platform/xss.js'; @@ -15,7 +16,7 @@ export class PgpBlockViewPrintModule { return; } const w = window.open(); - let html = ` + const html = ` @@ -81,16 +82,19 @@ export class PgpBlockViewPrintModule { `; + let trustedHtml: TrustedHTML | undefined; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const trustedTypes = (w as unknown as TrustedTypesWindow).trustedTypes; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (w?.trustedTypes?.createPolicy) { + if (trustedTypes?.createPolicy) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - const policy = w?.trustedTypes.createPolicy('print-policy', { + const policy = trustedTypes.createPolicy('print-policy', { createHTML: (string: string) => string, }); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - html = policy.createHTML(html); + trustedHtml = policy.createHTML(html); } - w?.document.write(html); + w?.document.write(html || (trustedHtml as unknown as string)); // Give some time for above dom to load in print dialog // https://stackoverflow.com/questions/31725373/google-chrome-not-showing-image-in-print-preview await Time.sleep(250); diff --git a/extension/js/common/platform/xss.ts b/extension/js/common/platform/xss.ts index 89e285eeaf9..db3875f11e8 100644 --- a/extension/js/common/platform/xss.ts +++ b/extension/js/common/platform/xss.ts @@ -82,13 +82,15 @@ export class Xss { public static htmlSanitize = (dirtyHtml: string, tagCheck = false): string => { Xss.throwIfNotSupported(); /* eslint-disable @typescript-eslint/naming-convention */ - return DOMPurify.sanitize(dirtyHtml, { + const sanitized = DOMPurify.sanitize(dirtyHtml, { ADD_ATTR: Xss.ADD_ATTR, + RETURN_TRUSTED_TYPE: true, FORBID_ATTR: Xss.FORBID_ATTR, ...(tagCheck && { ALLOWED_TAGS: Xss.ALLOWED_HTML_TAGS }), ALLOWED_URI_REGEXP: Xss.sanitizeHrefRegexp(), }); /* eslint-enable @typescript-eslint/naming-convention */ + return sanitized as unknown as string; }; /** diff --git a/extension/types/purify.d.ts b/extension/types/purify.d.ts index f7328784c90..4489b9e02b2 100644 --- a/extension/types/purify.d.ts +++ b/extension/types/purify.d.ts @@ -1,4 +1,4 @@ -/// +import { TrustedHTML } from 'trusted-types'; export as namespace DOMPurify; export = DOMPurify; @@ -24,7 +24,7 @@ interface createDOMPurifyI extends DOMPurify.DOMPurifyI { declare namespace DOMPurify { interface DOMPurifyI { sanitize(source: string | Node): string; - // sanitize(source: string | Node, config: Config & { RETURN_TRUSTED_TYPE: true }): TrustedHTML; + sanitize(source: string | Node, config: Config & { RETURN_TRUSTED_TYPE: true }): TrustedHTML; sanitize( source: string | Node, config: Config & { RETURN_DOM_FRAGMENT?: false | undefined; RETURN_DOM?: false | undefined }, diff --git a/extension/types/trusted-types.d.ts b/extension/types/trusted-types.d.ts index bfefff8c4f3..2c7ccd4b806 100644 --- a/extension/types/trusted-types.d.ts +++ b/extension/types/trusted-types.d.ts @@ -1,3 +1,64 @@ -interface Window { - trustedTypes: any; -} \ No newline at end of file +// The main type definitions. Packages that do not want to pollute the global +// scope with Trusted Types (e.g. libraries whose users may not be using Trusted +// Types) can import the types directly from 'trusted-types/lib'. + +export type FnNames = keyof TrustedTypePolicyOptions; +export type Args = Parameters>; + +export class TrustedHTML { + private constructor(); // To prevent instantiting with 'new'. + private brand: true; // To prevent structural typing. +} + +export class TrustedScript { + private constructor(); // To prevent instantiting with 'new'. + private brand: true; // To prevent structural typing. +} + +export class TrustedScriptURL { + private constructor(); // To prevent instantiting with 'new'. + private brand: true; // To prevent structural typing. +} + +export abstract class TrustedTypePolicyFactory { + createPolicy( + policyName: string, + policyOptions?: Options, + ): Pick, "name" | Extract>; + isHTML(value: unknown): value is TrustedHTML; + isScript(value: unknown): value is TrustedScript; + isScriptURL(value: unknown): value is TrustedScriptURL; + readonly emptyHTML: TrustedHTML; + readonly emptyScript: TrustedScript; + getAttributeType(tagName: string, attribute: string, elementNs?: string, attrNs?: string): string | null; + getPropertyType(tagName: string, property: string, elementNs?: string): string | null; + readonly defaultPolicy: TrustedTypePolicy | null; +} + +export abstract class TrustedTypePolicy { + readonly name: string; + createHTML(...args: Args): TrustedHTML; + createScript(...args: Args): TrustedScript; + createScriptURL(...args: Args): TrustedScriptURL; +} + +export interface TrustedTypePolicyOptions { + createHTML?: ((input: string, ...arguments: any[]) => string) | undefined; + createScript?: ((input: string, ...arguments: any[]) => string) | undefined; + createScriptURL?: ((input: string, ...arguments: any[]) => string) | undefined; +} + +// The Window object is augmented with the following properties in browsers that +// support Trusted Types. Users of the 'trusted-types/lib' entrypoint can cast +// window as TrustedTypesWindow to access these properties. +export interface TrustedTypesWindow { + // `trustedTypes` is left intentionally optional to make sure that + // people handle the case when their code is running in a browser not + // supporting trustedTypes. + trustedTypes?: TrustedTypePolicyFactory | undefined; + TrustedHTML: typeof TrustedHTML; + TrustedScript: typeof TrustedScript; + TrustedScriptURL: typeof TrustedScriptURL; + TrustedTypePolicyFactory: typeof TrustedTypePolicyFactory; + TrustedTypePolicy: typeof TrustedTypePolicy; +}