fix: validate calendar date in fixed-offset due_at
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCrwHHnGCB5XH968Nxokqw
This commit is contained in:
@@ -10,7 +10,7 @@ final class DueAtParser
|
||||
{
|
||||
private const DATE = '/^(\d{4})-(\d{2})-(\d{2})$/';
|
||||
private const LOCAL_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/';
|
||||
private const FIXED_DATETIME = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(Z|[+-]\d{2}:\d{2})$/';
|
||||
private const FIXED_DATETIME = '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(Z|[+-]\d{2}:\d{2})$/';
|
||||
|
||||
public static function parse(string $raw): ParsedDue
|
||||
{
|
||||
@@ -30,7 +30,10 @@ final class DueAtParser
|
||||
);
|
||||
}
|
||||
|
||||
if (preg_match(self::FIXED_DATETIME, $raw)) {
|
||||
if (preg_match(self::FIXED_DATETIME, $raw, $m)) {
|
||||
self::assertDate((int) $m[1], (int) $m[2], (int) $m[3], $raw);
|
||||
self::assertTime((int) $m[4], (int) $m[5], (int) ($m[6] ?? 0), $raw);
|
||||
|
||||
try {
|
||||
return ParsedDue::fixed(CarbonImmutable::parse($raw));
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
@@ -43,7 +43,7 @@ class DueAtParserTest extends TestCase
|
||||
|
||||
public function test_garbage_is_rejected(): void
|
||||
{
|
||||
foreach (['', 'tomorrow', '2026-13-01', '14:00', '2026-09-10 14:00', '2026-09-10T25:00'] as $bad) {
|
||||
foreach (['', 'tomorrow', '2026-13-01', '14:00', '2026-09-10 14:00', '2026-09-10T25:00', '2026-02-30T08:15:00+02:00', '2026-09-10T25:00Z'] as $bad) {
|
||||
try {
|
||||
DueAtParser::parse($bad);
|
||||
$this->fail("accepted: $bad");
|
||||
|
||||
Reference in New Issue
Block a user