diff --git a/packages/utils/src/shorthand-time.ts b/packages/utils/src/shorthand-time.ts index 94f5c54f..30964d6d 100644 --- a/packages/utils/src/shorthand-time.ts +++ b/packages/utils/src/shorthand-time.ts @@ -28,8 +28,11 @@ export const shorthandToMilliseconds = ( // Check if the string is purely numeric if (Number.isNaN(Number(shorthand))) { - // Use a case-insensitive regex that supports decimals and 'ms' unit - const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand); + // Require at least one digit and at most one decimal point. + // Keep the decimal part in one group to prevent excess backtracking. + const match = /^(\d+(?:\.\d*)?|\.\d+)\s*(ms|s|m|h|hr|d)$/i.exec( + shorthand, + ); if (!match) { throw new Error( diff --git a/packages/utils/test/shorthand-time.test.ts b/packages/utils/test/shorthand-time.test.ts index c043096a..10d38fb9 100644 --- a/packages/utils/test/shorthand-time.test.ts +++ b/packages/utils/test/shorthand-time.test.ts @@ -34,6 +34,20 @@ describe("time parser", () => { "Unsupported time format: \"1z\". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.", ); }); + test.each(["1.2.3s", "1..5s", "..s", ".s", ".m"])( + 'reject a malformed number in "%s"', + (value) => { + expect(() => shorthandToMilliseconds(value)).toThrowError( + `Unsupported time format: "${value}"`, + ); + }, + ); + test.each([ + [".5s", 500], + ["5.s", 5000], + ])('convert "%s" to milliseconds', (value, expected) => { + expect(shorthandToMilliseconds(value)).toBe(expected); + }); test("send in string with number", () => { expect(shorthandToMilliseconds("1000")).toBe(1000); });