Skip to content

Commit 3988cd5

Browse files
committed
fix(server): validate If-Range HTTP dates
1 parent e527192 commit 3988cd5

2 files changed

Lines changed: 29 additions & 1 deletion

File tree

packages/vinext/src/server/http-range.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,31 @@ export function ifRangeAllowsRange(
5555
return !trimmed.startsWith("W/") && !etag.startsWith("W/") && trimmed === etag;
5656
}
5757

58-
const timestamp = Date.parse(trimmed);
58+
const timestamp = parseHttpDate(trimmed);
5959
return Number.isFinite(timestamp) && Math.floor(mtimeMs / 1000) * 1000 <= timestamp;
6060
}
6161

62+
const HTTP_WEEKDAY = "(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)";
63+
const HTTP_WEEKDAY_LONG = "(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)";
64+
const HTTP_MONTH = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
65+
const IMF_FIXDATE_RE = new RegExp(
66+
`^${HTTP_WEEKDAY}, \\d{2} ${HTTP_MONTH} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT$`,
67+
);
68+
const RFC850_DATE_RE = new RegExp(
69+
`^${HTTP_WEEKDAY_LONG}, \\d{2}-${HTTP_MONTH}-\\d{2} \\d{2}:\\d{2}:\\d{2} GMT$`,
70+
);
71+
const ASCTIME_DATE_RE = new RegExp(
72+
`^${HTTP_WEEKDAY} ${HTTP_MONTH} (?: \\d|\\d{2}) \\d{2}:\\d{2}:\\d{2} \\d{4}$`,
73+
);
74+
75+
/** Parse only the three HTTP-date wire formats accepted by RFC 9110. */
76+
function parseHttpDate(value: string): number {
77+
if (!IMF_FIXDATE_RE.test(value) && !RFC850_DATE_RE.test(value) && !ASCTIME_DATE_RE.test(value)) {
78+
return Number.NaN;
79+
}
80+
return Date.parse(value);
81+
}
82+
6283
function parseDecimalInteger(value: string): number | null {
6384
if (!/^\d+$/.test(value)) return null;
6485
const parsed = Number(value);

tests/http-range.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,12 @@ describe("ifRangeAllowsRange", () => {
5454
it("rejects stale or invalid dates", () => {
5555
expect(ifRangeAllowsRange("Wed, 31 Dec 2025 23:59:59 GMT", '"asset"', mtimeMs)).toBe(false);
5656
expect(ifRangeAllowsRange("not-a-date", '"asset"', mtimeMs)).toBe(false);
57+
expect(ifRangeAllowsRange("12/31/2099", '"asset"', mtimeMs)).toBe(false);
58+
expect(ifRangeAllowsRange("2099-12-31", '"asset"', mtimeMs)).toBe(false);
59+
});
60+
61+
it("accepts obsolete HTTP-date formats required for recipients", () => {
62+
expect(ifRangeAllowsRange("Thursday, 01-Jan-26 00:00:01 GMT", '"asset"', mtimeMs)).toBe(true);
63+
expect(ifRangeAllowsRange("Thu Jan 1 00:00:01 2026", '"asset"', mtimeMs)).toBe(true);
5764
});
5865
});

0 commit comments

Comments
 (0)