refactor(core): remove style sanitization code for [style]/[style.prop] bindings (#36965)

In 420b9be1c1 all style-based sanitization code was
disabled because modern browsers no longer allow for javascript expressions within
CSS. This patch is a follow-up patch which removes all traces of style sanitization
code (both instructions and runtime logic) for the `[style]` and `[style.prop]` bindings.

PR Close #36965
This commit is contained in:
Matias Niemelä
2020-05-06 16:14:37 -07:00
committed by Misko Hevery
parent 141fcb95a4
commit 45f4a47286
17 changed files with 54 additions and 409 deletions

View File

@ -227,7 +227,6 @@ export {
ɵɵstylePropInterpolate7,
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstyleSanitizer,
ɵɵtemplate,
ɵɵtemplateRefExtractor,
ɵɵtext,
@ -286,7 +285,6 @@ export {
bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl,
} from './sanitization/bypass';
export {
ɵɵdefaultStyleSanitizer,
ɵɵsanitizeHtml,
ɵɵsanitizeResourceUrl,
ɵɵsanitizeScript,

View File

@ -113,7 +113,6 @@ export {
ɵɵstylePropInterpolate8,
ɵɵstylePropInterpolateV,
ɵɵstyleSanitizer,
ɵɵtemplate,
ɵɵtext,

View File

@ -7,7 +7,7 @@
*/
import {bindingUpdated} from '../bindings';
import {SanitizerFn} from '../interfaces/sanitization';
import {getLView, getSelectedIndex, getSelectedTNode, getTView, nextBindingIndex} from '../state';
import {getLView, getSelectedTNode, getTView, nextBindingIndex} from '../state';
import {elementAttributeInternal, storePropertyBindingMetadata} from './shared';

View File

@ -7,8 +7,6 @@
*/
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
import {stylePropNeedsSanitization, ɵɵsanitizeStyle} from '../../sanitization/sanitization';
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {KeyValueArray, keyValueArrayGet, keyValueArraySet} from '../../util/array_utils';
import {assertDefined, assertEqual, assertLessThan, assertNotEqual, throwError} from '../../util/assert';
import {EMPTY_ARRAY} from '../../util/empty';
@ -18,11 +16,10 @@ import {bindingUpdated} from '../bindings';
import {DirectiveDef} from '../interfaces/definition';
import {AttributeMarker, TAttributes, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement, Renderer3} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {getTStylingRangeNext, getTStylingRangeNextDuplicate, getTStylingRangePrev, getTStylingRangePrevDuplicate, TStylingKey, TStylingRange} from '../interfaces/styling';
import {HEADER_OFFSET, LView, RENDERER, TData, TView} from '../interfaces/view';
import {applyStyling} from '../node_manipulation';
import {getCurrentDirectiveDef, getCurrentStyleSanitizer, getLView, getSelectedIndex, getTView, incrementBindingIndex, setCurrentStyleSanitizer} from '../state';
import {getCurrentDirectiveDef, getLView, getSelectedIndex, getTView, incrementBindingIndex} from '../state';
import {insertTStylingBinding} from '../styling/style_binding_list';
import {getLastParsedKey, getLastParsedValue, parseClassName, parseClassNameNext, parseStyle, parseStyleNext} from '../styling/styling_parser';
import {NO_CHANGE} from '../tokens';
@ -31,26 +28,6 @@ import {getNativeByIndex} from '../util/view_utils';
import {setDirectiveInputsWhichShadowsStyling} from './property';
/**
* Sets the current style sanitizer function which will then be used
* within all follow-up prop and map-based style binding instructions
* for the given element.
*
* Note that once styling has been applied to the element (i.e. once
* `advance(n)` is executed or the hostBindings/template function exits)
* then the active `sanitizerFn` will be set to `null`. This means that
* once styling is applied to another element then a another call to
* `styleSanitizer` will need to be made.
*
* @param sanitizerFn The sanitization function that will be used to
* process style prop/value entries.
*
* @codeGenApi
*/
export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn|null): void {
setCurrentStyleSanitizer(sanitizer);
}
/**
* Update a style binding on an element with the provided value.
*
@ -64,8 +41,6 @@ export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn|null): void {
* @param prop A valid CSS property.
* @param value New value to write (`null` or an empty string to remove).
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
* Note that when a suffix is provided then the underlying sanitizer will
* be ignored.
*
* Note that this will apply the provided style value to the host element if this function is called
* within a host binding function.
@ -183,11 +158,11 @@ export function classStringParser(keyValueArray: KeyValueArray<any>, text: strin
*
* @param prop property name.
* @param value binding value.
* @param suffixOrSanitizer suffix or sanitization function
* @param suffix suffix for the property (e.g. `em` or `px`)
* @param isClassBased `true` if `class` change (`false` if `style`)
*/
export function checkStylingProperty(
prop: string, value: any|NO_CHANGE, suffixOrSanitizer: SanitizerFn|string|undefined|null,
prop: string, value: any|NO_CHANGE, suffix: string|undefined|null,
isClassBased: boolean): void {
const lView = getLView();
const tView = getTView();
@ -199,19 +174,10 @@ export function checkStylingProperty(
stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
}
if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
// This is a work around. Once PR#34480 lands the sanitizer is passed explicitly and this line
// can be removed.
let styleSanitizer: StyleSanitizeFn|null;
if (suffixOrSanitizer == null) {
if (styleSanitizer = getCurrentStyleSanitizer()) {
suffixOrSanitizer = styleSanitizer as any;
}
}
const tNode = tView.data[getSelectedIndex() + HEADER_OFFSET] as TNode;
updateStyling(
tView, tNode, lView, lView[RENDERER], prop,
lView[bindingIndex + 1] = normalizeAndApplySuffixOrSanitizer(value, suffixOrSanitizer),
isClassBased, bindingIndex);
lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
}
}
@ -219,9 +185,7 @@ export function checkStylingProperty(
* Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* function so that `style` can be processed. This is done for tree shaking purposes.
* @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
* have different parsers.)
* @param value bound value from application
@ -605,9 +569,8 @@ function collectStylingFromTAttrs(
* keep additional `Map` to keep track of duplicates or items which have not yet been visited.
*
* @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
* function so that
* `style` can pass in version which does sanitization. This is done for tree shaking
* purposes.
* function so that `style` can be processed. This is done
* for tree shaking purposes.
* @param stringParser The parser is passed in so that it will be tree shakable. See
* `styleStringParser` and `classStringParser`
* @param value The value to parse/convert to `KeyValueArray`
@ -639,19 +602,16 @@ export function toStylingKeyValueArray(
}
/**
* Set a `value` for a `key` taking style sanitization into account.
* Set a `value` for a `key`.
*
* See: `keyValueArraySet` for details
*
* @param keyValueArray KeyValueArray to add to.
* @param key Style key to add. (This key will be checked if it needs sanitization)
* @param value The value to set (If key needs sanitization it will be sanitized)
* @param key Style key to add.
* @param value The value to set.
*/
export function styleKeyValueArraySet(keyValueArray: KeyValueArray<any>, key: string, value: any) {
if (stylePropNeedsSanitization(key)) {
value = ɵɵsanitizeStyle(value);
}
keyValueArraySet(keyValueArray, key, value);
keyValueArraySet(keyValueArray, key, unwrapSafeValue(value));
}
/**
@ -784,10 +744,7 @@ function updateStyling(
* NOTE: The styling stores two values.
* 1. The raw value which came from the application is stored at `index + 0` location. (This value
* is used for dirty checking).
* 2. The normalized value (converted to `KeyValueArray` if map and sanitized) is stored at `index +
* 1`.
* The advantage of storing the sanitized value is that once the value is written we don't need
* to worry about sanitizing it later or keeping track of the sanitizer.
* 2. The normalized value is stored at `index + 1`.
*
* @param tData `TData` used for traversing the priority.
* @param tNode `TNode` to use for resolving static styling. Also controls search direction.
@ -867,22 +824,17 @@ function isStylingValuePresent(value: any): boolean {
}
/**
* Sanitizes or adds suffix to the value.
* Normalizes and/or adds a suffix to the value.
*
* If value is `null`/`undefined` no suffix is added
* @param value
* @param suffixOrSanitizer
* @param suffix
*/
function normalizeAndApplySuffixOrSanitizer(
value: any, suffixOrSanitizer: SanitizerFn|string|undefined|null): string|null|undefined|
boolean {
function normalizeSuffix(value: any, suffix: string|undefined|null): string|null|undefined|boolean {
if (value == null /** || value === undefined */) {
// do nothing
} else if (typeof suffixOrSanitizer === 'function') {
// sanitize the value.
value = suffixOrSanitizer(value);
} else if (typeof suffixOrSanitizer === 'string') {
value = value + suffixOrSanitizer;
} else if (typeof suffix === 'string') {
value = value + suffix;
} else if (typeof value === 'object') {
value = stringify(unwrapSafeValue(value));
}

View File

@ -133,7 +133,6 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵstylePropInterpolate7': r3.ɵɵstylePropInterpolate7,
'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8,
'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV,
'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer,
'ɵɵclassProp': r3.ɵɵclassProp,
'ɵɵselect': r3.ɵɵselect,
'ɵɵadvance': r3.ɵɵadvance,
@ -164,7 +163,6 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵsanitizeHtml': sanitization.ɵɵsanitizeHtml,
'ɵɵsanitizeStyle': sanitization.ɵɵsanitizeStyle,
'ɵɵdefaultStyleSanitizer': sanitization.ɵɵdefaultStyleSanitizer,
'ɵɵsanitizeResourceUrl': sanitization.ɵɵsanitizeResourceUrl,
'ɵɵsanitizeScript': sanitization.ɵɵsanitizeScript,
'ɵɵsanitizeUrl': sanitization.ɵɵsanitizeUrl,

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
import {assertDefined, assertEqual} from '../util/assert';
import {assertLViewOrUndefined} from './assert';
import {DirectiveDef} from './interfaces/definition';
@ -97,11 +96,6 @@ interface LFrame {
*/
currentNamespace: string|null;
/**
* Current sanitizer
*/
currentSanitizer: StyleSanitizeFn|null;
/**
* The root index from which pure function instructions should calculate their binding
@ -421,7 +415,6 @@ export function enterView(newView: LView, tNode: TNode|null): void {
assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
assertEqual(newLFrame.currentSanitizer, null, 'Expected clean LFrame');
assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
}
@ -454,7 +447,6 @@ function createLFrame(parent: LFrame|null): LFrame {
contextLView: null!, //
elementDepthCount: 0, //
currentNamespace: null, //
currentSanitizer: null, //
currentDirectiveIndex: -1, //
bindingRootIndex: -1, //
bindingIndex: -1, //
@ -508,7 +500,6 @@ export function leaveView() {
oldLFrame.elementDepthCount = 0;
oldLFrame.currentDirectiveIndex = -1;
oldLFrame.currentNamespace = null;
oldLFrame.currentSanitizer = null;
oldLFrame.bindingRootIndex = -1;
oldLFrame.bindingIndex = -1;
oldLFrame.currentQueryIndex = 0;
@ -602,18 +593,3 @@ export function namespaceHTMLInternal() {
export function getNamespace(): string|null {
return instructionState.lFrame.currentNamespace;
}
export function setCurrentStyleSanitizer(sanitizer: StyleSanitizeFn|null) {
instructionState.lFrame.currentSanitizer = sanitizer;
}
export function resetCurrentStyleSanitizer() {
setCurrentStyleSanitizer(null);
}
export function getCurrentStyleSanitizer() {
// TODO(misko): This should throw when there is no LView, but it turns out we can get here from
// `NodeStyleDebug` hence we return `null`. This should be fixed
const lFrame = instructionState.lFrame;
return lFrame === null ? null : lFrame.currentSanitizer;
}

View File

@ -15,7 +15,6 @@ import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './by
import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer';
import {Sanitizer} from './sanitizer';
import {SecurityContext} from './security';
import {StyleSanitizeFn, StyleSanitizeMode} from './style_sanitizer';
import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer';
@ -176,47 +175,6 @@ export function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl: any, tag: string, prop:
return getUrlSanitizer(tag, prop)(unsafeUrl);
}
/**
* The default style sanitizer will handle sanitization for style properties.
*
* Style sanitization is no longer apart of Angular because modern browsers no
* longer support javascript expressions. Therefore, the reason why this API
* exists is exclusively for unwrapping any style value expressions that were
* marked as `SafeValue` values.
*
* This API will be removed in a future release of Angular.
*
* @publicApi
*/
export const ɵɵdefaultStyleSanitizer =
(function(prop: string, value: string|null, mode?: StyleSanitizeMode): string|boolean|null {
if (value === undefined && mode === undefined) {
// This is a workaround for the fact that `StyleSanitizeFn` should not exist once PR#34480
// lands. For now the `StyleSanitizeFn` and should act like `(value: any) => string` as a
// work around.
return ɵɵsanitizeStyle(prop);
}
mode = mode || StyleSanitizeMode.ValidateAndSanitize;
let doSanitizeValue = true;
if (mode & StyleSanitizeMode.ValidateProperty) {
doSanitizeValue = stylePropNeedsSanitization(prop);
}
if (mode & StyleSanitizeMode.SanitizeOnly) {
return doSanitizeValue ? ɵɵsanitizeStyle(value) : unwrapSafeValue(value);
} else {
return doSanitizeValue;
}
} as StyleSanitizeFn);
export function stylePropNeedsSanitization(prop: string): boolean {
return prop === 'background-image' || prop === 'backgroundImage' || prop === 'background' ||
prop === 'border-image' || prop === 'borderImage' || prop === 'border-image-source' ||
prop === 'borderImageSource' || prop === 'filter' || prop === 'list-style' ||
prop === 'listStyle' || prop === 'list-style-image' || prop === 'listStyleImage' ||
prop === 'clip-path' || prop === 'clipPath';
}
export function validateAgainstEventProperties(name: string) {
if (name.toLowerCase().startsWith('on')) {
const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +

View File

@ -1,58 +0,0 @@
/**
* @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 {SafeValue} from './bypass';
/*
* ========== WARNING ==========
*
* Style sanitization in Angular (for `[style.prop]` and `[style]` bindings)
* is no longer required and has been removed. The reason why this feature
* has been removed is because style-based sanitization is no longer
* required with modern browsers.
*
* The contents of this file are still in flux. Various APIs and symbols will
* be removed in a future version of Angular. Please hold off from modifying this
* file for the time being.
*
* =============================
*/
/**
* A series of flags to instruct a style sanitizer to either validate
* or sanitize a value.
*
* Because sanitization is dependent on the style property (i.e. style
* sanitization for `width` is much different than for `background-image`)
* the sanitization function (e.g. `StyleSanitizerFn`) needs to check a
* property value first before it actually sanitizes any values.
*
* This enum exist to allow a style sanitization function to either only
* do validation (check the property to see whether a value will be
* sanitized or not) or to sanitize the value (or both).
*
* @publicApi
*/
export const enum StyleSanitizeMode {
/** Just check to see if the property is required to be sanitized or not */
ValidateProperty = 0b01,
/** Skip checking the property; just sanitize the value */
SanitizeOnly = 0b10,
/** Check the property and (if true) then sanitize the value */
ValidateAndSanitize = 0b11,
}
/**
* Used to intercept and sanitize style values before they are written to the renderer.
*
* This function is designed to be called in two modes. When a value is not provided
* then the function will return a boolean whether a property will be sanitized later.
* If a value is provided then the sanitized version of that will be returned.
*/
export interface StyleSanitizeFn {
(prop: string, value: string|SafeValue|null, mode?: StyleSanitizeMode): any;
}