


























@@ -180,9 +180,41 @@ const parseDateParts = (
180180if (!Number.isFinite(year) || !Number.isFinite(monthIndex) || !Number.isFinite(day)) {
181181return undefined;
182182}
183+// The regex only checks shape; Date.* silently rolls impossible calendar dates over
184+// (e.g. 2026-02-30 -> 2026-03-02), so a typo'd day would return usage for the wrong day.
185+// Reject parts that don't round-trip through a UTC probe (also catches the JS 2-digit-year remap).
186+const probe = new Date(Date.UTC(year, monthIndex, day));
187+if (
188+probe.getUTCFullYear() !== year ||
189+probe.getUTCMonth() !== monthIndex ||
190+probe.getUTCDate() !== day
191+) {
192+return undefined;
193+}
183194return { year, monthIndex, day };
184195};
185196197+// usage.cost / sessions.usage accept optional startDate/endDate. parseDateParts returns
198+// undefined for both absent and invalid input, so an explicitly supplied but unparseable
199+// date (bad format or impossible calendar date like 2026-02-30) would otherwise silently
200+// fall through to the default range and return a successful response for an unrelated range.
201+// Return the offending field so handlers can reject it instead of querying the wrong window.
202+const findInvalidExplicitDate = (params: {
203+startDate?: unknown;
204+endDate?: unknown;
205+}): "startDate" | "endDate" | undefined => {
206+for (const field of ["startDate", "endDate"] as const) {
207+const raw = params[field];
208+if (raw === undefined || raw === null || (typeof raw === "string" && raw.trim() === "")) {
209+continue;
210+}
211+if (parseDateParts(raw) === undefined) {
212+return field;
213+}
214+}
215+return undefined;
216+};
217+186218/**
187219 * Parse a UTC offset string in the format UTC+H, UTC-H, UTC+HH, UTC-HH, UTC+H:MM, UTC-HH:MM.
188220 * Returns the UTC offset in minutes (east-positive), or undefined if invalid.
@@ -902,6 +934,7 @@ function mergeUsageCacheStatus(
902934// Exposed for unit tests (kept as a single export to avoid widening the public API surface).
903935export const testApi = {
904936 parseDateParts,
937+ findInvalidExplicitDate,
905938 parseUtcOffsetToMinutes,
906939 resolveDateInterpretation,
907940 parseDateToMs,
@@ -922,6 +955,21 @@ export const usageHandlers: GatewayRequestHandlers = {
922955respond(true, summary, undefined);
923956},
924957"usage.cost": async ({ respond, params, context }) => {
958+const invalidDate = findInvalidExplicitDate({
959+startDate: params?.startDate,
960+endDate: params?.endDate,
961+});
962+if (invalidDate) {
963+respond(
964+false,
965+undefined,
966+errorShape(
967+ErrorCodes.INVALID_REQUEST,
968+`invalid ${invalidDate}: expected a valid YYYY-MM-DD calendar date`,
969+),
970+);
971+return;
972+}
925973const config = context.getRuntimeConfig();
926974const { startMs, endMs } = parseDateRange({
927975startDate: params?.startDate,
@@ -956,6 +1004,18 @@ export const usageHandlers: GatewayRequestHandlers = {
9561004}
95710059581006const p = params;
1007+const invalidDate = findInvalidExplicitDate({ startDate: p.startDate, endDate: p.endDate });
1008+if (invalidDate) {
1009+respond(
1010+false,
1011+undefined,
1012+errorShape(
1013+ErrorCodes.INVALID_REQUEST,
1014+`invalid ${invalidDate}: expected a valid YYYY-MM-DD calendar date`,
1015+),
1016+);
1017+return;
1018+}
9591019const config = context.getRuntimeConfig();
9601020const { startMs, endMs } = parseDateRange({
9611021startDate: p.startDate,
@@ -1335,7 +1395,8 @@ export const usageHandlers: GatewayRequestHandlers = {
13351395}
13361396const hiddenSession = hiddenSessions[hiddenIndex];
13371397const merged = mergedEntries[hiddenSession.entryIndex];
1338-const usage = usageByEntryIndex[hiddenSession.entryIndex] ?? createEmptySessionCostSummary();
1398+const usage =
1399+usageByEntryIndex[hiddenSession.entryIndex] ?? createEmptySessionCostSummary();
13391400usage.sessionId = merged.sessionId;
13401401usage.sessionFile = merged.sessionFile;
13411402mergeSessionUsageInto(usage, summary);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。