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: ` -