Skip to content

Commit eeb03f4

Browse files
SkyZeroZxatscott
authored andcommitted
fix(common): Limits date format string length
Introduces a maximum length of 256 characters for date format strings. This prevents potential Denial of Service (DoS) attacks by throwing an `INVALID_DATE_FORMAT` error if an excessively long format string is provided to `formatDate` or `DatePipe`, safeguarding against performance degradation or application crashes.
1 parent af7cb63 commit eeb03f4

3 files changed

Lines changed: 28 additions & 0 deletions

File tree

packages/common/src/i18n/format_date.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const ISO8601_DATE_REGEX =
3636
const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {};
3737
const DATE_FORMATS_SPLIT =
3838
/((?:[^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]*)/;
39+
const MAX_DATE_FORMAT_LENGTH = 256;
3940

4041
const enum ZoneWidth {
4142
Short,
@@ -89,6 +90,7 @@ export function formatDate(
8990
timezone?: string,
9091
): string {
9192
let date = toDate(value);
93+
assertValidDateFormatLength(format);
9294
const namedFormat = getNamedFormat(locale, format);
9395
format = namedFormat || format;
9496

@@ -132,6 +134,16 @@ export function formatDate(
132134
return text;
133135
}
134136

137+
function assertValidDateFormatLength(format: string) {
138+
if (format.length > MAX_DATE_FORMAT_LENGTH) {
139+
throw new RuntimeError(
140+
RuntimeErrorCode.SUSPICIOUS_DATE_FORMAT,
141+
ngDevMode &&
142+
`Date format is too long. Exceeded maximum length of ${MAX_DATE_FORMAT_LENGTH} characters.`,
143+
);
144+
}
145+
}
146+
135147
/**
136148
* Asserts that the given date format is free from common mistakes. Throws an
137149
* error if one is found (except for the case of all "Y", in which case we just

packages/common/test/i18n/format_date_spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,16 @@ describe('Format date', () => {
351351
});
352352
});
353353

354+
it('should throw if the date format exceeds 256 characters to prevent DoS', () => {
355+
const expectedError = /Exceeded maximum length of 256 characters/;
356+
expect(() => formatDate(date, 'y'.repeat(257), ɵDEFAULT_LOCALE_ID)).toThrowError(
357+
expectedError,
358+
);
359+
expect(() => formatDate(date, "'literal'".repeat(257), ɵDEFAULT_LOCALE_ID)).toThrowError(
360+
expectedError,
361+
);
362+
});
363+
354364
it('should format invalid in IE ISO date', () =>
355365
expect(formatDate('2017-01-11T12:00:00.014-0500', defaultFormat, ɵDEFAULT_LOCALE_ID)).toEqual(
356366
'Jan 11, 2017',

packages/common/test/pipes/date_pipe_spec.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ describe('DatePipe', () => {
7777
it('should give precedence to the passed in format', () =>
7878
expect(pipe.transform('2017-01-11T10:14:39+0000', 'shortDate')).toEqual('1/11/17'));
7979

80+
it('should reject date formats that are too long', () => {
81+
expect(() => pipe.transform(date, 'y'.repeat(257))).toThrowError(
82+
/InvalidPipeArgument: 'NG02300: Date format is too long/,
83+
);
84+
});
85+
8086
it('should use format provided in component as default format when no format is passed in', () => {
8187
@Component({
8288
selector: 'test-component',

0 commit comments

Comments
 (0)