@angular/common: Denial of Service (DoS) via OOM in Date Formatting (formatDate)
Description
A DoS vulnerability in Angular's formatDate function allows resource exhaustion via an excessively long format string, impacting SSR and CSR.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A DoS vulnerability in Angular's `formatDate` function allows resource exhaustion via an excessively long format string, impacting SSR and CSR.
Vulnerability
A Denial of Service (DoS) vulnerability exists in the @angular/common package of the Angular framework [1][2]. The formatDate function, also used by the standard Angular DatePipe, does not validate or limit the length of the format parameter. When parsing a maliciously crafted, excessively long date format string (e.g., a repeating pattern), the internal parser splits the string iteratively using a regular expression loop, leading to uncontrolled CPU and memory consumption [1][2][3]. This affects versions prior to 22.0.1, 21.2.17, and 20.3.25 [1][2].
Exploitation
An attacker must control the format parameter passed to formatDate or DatePipe (e.g., via query parameters, user preferences, or API responses) [1][2]. The format must be customisable by untrusted user input; hardcoded or properly validated formats are not exploitable [1][2]. No authentication or special network position is required if the application exposes this parameter. The attacker simply supplies an arbitrarily long format string (e.g., many repeating pattern characters) [1][2].
Impact
Successful exploitation causes uncontrolled resource consumption. In Server-Side Rendering (SSR), the server experiences high CPU usage and a JavaScript heap out of memory crash, rendering the application unavailable to all users [1][2]. In Client-Side Rendering (CSR), the browser's main thread blocks, freezing the tab completely [1][2]. The impact is a Denial of Service; no data confidentiality or integrity is compromised.
Mitigation
Patched versions released on 2026-06-15 are 22.0.1, 21.2.17, and 20.3.25 [1][2]. The fix introduces a maximum format string length of 256 characters and throws a RuntimeError if exceeded [3][4]. If upgrading is not immediately possible, ensure date format strings are hardcoded or validated to a reasonable length limit [1][2]. No other workarounds are documented in the available references.
AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
2eeb03f4ea310fix(common): Limits date format string length
3 files changed · +28 −0
packages/common/src/i18n/format_date.ts+12 −0 modified@@ -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
packages/common/test/i18n/format_date_spec.ts+10 −0 modified@@ -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',
packages/common/test/pipes/date_pipe_spec.ts+6 −0 modified@@ -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',
9f443bc24c79fix(common): Limits date format string length
3 files changed · +28 −0
packages/common/src/i18n/format_date.ts+12 −0 modified@@ -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
packages/common/test/i18n/format_date_spec.ts+10 −0 modified@@ -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',
packages/common/test/pipes/date_pipe_spec.ts+6 −0 modified@@ -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',
Vulnerability mechanics
Root cause
"Missing input length validation in the date format parser allows uncontrolled resource consumption via an excessively long format string."
Attack vector
An attacker supplies a maliciously crafted, excessively long date format string (e.g., a repeating pattern exceeding 256 characters) to the `formatDate` function or `DatePipe` [ref_id=1]. In server-side rendering (SSR) applications, processing this payload on the server causes high CPU usage and a JavaScript heap out-of-memory crash, making the application unavailable to all users. In client-side rendering (CSR), the same payload blocks the browser's main thread, freezing the tab. The attack requires that the date format string be attacker-controlled (e.g., from query parameters or user preferences) rather than hardcoded.
Affected code
The vulnerability resides in `packages/common/src/i18n/format_date.ts` in the `formatDate` function and the `DatePipe` that calls it. The internal parser splits the format string iteratively using the `DATE_FORMATS_SPLIT` regular expression loop without any length check, causing uncontrolled resource consumption when an excessively long format string is supplied.
What the fix does
The patch adds a `MAX_DATE_FORMAT_LENGTH` constant set to 256 and a new `assertValidDateFormatLength` function that throws a `RuntimeError` with code `SUSPICIOUS_DATE_FORMAT` if the format string exceeds that limit [patch_id=6085146]. This check is called at the top of `formatDate` before any parsing begins, preventing the regex-based splitting loop from ever processing an overly long string. The corresponding unit tests verify that both repeated pattern characters and repeated literal strings longer than 256 characters are rejected.
Preconditions
- configThe application must use `formatDate` or `DatePipe` to format dates.
- inputThe date format string must be controllable by untrusted user input (e.g., query parameters, user preferences, API responses).
Generated on Jun 15, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.