From 73bd054d19176f9df2e568f0d163bd514259102d Mon Sep 17 00:00:00 2001 From: Ninym Date: Mon, 20 Apr 2026 11:28:06 +0200 Subject: [PATCH] fix(cli-date): reject month/day overflow in parseLocalDate parseLocalDate validated the YYYY-MM-DD shape via regex but relied on the Date constructor to normalize values. That meant inputs like 2026-13-45 or 2026-02-30 silently rolled forward into a different valid date, and --from/--to then produced a misleading "must not be after" error instead of rejecting the input. Check the round-tripped fields against the parsed numbers and throw a clear "month or day out of range" error when they diverge. --- src/cli-date.ts | 6 +++++- tests/date-range-filter.test.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cli-date.ts b/src/cli-date.ts index 66831b96..4b58ed4f 100644 --- a/src/cli-date.ts +++ b/src/cli-date.ts @@ -12,7 +12,11 @@ function parseLocalDate(s: string): Date { throw new Error(`Invalid date format "${s}": expected YYYY-MM-DD`) } const [y, m, d] = s.split('-').map(Number) as [number, number, number] - return new Date(y, m - 1, d) + const parsed = new Date(y, m - 1, d) + if (parsed.getFullYear() !== y || parsed.getMonth() !== m - 1 || parsed.getDate() !== d) { + throw new Error(`Invalid date "${s}": month or day out of range`) + } + return parsed } export function parseDateRangeFlags(from: string | undefined, to: string | undefined): DateRange | null { diff --git a/tests/date-range-filter.test.ts b/tests/date-range-filter.test.ts index c4f94080..b4c1ddbe 100644 --- a/tests/date-range-filter.test.ts +++ b/tests/date-range-filter.test.ts @@ -48,6 +48,16 @@ describe('parseDateRangeFlags', () => { .toThrow('Invalid date format') }) + it('throws on month out of range', () => { + expect(() => parseDateRangeFlags('2026-13-01', undefined)) + .toThrow('month or day out of range') + }) + + it('throws on day out of range for month', () => { + expect(() => parseDateRangeFlags('2026-02-30', undefined)) + .toThrow('month or day out of range') + }) + it('same day is valid (start midnight, end 23:59:59)', () => { const range = parseDateRangeFlags('2026-04-10', '2026-04-10') expect(range).not.toBeNull()