feat(ivy): provide sanitization methods which can be tree shaken (#22540)
By providing a top level sanitization methods (rather than service) the compiler can generate calls into the methods only when needed. This makes the methods tree shakable. PR Close #22540
This commit is contained in:
parent
538f1d980f
commit
6d1367d297
|
@ -18,9 +18,9 @@ export {CodegenComponentFactoryResolver as ɵCodegenComponentFactoryResolver} fr
|
|||
export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/reflection_capabilities';
|
||||
export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types';
|
||||
export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api';
|
||||
export {sanitizeHtml as ɵsanitizeHtml} from './sanitization/html_sanitizer';
|
||||
export {sanitizeStyle as ɵsanitizeStyle} from './sanitization/style_sanitizer';
|
||||
export {sanitizeUrl as ɵsanitizeUrl} from './sanitization/url_sanitizer';
|
||||
export {_sanitizeHtml as ɵ_sanitizeHtml} from './sanitization/html_sanitizer';
|
||||
export {_sanitizeStyle as ɵ_sanitizeStyle} from './sanitization/style_sanitizer';
|
||||
export {_sanitizeUrl as ɵ_sanitizeUrl} from './sanitization/url_sanitizer';
|
||||
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
|
||||
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
||||
|
|
|
@ -71,8 +71,15 @@ export {
|
|||
ld as ɵld,
|
||||
Pp as ɵPp,
|
||||
} from './render3/index';
|
||||
export {
|
||||
bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml,
|
||||
bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle,
|
||||
bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript,
|
||||
bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl,
|
||||
bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl,
|
||||
sanitizeHtml as ɵsanitizeHtml,
|
||||
sanitizeStyle as ɵsanitizeStyle,
|
||||
sanitizeUrl as ɵsanitizeUrl,
|
||||
sanitizeResourceUrl as ɵsanitizeResourceUrl,
|
||||
} from './sanitization/sanitization';
|
||||
// clang-format on
|
||||
|
||||
export {htmlSanitizer as ɵhtmlSanitizer} from './sanitization/html_sanitizer';
|
||||
export {styleSanitizer as ɵstyleSanitizer} from './sanitization/style_sanitizer';
|
||||
export {urlSanitizer as ɵurlSanitizer, resourceUrlSanitizer as ɵresourceUrlSanitizer} from './sanitization/url_sanitizer';
|
||||
|
|
|
@ -38,6 +38,11 @@ export const NG_HOST_SYMBOL = '__ngHostLNode__';
|
|||
*/
|
||||
const _CLEAN_PROMISE = Promise.resolve(null);
|
||||
|
||||
/**
|
||||
* Function used to sanitize the value before writing it into the renderer.
|
||||
*/
|
||||
export type Sanitizer = (value: any) => string;
|
||||
|
||||
|
||||
/**
|
||||
* This property gets set before entering a template.
|
||||
|
@ -689,20 +694,22 @@ export function elementEnd() {
|
|||
* Updates the value of removes an attribute on an Element.
|
||||
*
|
||||
* @param number index The index of the element in the data array
|
||||
* @param string name The name of the attribute.
|
||||
* @param any value The attribute is removed when value is `null` or `undefined`.
|
||||
* @param name name The name of the attribute.
|
||||
* @param value value The attribute is removed when value is `null` or `undefined`.
|
||||
* Otherwise the attribute value is set to the stringified value.
|
||||
* @param sanitizer An optional function used to sanitize the value.
|
||||
*/
|
||||
export function elementAttribute(index: number, name: string, value: any): void {
|
||||
export function elementAttribute(
|
||||
index: number, name: string, value: any, sanitizer?: Sanitizer): void {
|
||||
if (value !== NO_CHANGE) {
|
||||
const element: LElementNode = data[index];
|
||||
if (value == null) {
|
||||
isProceduralRenderer(renderer) ? renderer.removeAttribute(element.native, name) :
|
||||
element.native.removeAttribute(name);
|
||||
} else {
|
||||
isProceduralRenderer(renderer) ?
|
||||
renderer.setAttribute(element.native, name, stringify(value)) :
|
||||
element.native.setAttribute(name, stringify(value));
|
||||
const strValue = sanitizer == null ? stringify(value) : sanitizer(value);
|
||||
isProceduralRenderer(renderer) ? renderer.setAttribute(element.native, name, strValue) :
|
||||
element.native.setAttribute(name, strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -718,9 +725,11 @@ export function elementAttribute(index: number, name: string, value: any): void
|
|||
* @param propName Name of property. Because it is going to DOM, this is not subject to
|
||||
* renaming as part of minification.
|
||||
* @param value New value to write.
|
||||
* @param sanitizer An optional function used to sanitize the value.
|
||||
*/
|
||||
|
||||
export function elementProperty<T>(index: number, propName: string, value: T | NO_CHANGE): void {
|
||||
export function elementProperty<T>(
|
||||
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: Sanitizer): void {
|
||||
if (value === NO_CHANGE) return;
|
||||
const node = data[index] as LElementNode;
|
||||
const tNode = node.tNode !;
|
||||
|
@ -737,6 +746,7 @@ export function elementProperty<T>(index: number, propName: string, value: T | N
|
|||
setInputsForProperty(dataValue, value);
|
||||
markDirtyIfOnPush(node);
|
||||
} else {
|
||||
value = (sanitizer != null ? sanitizer(value) : stringify(value)) as any;
|
||||
const native = node.native;
|
||||
isProceduralRenderer(renderer) ? renderer.setProperty(native, propName, value) :
|
||||
(native.setProperty ? native.setProperty(propName, value) :
|
||||
|
@ -842,10 +852,17 @@ export function elementClass<T>(index: number, className: string, value: T | NO_
|
|||
* @param styleName Name of property. Because it is going to DOM this is not subject to
|
||||
* renaming as part of minification.
|
||||
* @param value New value to write (null to remove).
|
||||
* @param suffix Suffix to add to style's value (optional).
|
||||
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
|
||||
* @param sanitizer An optional function used to transform the value typically used for
|
||||
* sanitization.
|
||||
*/
|
||||
export function elementStyle<T>(
|
||||
index: number, styleName: string, value: T | NO_CHANGE, suffix?: string): void {
|
||||
index: number, styleName: string, value: T | NO_CHANGE, suffix?: string): void;
|
||||
export function elementStyle<T>(
|
||||
index: number, styleName: string, value: T | NO_CHANGE, sanitizer?: Sanitizer): void;
|
||||
export function elementStyle<T>(
|
||||
index: number, styleName: string, value: T | NO_CHANGE,
|
||||
suffixOrSanitizer?: string | Sanitizer): void {
|
||||
if (value !== NO_CHANGE) {
|
||||
const lElement = data[index] as LElementNode;
|
||||
if (value == null) {
|
||||
|
@ -853,7 +870,9 @@ export function elementStyle<T>(
|
|||
renderer.removeStyle(lElement.native, styleName, RendererStyleFlags3.DashCase) :
|
||||
lElement.native.style.removeProperty(styleName);
|
||||
} else {
|
||||
const strValue = suffix ? stringify(value) + suffix : stringify(value);
|
||||
let strValue =
|
||||
typeof suffixOrSanitizer == 'function' ? suffixOrSanitizer(value) : stringify(value);
|
||||
if (typeof suffixOrSanitizer == 'string') strValue = strValue + suffixOrSanitizer;
|
||||
isProceduralRenderer(renderer) ?
|
||||
renderer.setStyle(lElement.native, styleName, strValue, RendererStyleFlags3.DashCase) :
|
||||
lElement.native.style.setProperty(styleName, strValue);
|
||||
|
|
|
@ -6,10 +6,9 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isDevMode} from '@angular/core';
|
||||
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {InertBodyHelper} from './inert_body';
|
||||
import {sanitizeSrcset, sanitizeUrl} from './url_sanitizer';
|
||||
import {_sanitizeUrl, sanitizeSrcset} from './url_sanitizer';
|
||||
|
||||
function tagSet(tags: string): {[k: string]: boolean} {
|
||||
const res: {[k: string]: boolean} = {};
|
||||
|
@ -143,21 +142,17 @@ class SanitizingHtmlSerializer {
|
|||
for (let i = 0; i < elAttrs.length; i++) {
|
||||
const elAttr = elAttrs.item(i);
|
||||
const attrName = elAttr.name;
|
||||
let value = elAttr.value;
|
||||
const lower = attrName.toLowerCase();
|
||||
if (!VALID_ATTRS.hasOwnProperty(lower)) {
|
||||
this.sanitizedSomething = true;
|
||||
continue;
|
||||
}
|
||||
let value = elAttr.value;
|
||||
// TODO(martinprobst): Special case image URIs for data:image/...
|
||||
if (URI_ATTRS[lower]) value = sanitizeUrl(value);
|
||||
if (URI_ATTRS[lower]) value = _sanitizeUrl(value);
|
||||
if (SRCSET_ATTRS[lower]) value = sanitizeSrcset(value);
|
||||
this.buf.push(' ');
|
||||
this.buf.push(attrName);
|
||||
this.buf.push('="');
|
||||
this.buf.push(encodeEntities(value));
|
||||
this.buf.push('"');
|
||||
};
|
||||
this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
|
||||
}
|
||||
this.buf.push('>');
|
||||
}
|
||||
|
||||
|
@ -173,7 +168,9 @@ class SanitizingHtmlSerializer {
|
|||
private chars(chars: string) { this.buf.push(encodeEntities(chars)); }
|
||||
|
||||
checkClobberedElement(node: Node, nextNode: Node): Node {
|
||||
if (nextNode && node.contains(nextNode)) {
|
||||
if (nextNode &&
|
||||
(node.compareDocumentPosition(nextNode) &
|
||||
Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) {
|
||||
throw new Error(
|
||||
`Failed to sanitize html because the element is clobbered: ${(node as Element).outerHTML}`);
|
||||
}
|
||||
|
@ -214,7 +211,7 @@ let inertBodyHelper: InertBodyHelper;
|
|||
* Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
|
||||
* the DOM in a browser environment.
|
||||
*/
|
||||
export function sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
|
||||
export function _sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
|
||||
let inertBodyElement: HTMLElement|null = null;
|
||||
try {
|
||||
inertBodyHelper = inertBodyHelper || new InertBodyHelper(defaultDoc);
|
||||
|
@ -259,8 +256,8 @@ export function sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
|
|||
}
|
||||
|
||||
function getTemplateContent(el: Node): Node|null {
|
||||
return 'content' in el && isTemplateElement(el) ? (<any>el).content : null;
|
||||
return 'content' in el && isTemplateElement(el) ? el.content : null;
|
||||
}
|
||||
function isTemplateElement(el: Node): boolean {
|
||||
function isTemplateElement(el: Node): el is HTMLTemplateElement {
|
||||
return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {stringify} from '../render3/util';
|
||||
|
||||
import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer';
|
||||
import {_sanitizeStyle as _sanitizeStyle} from './style_sanitizer';
|
||||
import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
|
||||
|
||||
const BRAND = '__SANITIZER_TRUSTED_BRAND__';
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization.
|
||||
*
|
||||
* See: {@link TrustedHtmlString}, {@link TrustedResourceUrlString}, {@link TrustedScriptString},
|
||||
* {@link TrustedStyleString}, {@link TrustedUrlString}
|
||||
*/
|
||||
export interface TrustedString extends String {
|
||||
'__SANITIZER_TRUSTED_BRAND__': 'Html'|'Style'|'Script'|'Url'|'ResourceUrl';
|
||||
}
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization of `html` strings.
|
||||
*
|
||||
* See: {@link bypassSanitizationTrustHtml} and {@link htmlSanitizer}.
|
||||
*/
|
||||
export interface TrustedHtmlString extends TrustedString { '__SANITIZER_TRUSTED_BRAND__': 'Html'; }
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization of `style` strings.
|
||||
*
|
||||
* See: {@link bypassSanitizationTrustStyle} and {@link styleSanitizer}.
|
||||
*/
|
||||
export interface TrustedStyleString extends TrustedString {
|
||||
'__SANITIZER_TRUSTED_BRAND__': 'Style';
|
||||
}
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization of `url` strings.
|
||||
*
|
||||
* See: {@link bypassSanitizationTrustScript} and {@link scriptSanitizer}.
|
||||
*/
|
||||
export interface TrustedScriptString extends TrustedString {
|
||||
'__SANITIZER_TRUSTED_BRAND__': 'Script';
|
||||
}
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization of `url` strings.
|
||||
*
|
||||
* See: {@link bypassSanitizationTrustUrl} and {@link urlSanitizer}.
|
||||
*/
|
||||
export interface TrustedUrlString extends TrustedString { '__SANITIZER_TRUSTED_BRAND__': 'Url'; }
|
||||
|
||||
/**
|
||||
* A branded trusted string used with sanitization of `resourceUrl` strings.
|
||||
*
|
||||
* See: {@link bypassSanitizationTrustResourceUrl} and {@link resourceUrlSanitizer}.
|
||||
*/
|
||||
export interface TrustedResourceUrlString extends TrustedString {
|
||||
'__SANITIZER_TRUSTED_BRAND__': 'ResourceUrl';
|
||||
}
|
||||
|
||||
/**
|
||||
* An `html` sanitizer which converts untrusted `html` **string** into trusted string by removing
|
||||
* dangerous content.
|
||||
*
|
||||
* This method parses the `html` and locates potentially dangerous content (such as urls and
|
||||
* javascript) and removes it.
|
||||
*
|
||||
* It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustHtml}.
|
||||
*
|
||||
* @param unsafeHtml untrusted `html`, typically from the user.
|
||||
* @returns `html` string which is safe to display to user, because all of the dangerous javascript
|
||||
* and urls have been removed.
|
||||
*/
|
||||
export function sanitizeHtml(unsafeHtml: any): string {
|
||||
if (unsafeHtml instanceof String && (unsafeHtml as TrustedHtmlString)[BRAND] === 'Html') {
|
||||
return unsafeHtml.toString();
|
||||
}
|
||||
return _sanitizeHtml(document, stringify(unsafeHtml));
|
||||
}
|
||||
|
||||
/**
|
||||
* A `style` sanitizer which converts untrusted `style` **string** into trusted string by removing
|
||||
* dangerous content.
|
||||
*
|
||||
* This method parses the `style` and locates potentially dangerous content (such as urls and
|
||||
* javascript) and removes it.
|
||||
*
|
||||
* It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustStyle}.
|
||||
*
|
||||
* @param unsafeStyle untrusted `style`, typically from the user.
|
||||
* @returns `style` string which is safe to bind to the `style` properties, because all of the
|
||||
* dangerous javascript and urls have been removed.
|
||||
*/
|
||||
export function sanitizeStyle(unsafeStyle: any): string {
|
||||
if (unsafeStyle instanceof String && (unsafeStyle as TrustedStyleString)[BRAND] === 'Style') {
|
||||
return unsafeStyle.toString();
|
||||
}
|
||||
return _sanitizeStyle(stringify(unsafeStyle));
|
||||
}
|
||||
|
||||
/**
|
||||
* A `url` sanitizer which converts untrusted `url` **string** into trusted string by removing
|
||||
* dangerous
|
||||
* content.
|
||||
*
|
||||
* This method parses the `url` and locates potentially dangerous content (such as javascript) and
|
||||
* removes it.
|
||||
*
|
||||
* It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustUrl}.
|
||||
*
|
||||
* @param unsafeUrl untrusted `url`, typically from the user.
|
||||
* @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
|
||||
* all of the dangerous javascript has been removed.
|
||||
*/
|
||||
export function sanitizeUrl(unsafeUrl: any): string {
|
||||
if (unsafeUrl instanceof String && (unsafeUrl as TrustedUrlString)[BRAND] === 'Url') {
|
||||
return unsafeUrl.toString();
|
||||
}
|
||||
return _sanitizeUrl(stringify(unsafeUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* A `url` sanitizer which only lets trusted `url`s through.
|
||||
*
|
||||
* This passes only `url`s marked trusted by calling {@link bypassSanitizationTrustResourceUrl}.
|
||||
*
|
||||
* @param unsafeResourceUrl untrusted `url`, typically from the user.
|
||||
* @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
|
||||
* only trusted `url`s have been allowed to pass.
|
||||
*/
|
||||
export function sanitizeResourceUrl(unsafeResourceUrl: any): string {
|
||||
if (unsafeResourceUrl instanceof String &&
|
||||
(unsafeResourceUrl as TrustedResourceUrlString)[BRAND] === 'ResourceUrl') {
|
||||
return unsafeResourceUrl.toString();
|
||||
}
|
||||
throw new Error('unsafe value used in a resource URL context (see http://g.co/ng/security#xss)');
|
||||
}
|
||||
|
||||
/**
|
||||
* A `script` sanitizer which only lets trusted javascript through.
|
||||
*
|
||||
* This passes only `script`s marked trusted by calling {@link bypassSanitizationTrustScript}.
|
||||
*
|
||||
* @param unsafeScript untrusted `script`, typically from the user.
|
||||
* @returns `url` string which is safe to bind to the `<script>` element such as `<img src>`,
|
||||
* because only trusted `scripts`s have been allowed to pass.
|
||||
*/
|
||||
export function sanitizeScript(unsafeScript: any): string {
|
||||
if (unsafeScript instanceof String && (unsafeScript as TrustedScriptString)[BRAND] === 'Script') {
|
||||
return unsafeScript.toString();
|
||||
}
|
||||
throw new Error('unsafe value used in a script context');
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark `html` string as trusted.
|
||||
*
|
||||
* This function wraps the trusted string in `String` and brands it in a way which makes it
|
||||
* recognizable to {@link htmlSanitizer} to be trusted implicitly.
|
||||
*
|
||||
* @param trustedHtml `html` string which needs to be implicitly trusted.
|
||||
* @returns a `html` `String` which has been branded to be implicitly trusted.
|
||||
*/
|
||||
export function bypassSanitizationTrustHtml(trustedHtml: string): TrustedHtmlString {
|
||||
return bypassSanitizationTrustString(trustedHtml, 'Html');
|
||||
}
|
||||
/**
|
||||
* Mark `style` string as trusted.
|
||||
*
|
||||
* This function wraps the trusted string in `String` and brands it in a way which makes it
|
||||
* recognizable to {@link styleSanitizer} to be trusted implicitly.
|
||||
*
|
||||
* @param trustedStyle `style` string which needs to be implicitly trusted.
|
||||
* @returns a `style` `String` which has been branded to be implicitly trusted.
|
||||
*/
|
||||
export function bypassSanitizationTrustStyle(trustedStyle: string): TrustedStyleString {
|
||||
return bypassSanitizationTrustString(trustedStyle, 'Style');
|
||||
}
|
||||
/**
|
||||
* Mark `script` string as trusted.
|
||||
*
|
||||
* This function wraps the trusted string in `String` and brands it in a way which makes it
|
||||
* recognizable to {@link scriptSanitizer} to be trusted implicitly.
|
||||
*
|
||||
* @param trustedScript `script` string which needs to be implicitly trusted.
|
||||
* @returns a `script` `String` which has been branded to be implicitly trusted.
|
||||
*/
|
||||
export function bypassSanitizationTrustScript(trustedScript: string): TrustedScriptString {
|
||||
return bypassSanitizationTrustString(trustedScript, 'Script');
|
||||
}
|
||||
/**
|
||||
* Mark `url` string as trusted.
|
||||
*
|
||||
* This function wraps the trusted string in `String` and brands it in a way which makes it
|
||||
* recognizable to {@link urlSanitizer} to be trusted implicitly.
|
||||
*
|
||||
* @param trustedUrl `url` string which needs to be implicitly trusted.
|
||||
* @returns a `url` `String` which has been branded to be implicitly trusted.
|
||||
*/
|
||||
export function bypassSanitizationTrustUrl(trustedUrl: string): TrustedUrlString {
|
||||
return bypassSanitizationTrustString(trustedUrl, 'Url');
|
||||
}
|
||||
/**
|
||||
* Mark `url` string as trusted.
|
||||
*
|
||||
* This function wraps the trusted string in `String` and brands it in a way which makes it
|
||||
* recognizable to {@link resourceUrlSanitizer} to be trusted implicitly.
|
||||
*
|
||||
* @param trustedResourceUrl `url` string which needs to be implicitly trusted.
|
||||
* @returns a `url` `String` which has been branded to be implicitly trusted.
|
||||
*/
|
||||
export function bypassSanitizationTrustResourceUrl(trustedResourceUrl: string):
|
||||
TrustedResourceUrlString {
|
||||
return bypassSanitizationTrustString(trustedResourceUrl, 'ResourceUrl');
|
||||
}
|
||||
|
||||
|
||||
function bypassSanitizationTrustString(trustedString: string, mode: 'Html'): TrustedHtmlString;
|
||||
function bypassSanitizationTrustString(trustedString: string, mode: 'Style'): TrustedStyleString;
|
||||
function bypassSanitizationTrustString(trustedString: string, mode: 'Script'): TrustedScriptString;
|
||||
function bypassSanitizationTrustString(trustedString: string, mode: 'Url'): TrustedUrlString;
|
||||
function bypassSanitizationTrustString(
|
||||
trustedString: string, mode: 'ResourceUrl'): TrustedResourceUrlString;
|
||||
function bypassSanitizationTrustString(
|
||||
trustedString: string,
|
||||
mode: 'Html' | 'Style' | 'Script' | 'Url' | 'ResourceUrl'): TrustedString {
|
||||
const trusted = new String(trustedString) as TrustedString;
|
||||
trusted[BRAND] = mode;
|
||||
return trusted;
|
||||
}
|
|
@ -6,9 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isDevMode} from '@angular/core';
|
||||
|
||||
import {sanitizeUrl} from './url_sanitizer';
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {_sanitizeUrl} from './url_sanitizer';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -83,14 +82,14 @@ function hasBalancedQuotes(value: string) {
|
|||
* Sanitizes the given untrusted CSS style property value (i.e. not an entire object, just a single
|
||||
* value) and returns a value that is safe to use in a browser environment.
|
||||
*/
|
||||
export function sanitizeStyle(value: string): string {
|
||||
export function _sanitizeStyle(value: string): string {
|
||||
value = String(value).trim(); // Make sure it's actually a string.
|
||||
if (!value) return '';
|
||||
|
||||
// Single url(...) values are supported, but only for URLs that sanitize cleanly. See above for
|
||||
// reasoning behind this.
|
||||
const urlMatch = value.match(URL_RE);
|
||||
if ((urlMatch && sanitizeUrl(urlMatch[1]) === urlMatch[1]) ||
|
||||
if ((urlMatch && _sanitizeUrl(urlMatch[1]) === urlMatch[1]) ||
|
||||
value.match(SAFE_STYLE_VALUE) && hasBalancedQuotes(value)) {
|
||||
return value; // Safe style values.
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {isDevMode} from '@angular/core';
|
||||
|
||||
import {isDevMode} from '../application_ref';
|
||||
|
||||
/**
|
||||
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
||||
|
@ -44,7 +43,7 @@ const SAFE_SRCSET_PATTERN = /^(?:(?:https?|file):|[^&:/?#]*(?:[/?#]|$))/gi;
|
|||
const DATA_URL_PATTERN =
|
||||
/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+\/]+=*$/i;
|
||||
|
||||
export function sanitizeUrl(url: string): string {
|
||||
export function _sanitizeUrl(url: string): string {
|
||||
url = String(url);
|
||||
if (url.match(SAFE_URL_PATTERN) || url.match(DATA_URL_PATTERN)) return url;
|
||||
|
||||
|
@ -57,5 +56,5 @@ export function sanitizeUrl(url: string): string {
|
|||
|
||||
export function sanitizeSrcset(srcset: string): string {
|
||||
srcset = String(srcset);
|
||||
return srcset.split(',').map((srcset) => sanitizeUrl(srcset.trim())).join(', ');
|
||||
return srcset.split(',').map((srcset) => _sanitizeUrl(srcset.trim())).join(', ');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, ContentChildren, Directive, HostBinding, HostListener, Injectable, Input, NgModule, OnDestroy, Optional, Pipe, PipeTransform, QueryList, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef} from '../../../src/core';
|
||||
import * as $r3$ from '../../../src/core_render3_private_export';
|
||||
import {getHostElement} from '../../../src/render3/index';
|
||||
import {renderComponent, toHtml} from '../render_util';
|
||||
|
||||
/**
|
||||
* NORMATIVE => /NORMATIVE: Designates what the compiler is expected to generate.
|
||||
*
|
||||
* All local variable names are considered non-normative (informative). They should be
|
||||
* wrapped in $ on each end to simplify testing on the compiler side.
|
||||
*/
|
||||
|
||||
describe('compiler sanitization', () => {
|
||||
type $boolean$ = boolean;
|
||||
it('should translate DOM structure', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<div [innerHTML]="innerHTML"></div>` +
|
||||
`<img [style.background-image]="style" [src]="src">` +
|
||||
`<script [attr.src]=src></script>`
|
||||
})
|
||||
class MyComponent {
|
||||
innerHTML: string = '<frame></frame>';
|
||||
style: string = `url("http://evil")`;
|
||||
url: string = 'javascript:evil()';
|
||||
|
||||
// NORMATIVE
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
tag: 'my-component',
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(ctx: $MyComponent$, cm: $boolean$) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵE(1, 'img');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(0, 'innerHTML', $r3$.ɵb(ctx.innerHTML), $r3$.ɵsanitizeHtml);
|
||||
$r3$.ɵs(1, 'background-image', $r3$.ɵb(ctx.style), $r3$.ɵsanitizeStyle);
|
||||
$r3$.ɵp(1, 'src', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
$r3$.ɵa(1, 'srcset', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
}
|
||||
});
|
||||
// /NORMATIVE
|
||||
}
|
||||
|
||||
const myComponent = renderComponent(MyComponent);
|
||||
const div = getHostElement(myComponent).querySelector('div') !;
|
||||
// because sanitizer removed it is working.
|
||||
expect(div.innerHTML).toEqual('');
|
||||
|
||||
const img = getHostElement(myComponent).querySelector('img') !;
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.getAttribute('src')).toEqual('unsafe:javascript:evil()');
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.style.getPropertyValue('background-image')).toEqual('');
|
||||
// because sanitizer removed it is working.
|
||||
expect(img.getAttribute('srcset')).toEqual('unsafe:javascript:evil()');
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {elementAttribute, elementEnd, elementProperty, elementStart, elementStyle, renderTemplate} from '../../src/render3/instructions';
|
||||
import {LElementNode, LNode} from '../../src/render3/interfaces/node';
|
||||
import {RElement, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
import {bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
|
||||
import {TemplateFixture} from './render_util';
|
||||
|
||||
describe('instructions', () => {
|
||||
function createDiv() {
|
||||
elementStart(0, 'div');
|
||||
elementEnd();
|
||||
}
|
||||
|
||||
describe('elementAttribute', () => {
|
||||
it('should use sanitizer function', () => {
|
||||
const t = new TemplateFixture(createDiv);
|
||||
|
||||
t.update(() => elementAttribute(0, 'title', 'javascript:true', sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="unsafe:javascript:true"></div>');
|
||||
|
||||
t.update(
|
||||
() => elementAttribute(
|
||||
0, 'title', bypassSanitizationTrustUrl('javascript:true'), sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="javascript:true"></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('elementProperty', () => {
|
||||
it('should use sanitizer function', () => {
|
||||
const t = new TemplateFixture(createDiv);
|
||||
|
||||
t.update(() => elementProperty(0, 'title', 'javascript:true', sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="unsafe:javascript:true"></div>');
|
||||
|
||||
t.update(
|
||||
() => elementProperty(
|
||||
0, 'title', bypassSanitizationTrustUrl('javascript:false'), sanitizeUrl));
|
||||
expect(t.html).toEqual('<div title="javascript:false"></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('elementStyle', () => {
|
||||
it('should use sanitizer function', () => {
|
||||
const t = new TemplateFixture(createDiv);
|
||||
t.update(() => elementStyle(0, 'background-image', 'url("http://server")', sanitizeStyle));
|
||||
// nothing is set because sanitizer suppresses it.
|
||||
expect(t.html).toEqual('<div></div>');
|
||||
|
||||
t.update(
|
||||
() => elementStyle(
|
||||
0, 'background-image', bypassSanitizationTrustStyle('url("http://server")'),
|
||||
sanitizeStyle));
|
||||
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
||||
.toEqual('url("http://server")');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -16,6 +16,58 @@ import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from
|
|||
|
||||
import {getRendererFactory2} from './imported_renderer2';
|
||||
|
||||
function noop() {}
|
||||
/**
|
||||
* Fixture for testing template functions in a convenient way.
|
||||
*
|
||||
* This fixture allows:
|
||||
* - specifying the creation block and update block as two separate functions,
|
||||
* - maintaining the template state between invocations,
|
||||
* - access to the render `html`.
|
||||
*/
|
||||
export class TemplateFixture {
|
||||
hostElement: HTMLElement;
|
||||
|
||||
hostNode: LElementNode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param createBlock Instructions which go into the creation block:
|
||||
* `if (creationMode) { __here__ }`.
|
||||
* @param updateBlock Optional instructions which go after the creation block:
|
||||
* `if (creationMode) { ... } __here__`.
|
||||
*/
|
||||
constructor(private createBlock: () => void, private updateBlock: () => void = noop) {
|
||||
this.updateBlock = updateBlock || function() {};
|
||||
this.hostElement = document.createElement('div');
|
||||
this.hostNode = renderTemplate(this.hostElement, (ctx: any, cm: boolean) => {
|
||||
if (cm) {
|
||||
this.createBlock();
|
||||
}
|
||||
this.updateBlock();
|
||||
}, null !, domRendererFactory3, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the existing template
|
||||
*
|
||||
* @param updateBlock Optional update block.
|
||||
*/
|
||||
update(updateBlock?: () => void): void {
|
||||
renderTemplate(
|
||||
this.hostNode.native, updateBlock || this.updateBlock, null !, domRendererFactory3,
|
||||
this.hostNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Current state of rendered HTML.
|
||||
*/
|
||||
get html(): string {
|
||||
return (this.hostNode.native as any as Element).innerHTML.replace(/ style=""/g, '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const document = ((global || window) as any).document;
|
||||
export let containerEl: HTMLElement = null !;
|
||||
let host: LElementNode|null;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
|
||||
|
||||
import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
||||
import {_sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
||||
|
||||
{
|
||||
describe('HTML sanitizer', () => {
|
||||
|
@ -26,62 +26,62 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
afterEach(() => { console.warn = originalLog; });
|
||||
|
||||
it('serializes nested structures', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>'))
|
||||
.toEqual('<div alt="x"><p>a</p>b<b>c<a alt="more">d</a></b>e</div>');
|
||||
expect(logMsgs).toEqual([]);
|
||||
});
|
||||
|
||||
it('serializes self closing elements', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<p>Hello <br> World</p>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<p>Hello <br> World</p>'))
|
||||
.toEqual('<p>Hello <br> World</p>');
|
||||
});
|
||||
|
||||
it('supports namespaced elements',
|
||||
() => { expect(sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc'); });
|
||||
() => { expect(_sanitizeHtml(defaultDoc, 'a<my:hr/><my:div>b</my:div>c')).toEqual('abc'); });
|
||||
|
||||
it('supports namespaced attributes', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<a xlink:href="something">t</a>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<a xlink:href="something">t</a>'))
|
||||
.toEqual('<a xlink:href="something">t</a>');
|
||||
expect(sanitizeHtml(defaultDoc, '<a xlink:evil="something">t</a>')).toEqual('<a>t</a>');
|
||||
expect(sanitizeHtml(defaultDoc, '<a xlink:href="javascript:foo()">t</a>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<a xlink:evil="something">t</a>')).toEqual('<a>t</a>');
|
||||
expect(_sanitizeHtml(defaultDoc, '<a xlink:href="javascript:foo()">t</a>'))
|
||||
.toEqual('<a xlink:href="unsafe:javascript:foo()">t</a>');
|
||||
});
|
||||
|
||||
it('supports HTML5 elements', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<main><summary>Works</summary></main>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<main><summary>Works</summary></main>'))
|
||||
.toEqual('<main><summary>Works</summary></main>');
|
||||
});
|
||||
|
||||
it('sanitizes srcset attributes', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<img srcset="/foo.png 400px, javascript:evil() 23px">'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<img srcset="/foo.png 400px, javascript:evil() 23px">'))
|
||||
.toEqual('<img srcset="/foo.png 400px, unsafe:javascript:evil() 23px">');
|
||||
});
|
||||
|
||||
it('supports sanitizing plain text',
|
||||
() => { expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); });
|
||||
() => { expect(_sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World'); });
|
||||
|
||||
it('ignores non-element, non-attribute nodes', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<!-- comments? -->no.')).toEqual('no.');
|
||||
expect(sanitizeHtml(defaultDoc, '<?pi nodes?>no.')).toEqual('no.');
|
||||
expect(_sanitizeHtml(defaultDoc, '<!-- comments? -->no.')).toEqual('no.');
|
||||
expect(_sanitizeHtml(defaultDoc, '<?pi nodes?>no.')).toEqual('no.');
|
||||
expect(logMsgs.join('\n')).toMatch(/sanitizing HTML stripped some content/);
|
||||
});
|
||||
|
||||
it('supports sanitizing escaped entities', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '🚀')).toEqual('🚀');
|
||||
expect(_sanitizeHtml(defaultDoc, '🚀')).toEqual('🚀');
|
||||
expect(logMsgs).toEqual([]);
|
||||
});
|
||||
|
||||
it('does not warn when just re-encoding text', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<p>Hellö Wörld</p>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<p>Hellö Wörld</p>'))
|
||||
.toEqual('<p>Hellö Wörld</p>');
|
||||
expect(logMsgs).toEqual([]);
|
||||
});
|
||||
|
||||
it('escapes entities', () => {
|
||||
expect(sanitizeHtml(defaultDoc, '<p>Hello < World</p>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<p>Hello < World</p>'))
|
||||
.toEqual('<p>Hello < World</p>');
|
||||
expect(sanitizeHtml(defaultDoc, '<p>Hello < World</p>')).toEqual('<p>Hello < World</p>');
|
||||
expect(sanitizeHtml(defaultDoc, '<p alt="% & " !">Hello</p>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<p>Hello < World</p>')).toEqual('<p>Hello < World</p>');
|
||||
expect(_sanitizeHtml(defaultDoc, '<p alt="% & " !">Hello</p>'))
|
||||
.toEqual('<p alt="% & " !">Hello</p>'); // NB: quote encoded as ASCII ".
|
||||
});
|
||||
|
||||
|
@ -93,11 +93,11 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
|
||||
for (const tag of dangerousTags) {
|
||||
it(`${tag}`,
|
||||
() => { expect(sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!'); });
|
||||
() => { expect(_sanitizeHtml(defaultDoc, `<${tag}>evil!</${tag}>`)).toEqual('evil!'); });
|
||||
}
|
||||
|
||||
it(`swallows frame entirely`, () => {
|
||||
expect(sanitizeHtml(defaultDoc, `<frame>evil!</frame>`)).not.toContain('<frame>');
|
||||
expect(_sanitizeHtml(defaultDoc, `<frame>evil!</frame>`)).not.toContain('<frame>');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -106,7 +106,7 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
|
||||
for (const attr of dangerousAttrs) {
|
||||
it(`${attr}`, () => {
|
||||
expect(sanitizeHtml(defaultDoc, `<a ${attr}="x">evil!</a>`)).toEqual('<a>evil!</a>');
|
||||
expect(_sanitizeHtml(defaultDoc, `<a ${attr}="x">evil!</a>`)).toEqual('<a>evil!</a>');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -117,17 +117,18 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
// Anyway what we want to test is that browsers do not enter an infinite loop which would
|
||||
// result in a timeout error for the test.
|
||||
try {
|
||||
sanitizeHtml(defaultDoc, '<form><input name="parentNode" /></form>');
|
||||
_sanitizeHtml(defaultDoc, '<form><input name="parentNode" /></form>');
|
||||
} catch (e) {
|
||||
// depending on the browser, we might ge an exception
|
||||
}
|
||||
try {
|
||||
sanitizeHtml(defaultDoc, '<form><input name="nextSibling" /></form>');
|
||||
_sanitizeHtml(defaultDoc, '<form><input name="nextSibling" /></form>');
|
||||
} catch (e) {
|
||||
// depending on the browser, we might ge an exception
|
||||
}
|
||||
try {
|
||||
sanitizeHtml(defaultDoc, '<form><div><div><input name="nextSibling" /></div></div></form>');
|
||||
_sanitizeHtml(
|
||||
defaultDoc, '<form><div><div><input name="nextSibling" /></div></div></form>');
|
||||
} catch (e) {
|
||||
// depending on the browser, we might ge an exception
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
// See
|
||||
// https://github.com/cure53/DOMPurify/blob/a992d3a75031cb8bb032e5ea8399ba972bdf9a65/src/purify.js#L439-L449
|
||||
it('should not allow JavaScript execution when creating inert document', () => {
|
||||
const output = sanitizeHtml(defaultDoc, '<svg><g onload="window.xxx = 100"></g></svg>');
|
||||
const output = _sanitizeHtml(defaultDoc, '<svg><g onload="window.xxx = 100"></g></svg>');
|
||||
const window = defaultDoc.defaultView;
|
||||
if (window) {
|
||||
expect(window.xxx).toBe(undefined);
|
||||
|
@ -148,7 +149,7 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
// See https://github.com/cure53/DOMPurify/releases/tag/0.6.7
|
||||
it('should not allow JavaScript hidden in badly formed HTML to get through sanitization (Firefox bug)',
|
||||
() => {
|
||||
expect(sanitizeHtml(
|
||||
expect(_sanitizeHtml(
|
||||
defaultDoc, '<svg><p><style><img src="</style><img src=x onerror=alert(1)//">'))
|
||||
.toEqual(
|
||||
isDOMParserAvailable() ?
|
||||
|
@ -161,7 +162,7 @@ import {sanitizeHtml} from '../../src/sanitization/html_sanitizer';
|
|||
if (browserDetection.isWebkit) {
|
||||
it('should prevent mXSS attacks', function() {
|
||||
// In Chrome Canary 62, the ideographic space character is kept as a stringified HTML entity
|
||||
expect(sanitizeHtml(defaultDoc, '<a href=" javascript:alert(1)">CLICKME</a>'))
|
||||
expect(_sanitizeHtml(defaultDoc, '<a href=" javascript:alert(1)">CLICKME</a>'))
|
||||
.toMatch(/<a href="unsafe:( )?javascript:alert\(1\)">CLICKME<\/a>/);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, sanitizeHtml, sanitizeResourceUrl, sanitizeScript, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
|
||||
describe('sanitization', () => {
|
||||
class Wrap {
|
||||
constructor(private value: string) {}
|
||||
toString() { return this.value; }
|
||||
}
|
||||
it('should sanitize html', () => {
|
||||
expect(sanitizeHtml('<div></div>')).toEqual('<div></div>');
|
||||
expect(sanitizeHtml(new Wrap('<div></div>'))).toEqual('<div></div>');
|
||||
expect(sanitizeHtml('<img src="javascript:true">'))
|
||||
.toEqual('<img src="unsafe:javascript:true">');
|
||||
expect(sanitizeHtml(new Wrap('<img src="javascript:true">')))
|
||||
.toEqual('<img src="unsafe:javascript:true">');
|
||||
expect(sanitizeHtml(bypassSanitizationTrustUrl('<img src="javascript:true">')))
|
||||
.toEqual('<img src="unsafe:javascript:true">');
|
||||
expect(sanitizeHtml(bypassSanitizationTrustHtml('<img src="javascript:true">')))
|
||||
.toEqual('<img src="javascript:true">');
|
||||
});
|
||||
|
||||
it('should sanitize url', () => {
|
||||
expect(sanitizeUrl('http://server')).toEqual('http://server');
|
||||
expect(sanitizeUrl(new Wrap('http://server'))).toEqual('http://server');
|
||||
expect(sanitizeUrl('javascript:true')).toEqual('unsafe:javascript:true');
|
||||
expect(sanitizeUrl(new Wrap('javascript:true'))).toEqual('unsafe:javascript:true');
|
||||
expect(sanitizeUrl(bypassSanitizationTrustHtml('javascript:true')))
|
||||
.toEqual('unsafe:javascript:true');
|
||||
expect(sanitizeUrl(bypassSanitizationTrustUrl('javascript:true'))).toEqual('javascript:true');
|
||||
});
|
||||
|
||||
it('should sanitize resourceUrl', () => {
|
||||
const ERROR = 'unsafe value used in a resource URL context (see http://g.co/ng/security#xss)';
|
||||
expect(() => sanitizeResourceUrl('http://server')).toThrowError(ERROR);
|
||||
expect(() => sanitizeResourceUrl('javascript:true')).toThrowError(ERROR);
|
||||
expect(() => sanitizeResourceUrl(bypassSanitizationTrustHtml('javascript:true')))
|
||||
.toThrowError(ERROR);
|
||||
expect(sanitizeResourceUrl(bypassSanitizationTrustResourceUrl('javascript:true')))
|
||||
.toEqual('javascript:true');
|
||||
});
|
||||
|
||||
it('should sanitize style', () => {
|
||||
expect(sanitizeStyle('red')).toEqual('red');
|
||||
expect(sanitizeStyle(new Wrap('red'))).toEqual('red');
|
||||
expect(sanitizeStyle('url("http://server")')).toEqual('unsafe');
|
||||
expect(sanitizeStyle(new Wrap('url("http://server")'))).toEqual('unsafe');
|
||||
expect(sanitizeStyle(bypassSanitizationTrustHtml('url("http://server")'))).toEqual('unsafe');
|
||||
expect(sanitizeStyle(bypassSanitizationTrustStyle('url("http://server")')))
|
||||
.toEqual('url("http://server")');
|
||||
});
|
||||
|
||||
it('should sanitize script', () => {
|
||||
const ERROR = 'unsafe value used in a script context';
|
||||
expect(() => sanitizeScript('true')).toThrowError(ERROR);
|
||||
expect(() => sanitizeScript('true')).toThrowError(ERROR);
|
||||
expect(() => sanitizeScript(bypassSanitizationTrustHtml('true'))).toThrowError(ERROR);
|
||||
expect(sanitizeScript(bypassSanitizationTrustScript('true'))).toEqual('true');
|
||||
});
|
||||
});
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as t from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {sanitizeStyle} from '../../src/sanitization/style_sanitizer';
|
||||
import {_sanitizeStyle} from '../../src/sanitization/style_sanitizer';
|
||||
|
||||
{
|
||||
t.describe('Style sanitizer', () => {
|
||||
|
@ -23,7 +23,7 @@ import {sanitizeStyle} from '../../src/sanitization/style_sanitizer';
|
|||
|
||||
afterEach(() => { console.warn = originalLog; });
|
||||
|
||||
function expectSanitize(v: string) { return t.expect(sanitizeStyle(v)); }
|
||||
function expectSanitize(v: string) { return t.expect(_sanitizeStyle(v)); }
|
||||
|
||||
t.it('sanitizes values', () => {
|
||||
expectSanitize('').toEqual('');
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as t from '@angular/core/testing/src/testing_internal';
|
||||
|
||||
import {sanitizeSrcset, sanitizeUrl} from '../../src/sanitization/url_sanitizer';
|
||||
import {_sanitizeUrl, sanitizeSrcset} from '../../src/sanitization/url_sanitizer';
|
||||
|
||||
{
|
||||
t.describe('URL sanitizer', () => {
|
||||
|
@ -24,7 +24,7 @@ import {sanitizeSrcset, sanitizeUrl} from '../../src/sanitization/url_sanitizer'
|
|||
afterEach(() => { console.warn = originalLog; });
|
||||
|
||||
t.it('reports unsafe URLs', () => {
|
||||
t.expect(sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()');
|
||||
t.expect(_sanitizeUrl('javascript:evil()')).toBe('unsafe:javascript:evil()');
|
||||
t.expect(logMsgs.join('\n')).toMatch(/sanitizing unsafe URL value/);
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ import {sanitizeSrcset, sanitizeUrl} from '../../src/sanitization/url_sanitizer'
|
|||
'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
|
||||
];
|
||||
for (const url of validUrls) {
|
||||
t.it(`valid ${url}`, () => t.expect(sanitizeUrl(url)).toEqual(url));
|
||||
t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toEqual(url));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -73,7 +73,7 @@ import {sanitizeSrcset, sanitizeUrl} from '../../src/sanitization/url_sanitizer'
|
|||
'data:application/x-msdownload;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
|
||||
];
|
||||
for (const url of invalidUrls) {
|
||||
t.it(`valid ${url}`, () => t.expect(sanitizeUrl(url)).toMatch(/^unsafe:/));
|
||||
t.it(`valid ${url}`, () => t.expect(_sanitizeUrl(url)).toMatch(/^unsafe:/));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Inject, Injectable, Sanitizer, SecurityContext, ɵsanitizeHtml as sanitizeHtml, ɵsanitizeStyle as sanitizeStyle, ɵsanitizeUrl as sanitizeUrl} from '@angular/core';
|
||||
import {Inject, Injectable, Sanitizer, SecurityContext, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl} from '@angular/core';
|
||||
|
||||
import {DOCUMENT} from '../dom/dom_tokens';
|
||||
|
||||
|
@ -156,11 +156,11 @@ export class DomSanitizerImpl extends DomSanitizer {
|
|||
case SecurityContext.HTML:
|
||||
if (value instanceof SafeHtmlImpl) return value.changingThisBreaksApplicationSecurity;
|
||||
this.checkNotSafeValue(value, 'HTML');
|
||||
return sanitizeHtml(this._doc, String(value));
|
||||
return _sanitizeHtml(this._doc, String(value));
|
||||
case SecurityContext.STYLE:
|
||||
if (value instanceof SafeStyleImpl) return value.changingThisBreaksApplicationSecurity;
|
||||
this.checkNotSafeValue(value, 'Style');
|
||||
return sanitizeStyle(value as string);
|
||||
return _sanitizeStyle(value as string);
|
||||
case SecurityContext.SCRIPT:
|
||||
if (value instanceof SafeScriptImpl) return value.changingThisBreaksApplicationSecurity;
|
||||
this.checkNotSafeValue(value, 'Script');
|
||||
|
@ -171,7 +171,7 @@ export class DomSanitizerImpl extends DomSanitizer {
|
|||
return value.changingThisBreaksApplicationSecurity;
|
||||
}
|
||||
this.checkNotSafeValue(value, 'URL');
|
||||
return sanitizeUrl(String(value));
|
||||
return _sanitizeUrl(String(value));
|
||||
case SecurityContext.RESOURCE_URL:
|
||||
if (value instanceof SafeResourceUrlImpl) {
|
||||
return value.changingThisBreaksApplicationSecurity;
|
||||
|
|
Loading…
Reference in New Issue