Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

# 5576 Add TrustedHTML flag in DomPurify #5855

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -15,7 +16,7 @@ export class PgpBlockViewPrintModule {
return;
}
const w = window.open();
let html = `
const html = `
<!DOCTYPE html>
<html lang="en-us">
<head>
Expand Down Expand Up @@ -81,16 +82,19 @@ export class PgpBlockViewPrintModule {
</body>
</html>
`;
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);
Expand Down
4 changes: 3 additions & 1 deletion extension/js/common/platform/xss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions extension/types/purify.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// <reference types="trusted-types"/>
import { TrustedHTML } from 'trusted-types';

export as namespace DOMPurify;
export = DOMPurify;
Expand All @@ -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 },
Expand Down
67 changes: 64 additions & 3 deletions extension/types/trusted-types.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,64 @@
interface Window {
trustedTypes: any;
}
// 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<Options extends TrustedTypePolicyOptions, K extends FnNames> = Parameters<NonNullable<Options[K]>>;

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<Options extends TrustedTypePolicyOptions>(
policyName: string,
policyOptions?: Options,
): Pick<TrustedTypePolicy<Options>, "name" | Extract<keyof Options, FnNames>>;
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<Options extends TrustedTypePolicyOptions = TrustedTypePolicyOptions> {
readonly name: string;
createHTML(...args: Args<Options, "createHTML">): TrustedHTML;
createScript(...args: Args<Options, "createScript">): TrustedScript;
createScriptURL(...args: Args<Options, "createScriptURL">): 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;
}
Loading