diff --git a/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.spec.ts b/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.spec.ts index 071d38324..dbd4417f1 100644 --- a/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.spec.ts +++ b/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.spec.ts @@ -299,6 +299,14 @@ describe('parseStalkerDate', () => { expect(parseStalkerDate('not a date')).toBeUndefined(); }); + it('rejects out-of-range calendar components instead of normalizing them', () => { + // new Date(2026, -1, 0) silently becomes Nov 30, 2025 — a + // placeholder must not fabricate an expiry. + expect(parseStalkerDate('2026-00-00')).toBeUndefined(); + expect(parseStalkerDate('2026-02-30')).toBeUndefined(); + expect(parseStalkerDate('2026-13-01')).toBeUndefined(); + }); + it('rejects negative unlimited-expiry sentinels instead of parsing them as dates', () => { // V8 reads Date.parse('-1') as January 1, 2001 — an unlimited // account must not render as expired. diff --git a/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.ts b/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.ts index 24bdc8179..aa6afb855 100644 --- a/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.ts +++ b/libs/portal/stalker/data-access/src/lib/stalker-account-info.service.ts @@ -238,14 +238,23 @@ export function parseStalkerDate( // UTC and moves the days-left boundary. Build it in local time instead. const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(text); if (dateOnly) { - const [, year, month, day] = dateOnly; - const local = new Date( - Number(year), - Number(month) - 1, - Number(day) - ).getTime(); - return Number.isFinite(local) && local > 0 - ? Math.round(local / 1000) + const year = Number(dateOnly[1]); + const month = Number(dateOnly[2]); + const day = Number(dateOnly[3]); + const local = new Date(year, month - 1, day); + // Round-trip check: the multi-argument constructor normalizes + // out-of-range components ('2026-00-00' → Nov 30, 2025), which + // would fabricate an expiry from a placeholder. + if ( + local.getFullYear() !== year || + local.getMonth() !== month - 1 || + local.getDate() !== day + ) { + return undefined; + } + const timestamp = local.getTime(); + return Number.isFinite(timestamp) && timestamp > 0 + ? Math.round(timestamp / 1000) : undefined; }