fix(common): add upper bounds for digitsInfo

The prevents the `roundNumber` function from allocating a large array.
This commit is contained in:
Matthieu Riegler
2026-05-21 00:23:23 +02:00
committed by Alex Rickabaugh
parent d5a489aed3
commit dfdfbe34a5
2 changed files with 28 additions and 2 deletions
+15 -1
View File
@@ -8,6 +8,7 @@
import {ɵRuntimeError as RuntimeError} from '@angular/core';
import {RuntimeErrorCode} from '../errors';
import {
getLocaleNumberFormat,
getLocaleNumberSymbol,
@@ -15,7 +16,6 @@ import {
NumberFormatStyle,
NumberSymbol,
} from './locale_data_api';
import {RuntimeErrorCode} from '../errors';
export const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
const MAX_DIGITS = 22;
@@ -77,6 +77,20 @@ function formatNumberToLocaleString(
} else if (minFractionPart != null && minFraction > maxFraction) {
maxFraction = minFraction;
}
// Prevent DoS via resource exhaustion by capping the maximum padding iterations
const MAX_ALLOWED_DIGITS = 100;
if (
minInt > MAX_ALLOWED_DIGITS ||
minFraction > MAX_ALLOWED_DIGITS ||
maxFraction > MAX_ALLOWED_DIGITS
) {
throw new RuntimeError(
RuntimeErrorCode.INVALID_DIGIT_INFO,
ngDevMode &&
`${digitsInfo} is not a valid digit info. Exceeded maximum limits of ${MAX_ALLOWED_DIGITS} digits.`,
);
}
}
roundNumber(parsedNumber, minFraction, maxFraction);
@@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {ɵDEFAULT_LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
import {formatCurrency, formatNumber, formatPercent} from '../../index';
import localeAr from '../../locales/ar';
import localeEn from '../../locales/en';
import localeEsUS from '../../locales/es-US';
import localeFr from '../../locales/fr';
import {ɵDEFAULT_LOCALE_ID, ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
describe('Format number', () => {
beforeAll(() => {
@@ -43,6 +43,18 @@ describe('Format number', () => {
/is higher than the maximum/,
);
});
it('should throw if minInt, minFraction, or maxFraction exceeds 100 to prevent DoS', () => {
const expectedError = /Exceeded maximum limits of 100 digits/;
expect(() => formatNumber(1.1, ɵDEFAULT_LOCALE_ID, '101.4-5')).toThrowError(expectedError);
expect(() => formatNumber(1.1, ɵDEFAULT_LOCALE_ID, '3.101-105')).toThrowError(
expectedError,
);
expect(() => formatNumber(1.1, ɵDEFAULT_LOCALE_ID, '3.4-101')).toThrowError(expectedError);
expect(() => formatNumber(1.1, ɵDEFAULT_LOCALE_ID, '1.2000000000-20000000')).toThrowError(
expectedError,
);
});
});
describe('transform with custom locales', () => {