From 420b9be1c14ec6900692b641a3d89b99eb9c6e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matias=20Niemel=C3=A4?= Date: Fri, 21 Feb 2020 14:11:00 -0800 Subject: [PATCH] refactor: disable sanitization for [style] and [style.prop] bindings (#35621) This patch is the first of many commits to disable sanitization for [stlye.prop] and [style] bindings in Angular. Historically, style-based sanitization has only been required for old IE browsers (IE6 and IE7). Since Angular does not support these old browsers at all, there is no reason for the framework to support style-based sanitization. PR Close #35621 --- aio/content/guide/deprecations.md | 91 ++++++++++++++++ goldens/size-tracking/aio-payloads.json | 8 +- .../size-tracking/integration-payloads.json | 4 +- packages/core/src/core_private_export.ts | 1 - .../core/src/sanitization/sanitization.ts | 20 ++-- .../core/src/sanitization/style_sanitizer.ts | 102 ++---------------- packages/core/test/acceptance/styling_spec.ts | 22 ++-- .../test/linker/security_integration_spec.ts | 20 ---- .../test/render3/instructions/styling_spec.ts | 28 +---- .../core/test/render3/instructions_spec.ts | 4 +- .../test/sanitization/sanitization_spec.ts | 4 +- .../test/sanitization/style_sanitizer_spec.ts | 82 -------------- .../src/security/dom_sanitization_service.ts | 4 +- 13 files changed, 132 insertions(+), 258 deletions(-) delete mode 100644 packages/core/test/sanitization/style_sanitizer_spec.ts diff --git a/aio/content/guide/deprecations.md b/aio/content/guide/deprecations.md index 64b80ad1bc..94647e47e3 100644 --- a/aio/content/guide/deprecations.md +++ b/aio/content/guide/deprecations.md @@ -494,6 +494,7 @@ The following APIs have been removed starting with version 10.0.0*: | ---------------- | -------------- | ----------- | ----- | | `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info | | `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info | +| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](#style-sanitization) for more info *To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed). @@ -545,3 +546,93 @@ In practical terms, the `package.json` of all `@angular` packages has changed in ``` For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv). + +{@a ie-9-10} +### IE 9 and 10 support + +Support for IE 9 and 10 has been deprecated and will be removed in a future version. +Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework. +For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors. + +The final decision was made on three key points: +* __Vendor support__: Microsoft dropped support of IE 9 and 10 on 1/12/16, meaning they no longer provide security updates or technical support. +* __Usage statistics__: We looked at usage trends for IE 9 and 10 from various sources and all indicated that usage percentages were extremely small (fractions of 1%). +* __Feedback from partners__: We also reached out to some of our Angular customers and none expressed concern about dropping IE 9 and 10 support. + +{@a removed} +## Removed APIs + +The following APIs have been removed starting with version 10.0.0*: + +| Package | API | Replacement | Notes | +| ---------------- | -------------- | ----------- | ----- | +| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info | +| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info | + +*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed). + + + +{@a http} +### @angular/http + + + + +The entire [`@angular/http`](http://v7.angular.io/api/http) package has been removed. Use [`@angular/common/http`](api/common/http) instead. + +The new API is a smaller, easier, and more powerful way to make HTTP requests in Angular. +The new API simplifies the default ergonomics: There is no need to map by invoking the `.json()` method. +It also supports typed return values and interceptors. + +To update your apps: +* Replace `HttpModule` with [`HttpClientModule`](api/common/http/HttpClientModule) (from [`@angular/common/http`](api/common/http)) in each of your modules. +* Replace the `Http` service with the [`HttpClient`](api/common/http/HttpClient) service. +* Remove any `map(res => res.json())` calls. They are no longer needed. + +For more information about using `@angular/common/http`, see the [HttpClient guide](guide/http "HTTP Client guide"). + + +| `@angular/http` | Closest replacement in `@angular/common/http` | +| ------------- | ------------------------------------------- | +| `BaseRequestOptions` | [`HttpRequest`](/api/common/http/HttpRequest) | +| `BaseResponseOptions` | [`HttpResponse`](/api/common/http/HttpResponse) | +| `BrowserXhr` | | +| `Connection` | [`HttpBackend`](/api/common/http/HttpBackend) | +| `ConnectionBackend` | [`HttpBackend`](/api/common/http/HttpBackend) | +| `CookieXSRFStrategy` | [`HttpClientXsrfModule`](/api/common/http/HttpClientXsrfModule) | +| `Headers` | [`HttpHeaders`](/api/common/http/HttpHeaders) | +| `Http` | [`HttpClient`](/api/common/http/HttpClient) | +| `HttpModule` | [`HttpClientModule`](/api/common/http/HttpClientModule) | +| `Jsonp` | [`HttpClient`](/api/common/http/HttpClient) | +| `JSONPBackend` | [`JsonpClientBackend`](/api/common/http/JsonpClientBackend) | +| `JSONPConnection` | [`JsonpClientBackend`](/api/common/http/JsonpClientBackend) | +| `JsonpModule` | [`HttpClientJsonpModule`](/api/common/http/HttpClientJsonpModule) | +| `QueryEncoder` | [`HttpUrlEncodingCodec`](/api/common/http/HttpUrlEncodingCodec) | +| `ReadyState` | [`HttpBackend`](/api/common/http/HttpBackend) | +| `Request` | [`HttpRequest`](/api/common/http/HttpRequest) | +| `RequestMethod` | [`HttpClient`](/api/common/http/HttpClient) | +| `RequestOptions` | [`HttpRequest`](/api/common/http/HttpRequest) | +| `RequestOptionsArgs` | [`HttpRequest`](/api/common/http/HttpRequest) | +| `Response` | [`HttpResponse`](/api/common/http/HttpResponse) | +| `ResponseContentType` | [`HttpClient`](/api/common/http/HttpClient) | +| `ResponseOptions` | [`HttpResponse`](/api/common/http/HttpResponse) | +| `ResponseOptionsArgs` | [`HttpResponse`](/api/common/http/HttpResponse) | +| `ResponseType` | [`HttpClient`](/api/common/http/HttpClient) | +| `URLSearchParams` | [`HttpParams`](/api/common/http/HttpParams) | +| `XHRBackend` | [`HttpXhrBackend`](/api/common/http/HttpXhrBackend) | +| `XHRConnection` | [`HttpXhrBackend`](/api/common/http/HttpXhrBackend) | +| `XSRFStrategy` | [`HttpClientXsrfModule`](/api/common/http/HttpClientXsrfModule) | + + +| `@angular/http/testing` | Closest replacement in `@angular/common/http/testing` | +| --------------------- | ------------------------------------------- | +| `MockBackend` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) | +| `MockConnection` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) | + +{@a style-sanitization} +### Style Sanitization for `[style]` and `[style.prop]` bindings +Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular. diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index 2b96ecfa8b..ae822a944b 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -12,8 +12,8 @@ "master": { "uncompressed": { "runtime-es2015": 2987, - "main-es2015": 454047, - "polyfills-es2015": 52628 + "main-es2015": 453518, + "polyfills-es2015": 52195 } } }, @@ -21,8 +21,8 @@ "master": { "uncompressed": { "runtime-es2015": 3097, - "main-es2015": 430383, - "polyfills-es2015": 52628 + "main-es2015": 429742, + "polyfills-es2015": 52195 } } } diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index e4ec706125..1be3f61112 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -39,7 +39,7 @@ "master": { "uncompressed": { "runtime-es2015": 2289, - "main-es2015": 248671, + "main-es2015": 247761, "polyfills-es2015": 36657, "5-es2015": 751 } @@ -60,7 +60,7 @@ "uncompressed": { "bundle": "TODO(i): temporarily increase the payload size limit from 105779 - this is due to a closure issue related to ESM reexports that still needs to be investigated", "bundle": "TODO(i): we should define ngDevMode to false in Closure, but --define only works in the global scope.", - "bundle": 170618 + "bundle": 169991 } } } diff --git a/packages/core/src/core_private_export.ts b/packages/core/src/core_private_export.ts index e8d075ea93..111345e31c 100644 --- a/packages/core/src/core_private_export.ts +++ b/packages/core/src/core_private_export.ts @@ -26,7 +26,6 @@ export {ReflectionCapabilities as ɵReflectionCapabilities} from './reflection/r export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types'; export {allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, BypassType as ɵBypassType, getSanitizationBypassType as ɵgetSanitizationBypassType, SafeHtml as ɵSafeHtml, SafeResourceUrl as ɵSafeResourceUrl, SafeScript as ɵSafeScript, SafeStyle as ɵSafeStyle, SafeUrl as ɵSafeUrl, SafeValue as ɵSafeValue, unwrapSafeValue as ɵunwrapSafeValue} from './sanitization/bypass'; 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 {looseIdentical as ɵlooseIdentical,} from './util/comparison'; export {makeDecorator as ɵmakeDecorator} from './util/decorators'; diff --git a/packages/core/src/sanitization/sanitization.ts b/packages/core/src/sanitization/sanitization.ts index cf9a3a4562..65246196e0 100644 --- a/packages/core/src/sanitization/sanitization.ts +++ b/packages/core/src/sanitization/sanitization.ts @@ -15,7 +15,7 @@ import {allowSanitizationBypassAndThrow, BypassType, unwrapSafeValue} from './by import {_sanitizeHtml as _sanitizeHtml} from './html_sanitizer'; import {Sanitizer} from './sanitizer'; import {SecurityContext} from './security'; -import {_sanitizeStyle, StyleSanitizeFn, StyleSanitizeMode} from './style_sanitizer'; +import {StyleSanitizeFn, StyleSanitizeMode} from './style_sanitizer'; import {_sanitizeUrl as _sanitizeUrl} from './url_sanitizer'; @@ -50,14 +50,10 @@ export function ɵɵsanitizeHtml(unsafeHtml: any): string { * 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. + * @returns `style` string which is safe to bind to the `style` properties. * * @publicApi */ @@ -69,7 +65,7 @@ export function ɵɵsanitizeStyle(unsafeStyle: any): string { if (allowSanitizationBypassAndThrow(unsafeStyle, BypassType.Style)) { return unwrapSafeValue(unsafeStyle); } - return _sanitizeStyle(renderStringify(unsafeStyle)); + return renderStringify(unsafeStyle); } /** @@ -181,8 +177,14 @@ export function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl: any, tag: string, prop: } /** - * The default style sanitizer will handle sanitization for style properties by - * sanitizing any CSS property that can include a `url` value (usually image-based properties) + * 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 */ diff --git a/packages/core/src/sanitization/style_sanitizer.ts b/packages/core/src/sanitization/style_sanitizer.ts index a1c0b7e0da..2217a7ce6f 100644 --- a/packages/core/src/sanitization/style_sanitizer.ts +++ b/packages/core/src/sanitization/style_sanitizer.ts @@ -5,104 +5,22 @@ * 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 {isDevMode} from '../util/is_dev_mode'; import {SafeValue} from './bypass'; -import {_sanitizeUrl} from './url_sanitizer'; - -/** - * Regular expression for safe style values. +/* + * ========== WARNING ========== * - * Quotes (" and ') are allowed, but a check must be done elsewhere to ensure they're balanced. + * 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. * - * ',' allows multiple values to be assigned to the same property (e.g. background-attachment or - * font-family) and hence could allow multiple values to get injected, but that should pose no risk - * of XSS. + * 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. * - * The function expression checks only for XSS safety, not for CSS validity. - * - * This regular expression was taken from the Closure sanitization library, and augmented for - * transformation values. + * ============================= */ -const VALUES = '[-,."\'%_!# a-zA-Z0-9]+'; -const TRANSFORMATION_FNS = '(?:matrix|translate|scale|rotate|skew|perspective)(?:X|Y|Z|3d)?'; -const COLOR_FNS = '(?:rgb|hsl)a?'; -const GRADIENTS = '(?:repeating-)?(?:linear|radial)-gradient'; -const CSS3_FNS = '(?:attr|calc|var)'; -const FN_ARGS = '\\([-0-9.%, #a-zA-Z]+\\)'; -const SAFE_STYLE_VALUE = new RegExp( - `^(${VALUES}|` + - `(?:${TRANSFORMATION_FNS}|${COLOR_FNS}|${GRADIENTS}|${CSS3_FNS})` + - `${FN_ARGS})$`, - 'g'); - -/** - * Matches a `url(...)` value with an arbitrary argument as long as it does - * not contain parentheses. - * - * The URL value still needs to be sanitized separately. - * - * `url(...)` values are a very common use case, e.g. for `background-image`. With carefully crafted - * CSS style rules, it is possible to construct an information leak with `url` values in CSS, e.g. - * by observing whether scroll bars are displayed, or character ranges used by a font face - * definition. - * - * Angular only allows binding CSS values (as opposed to entire CSS rules), so it is unlikely that - * binding a URL value without further cooperation from the page will cause an information leak, and - * if so, it is just a leak, not a full blown XSS vulnerability. - * - * Given the common use case, low likelihood of attack vector, and low impact of an attack, this - * code is permissive and allows URLs that sanitize otherwise. - */ -const URL_RE = /^url\(([^)]+)\)$/; - -/** - * Checks that quotes (" and ') are properly balanced inside a string. Assumes - * that neither escape (\) nor any other character that could result in - * breaking out of a string parsing context are allowed; - * see http://www.w3.org/TR/css3-syntax/#string-token-diagram. - * - * This code was taken from the Closure sanitization library. - */ -function hasBalancedQuotes(value: string) { - let outsideSingle = true; - let outsideDouble = true; - for (let i = 0; i < value.length; i++) { - const c = value.charAt(i); - if (c === '\'' && outsideDouble) { - outsideSingle = !outsideSingle; - } else if (c === '"' && outsideSingle) { - outsideDouble = !outsideDouble; - } - } - return outsideSingle && outsideDouble; -} - -/** - * 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 { - 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]) || - value.match(SAFE_STYLE_VALUE) && hasBalancedQuotes(value)) { - return value; // Safe style values. - } - - if (isDevMode()) { - console.warn( - `WARNING: sanitizing unsafe style value ${value} (see http://g.co/ng/security#xss).`); - } - - return 'unsafe'; -} - /** * A series of flags to instruct a style sanitizer to either validate diff --git a/packages/core/test/acceptance/styling_spec.ts b/packages/core/test/acceptance/styling_spec.ts index 8f1fc30b54..72f06e9c18 100644 --- a/packages/core/test/acceptance/styling_spec.ts +++ b/packages/core/test/acceptance/styling_spec.ts @@ -34,7 +34,7 @@ describe('styling', () => { it('should perform prop bindings', () => { @Component({ - template: `
` }) @@ -52,7 +52,7 @@ describe('styling', () => { onlyInIvy('style merging is ivy only feature').it('should perform map bindings', () => { @Component({ - template: `
` }) class Cmp { @@ -1883,9 +1883,9 @@ describe('styling', () => { .it('should sanitize style values before writing them', () => { @Component({ template: ` -
- ` +
+ ` }) class Cmp { widthExp = ''; @@ -1902,10 +1902,7 @@ describe('styling', () => { comp.bgImageExp = 'url("javascript:img")'; fixture.detectChanges(); - // for some reasons `background-image: unsafe` is suppressed - expect(getSortedStyle(div)).toEqual(''); - fixture.detectChanges(); - expect(getSortedStyle(div)).not.toContain('javascript'); + expect(getSortedStyle(div)).toContain('javascript:img'); // Prove that bindings work. comp.widthExp = '789px'; @@ -1938,11 +1935,6 @@ describe('styling', () => { comp.styleMapExp['background-image'] = 'url("javascript:img")'; fixture.detectChanges(); - // for some reasons `background-image: unsafe` is suppressed - expect(getSortedStyle(div)).toEqual(''); - - // for some reasons `border-image: unsafe` is NOT suppressed - fixture.detectChanges(); expect(getSortedStyle(div)).not.toContain('javascript'); // Prove that bindings work. @@ -2943,7 +2935,7 @@ describe('styling', () => { () => { @Component({ template: ` -
{ - const template = `
Text
`; - TestBed.overrideComponent(SecuredComponent, {set: {template}}); - const fixture = TestBed.createComponent(SecuredComponent); - - const e = fixture.debugElement.children[0].nativeElement; - const ci = fixture.componentInstance; - // Make sure binding harmless values works. - ci.ctxProp = 'red'; - fixture.detectChanges(); - // In some browsers, this will contain the full background specification, not just - // the color. - expect(e.style['background']).toMatch(/red.*/); - - ci.ctxProp = 'url(javascript:evil())'; - fixture.detectChanges(); - // Updated value gets rejected, no value change. - expect(e.style['background']).not.toContain('javascript'); - }); - modifiedInIvy('Unknown property error thrown during update mode, not creation mode') .it('should escape unsafe SVG attributes', () => { const template = `Text`; diff --git a/packages/core/test/render3/instructions/styling_spec.ts b/packages/core/test/render3/instructions/styling_spec.ts index 8751b15bff..5b5ea5047f 100644 --- a/packages/core/test/render3/instructions/styling_spec.ts +++ b/packages/core/test/render3/instructions/styling_spec.ts @@ -242,32 +242,6 @@ describe('styling', () => { expectStyle(div).toEqual({width: '200px'}); }); }); - - describe('sanitization', () => { - it('should sanitize property', () => { - ɵɵstyleSanitizer(ɵɵsanitizeStyle); - ɵɵstyleProp('background', 'url("javascript:/unsafe")'); - expect(div.style.getPropertyValue('background')).not.toContain('javascript'); - - clearFirstUpdatePass(); - - rewindBindingIndex(); - ɵɵstyleProp('background', bypassSanitizationTrustStyle('url("javascript:/trusted")')); - expect(div.style.getPropertyValue('background')).toContain('url("javascript:/trusted")'); - }); - - it('should sanitize map', () => { - ɵɵstyleSanitizer(ɵɵsanitizeStyle); - ɵɵstyleMap('background: url("javascript:/unsafe")'); - expect(div.style.getPropertyValue('background')).not.toContain('javascript'); - - clearFirstUpdatePass(); - - rewindBindingIndex(); - ɵɵstyleMap({'background': bypassSanitizationTrustStyle('url("javascript:/trusted")')}); - expect(div.style.getPropertyValue('background')).toContain('url("javascript:/trusted")'); - }); - }); }); describe('static', () => { @@ -578,4 +552,4 @@ function expectTStylingKeys(styling: 'style'|'class') { (isClassBased ? tNode.residualClasses : tNode.residualStyles) as null | string[]); return expect(tStylingKeys); -} \ No newline at end of file +} diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index 3f90a06cda..95074c5b76 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -163,7 +163,7 @@ describe('instructions', () => { }); describe('styleProp', () => { - it('should automatically sanitize unless a bypass operation is applied', () => { + it('should allow values even if a bypass operation is applied', () => { let backgroundImage: string|SafeValue = 'url("http://server")'; const t = new TemplateFixture( () => { @@ -176,7 +176,7 @@ describe('instructions', () => { 2, 2); // nothing is set because sanitizer suppresses it. expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image')) - .toEqual(''); + .toEqual('url("http://server")'); backgroundImage = bypassSanitizationTrustStyle('url("http://server2")'); t.update(); diff --git a/packages/core/test/sanitization/sanitization_spec.ts b/packages/core/test/sanitization/sanitization_spec.ts index d9dcc1bae1..106b4e34e2 100644 --- a/packages/core/test/sanitization/sanitization_spec.ts +++ b/packages/core/test/sanitization/sanitization_spec.ts @@ -64,8 +64,8 @@ describe('sanitization', () => { 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('url("http://server")')).toEqual('url("http://server")'); + expect(ɵɵsanitizeStyle(new Wrap('url("http://server")'))).toEqual('url("http://server")'); expect(() => ɵɵsanitizeStyle(bypassSanitizationTrustHtml('url("http://server")'))) .toThrowError(/Required a safe Style, got a HTML/); expect(ɵɵsanitizeStyle(bypassSanitizationTrustStyle('url("http://server")'))) diff --git a/packages/core/test/sanitization/style_sanitizer_spec.ts b/packages/core/test/sanitization/style_sanitizer_spec.ts deleted file mode 100644 index 0a776483f9..0000000000 --- a/packages/core/test/sanitization/style_sanitizer_spec.ts +++ /dev/null @@ -1,82 +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 {_sanitizeStyle} from '../../src/sanitization/style_sanitizer'; - - -describe('Style sanitizer', () => { - let logMsgs: string[]; - let originalLog: (msg: any) => any; - - beforeEach(() => { - logMsgs = []; - originalLog = console.warn; // Monkey patch DOM.log. - console.warn = (msg: any) => logMsgs.push(msg); - }); - - afterEach(() => { - console.warn = originalLog; - }); - - function expectSanitize(v: string) { - return expect(_sanitizeStyle(v)); - } - - it('sanitizes values', () => { - expectSanitize('').toEqual(''); - expectSanitize('abc').toEqual('abc'); - expectSanitize('50px').toEqual('50px'); - expectSanitize('rgb(255, 0, 0)').toEqual('rgb(255, 0, 0)'); - expectSanitize('expression(haha)').toEqual('unsafe'); - }); - - it('rejects unblanaced quotes', () => { - expectSanitize('"value" "').toEqual('unsafe'); - }); - - it('accepts transform functions', () => { - expectSanitize('rotate(90deg)').toEqual('rotate(90deg)'); - expectSanitize('rotate(javascript:evil())').toEqual('unsafe'); - expectSanitize('translateX(12px, -5px)').toEqual('translateX(12px, -5px)'); - expectSanitize('scale3d(1, 1, 2)').toEqual('scale3d(1, 1, 2)'); - }); - - it('accepts gradients', () => { - expectSanitize('linear-gradient(to bottom, #fg34a1, #bada55)') - .toEqual('linear-gradient(to bottom, #fg34a1, #bada55)'); - expectSanitize('repeating-radial-gradient(ellipse cover, black, red, black, red)') - .toEqual('repeating-radial-gradient(ellipse cover, black, red, black, red)'); - }); - - it('accepts attr', () => { - expectSanitize('attr(value string)').toEqual('attr(value string)'); - }); - - it('accepts calc', () => { - expectSanitize('calc(90%-123px)').toEqual('calc(90%-123px)'); - }); - - it('accepts var', () => { - expectSanitize('var(--my-custom-var)').toEqual('var(--my-custom-var)'); - }); - - it('sanitizes URLs', () => { - expectSanitize('url(foo/bar.png)').toEqual('url(foo/bar.png)'); - expectSanitize('url( foo/bar.png\n )').toEqual('url( foo/bar.png\n )'); - expectSanitize('url(javascript:evil())').toEqual('unsafe'); - expectSanitize('url(strangeprotocol:evil)').toEqual('unsafe'); - }); - - it('accepts quoted URLs', () => { - expectSanitize('url("foo/bar.png")').toEqual('url("foo/bar.png")'); - expectSanitize(`url('foo/bar.png')`).toEqual(`url('foo/bar.png')`); - expectSanitize(`url( 'foo/bar.png'\n )`).toEqual(`url( 'foo/bar.png'\n )`); - expectSanitize('url("javascript:evil()")').toEqual('unsafe'); - expectSanitize('url( " javascript:evil() " )').toEqual('unsafe'); - }); -}); diff --git a/packages/platform-browser/src/security/dom_sanitization_service.ts b/packages/platform-browser/src/security/dom_sanitization_service.ts index 4bb39ff56e..997d5b2e4f 100644 --- a/packages/platform-browser/src/security/dom_sanitization_service.ts +++ b/packages/platform-browser/src/security/dom_sanitization_service.ts @@ -7,7 +7,7 @@ */ import {DOCUMENT} from '@angular/common'; -import {forwardRef, Inject, Injectable, Injector, Sanitizer, SecurityContext, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeStyle as _sanitizeStyle, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵBypassType as BypassType, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core'; +import {forwardRef, Inject, Injectable, Injector, Sanitizer, SecurityContext, ɵ_sanitizeHtml as _sanitizeHtml, ɵ_sanitizeUrl as _sanitizeUrl, ɵallowSanitizationBypassAndThrow as allowSanitizationBypassOrThrow, ɵbypassSanitizationTrustHtml as bypassSanitizationTrustHtml, ɵbypassSanitizationTrustResourceUrl as bypassSanitizationTrustResourceUrl, ɵbypassSanitizationTrustScript as bypassSanitizationTrustScript, ɵbypassSanitizationTrustStyle as bypassSanitizationTrustStyle, ɵbypassSanitizationTrustUrl as bypassSanitizationTrustUrl, ɵBypassType as BypassType, ɵgetSanitizationBypassType as getSanitizationBypassType, ɵunwrapSafeValue as unwrapSafeValue} from '@angular/core'; export {SecurityContext}; @@ -167,7 +167,7 @@ export class DomSanitizerImpl extends DomSanitizer { if (allowSanitizationBypassOrThrow(value, BypassType.Style)) { return unwrapSafeValue(value); } - return _sanitizeStyle(value as string); + return value as string; case SecurityContext.SCRIPT: if (allowSanitizationBypassOrThrow(value, BypassType.Script)) { return unwrapSafeValue(value);