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);