diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index 3e631a0b2ce2..00d8608a3c53 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -36,6 +36,7 @@ export const ISO8601_DATE_REGEX = const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {}; const DATE_FORMATS_SPLIT = /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; +const MAX_DATE_FORMAT_LENGTH = 256; const enum ZoneWidth { Short, @@ -89,6 +90,7 @@ export function formatDate( timezone?: string, ): string { let date = toDate(value); + assertValidDateFormatLength(format); const namedFormat = getNamedFormat(locale, format); format = namedFormat || format; @@ -132,6 +134,16 @@ export function formatDate( return text; } +function assertValidDateFormatLength(format: string) { + if (format.length > MAX_DATE_FORMAT_LENGTH) { + throw new RuntimeError( + RuntimeErrorCode.SUSPICIOUS_DATE_FORMAT, + ngDevMode && + `Date format is too long. Exceeded maximum length of ${MAX_DATE_FORMAT_LENGTH} characters.`, + ); + } +} + /** * Asserts that the given date format is free from common mistakes. Throws an * error if one is found (except for the case of all "Y", in which case we just diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index efbe9f2bceb5..f9223705a1d1 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -351,6 +351,16 @@ describe('Format date', () => { }); }); + it('should throw if the date format exceeds 256 characters to prevent DoS', () => { + const expectedError = /Exceeded maximum length of 256 characters/; + expect(() => formatDate(date, 'y'.repeat(257), ɵDEFAULT_LOCALE_ID)).toThrowError( + expectedError, + ); + expect(() => formatDate(date, "'literal'".repeat(257), ɵDEFAULT_LOCALE_ID)).toThrowError( + expectedError, + ); + }); + it('should format invalid in IE ISO date', () => expect(formatDate('2017-01-11T12:00:00.014-0500', defaultFormat, ɵDEFAULT_LOCALE_ID)).toEqual( 'Jan 11, 2017', diff --git a/packages/common/test/pipes/date_pipe_spec.ts b/packages/common/test/pipes/date_pipe_spec.ts index bc64539d6e64..9e3f3f03d766 100644 --- a/packages/common/test/pipes/date_pipe_spec.ts +++ b/packages/common/test/pipes/date_pipe_spec.ts @@ -77,6 +77,12 @@ describe('DatePipe', () => { it('should give precedence to the passed in format', () => expect(pipe.transform('2017-01-11T10:14:39+0000', 'shortDate')).toEqual('1/11/17')); + it('should reject date formats that are too long', () => { + expect(() => pipe.transform(date, 'y'.repeat(257))).toThrowError( + /InvalidPipeArgument: 'NG02300: Date format is too long/, + ); + }); + it('should use format provided in component as default format when no format is passed in', () => { @Component({ selector: 'test-component',