From db537050186870463a70d0db65dd83117b3aa9c0 Mon Sep 17 00:00:00 2001 From: Alex Castillo Date: Tue, 20 Oct 2026 10:02:51 -0400 Subject: [PATCH] Validate the hour, minute, second and offset ranges of the time format The time regex used \d{1,2} for the hour, minute and second and \d\d:\d\d for the offset, so values such as 29:99:99 or 12:00:00+24:00 were accepted. Use 0-23 for the hour, 0-59 for the minute, 0-60 for the second (RFC 3339 leap second) and 00-23:00-59 for the offset. The optional parts and the one-digit forms the regex already accepted are unchanged. Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.txt | 1 + fastjsonschema/draft07.py | 6 +++--- tests/test_format.py | 24 ++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index b896386..d505096 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -6,6 +6,7 @@ * Added type annotations for public API and `py.typed` marker (PEP 561) * Added `message` to `JsonSchemaValuesException` joining messages of all collected errors * Fixed the `date-time` format accepting months, days and hours out of range (such as `2020-19-39T29:00:00Z`) and rejecting a leap second (`23:59:60Z`) +* Fixed the `time` format accepting hours, minutes, seconds and offsets out of range (such as `29:99:99`) === 2.22.2 (2026-08-15) diff --git a/fastjsonschema/draft07.py b/fastjsonschema/draft07.py index 4c6f303..c881d23 100644 --- a/fastjsonschema/draft07.py +++ b/fastjsonschema/draft07.py @@ -13,9 +13,9 @@ class CodeGeneratorDraft07(CodeGeneratorDraft06): 'relative-json-pointer': r'^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)\Z', #'regex': r'', 'time': ( - r'^(?P\d{1,2}):(?P\d{1,2})' - r'(?::(?P\d{1,2})(?:\.(?P\d{1,6}))?' - r'([zZ]|[+-]\d\d:\d\d)?)?\Z' + r'^(?P[01]?\d|2[0-3]):(?P[0-5]?\d)' + r'(?::(?P[0-5]?\d|60)(?:\.(?P\d{1,6}))?' + r'([zZ]|[+-](?:[01]\d|2[0-3]):[0-5]\d)?)?\Z' ), }) diff --git a/tests/test_format.py b/tests/test_format.py index 95bbc10..637d500 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -50,6 +50,30 @@ def test_hostname(asserter, value, expected): asserter({'type': 'string', 'format': 'hostname'}, value, expected) +exc = JsonSchemaValueException('data must be time', value='{data}', name='data', definition='{definition}', rule='format') +@pytest.mark.parametrize('value, expected', [ + ('', exc), + ('bla', exc), + ('12:00:00', '12:00:00'), + ('12:00:00Z', '12:00:00Z'), + ('00:00:00.5+01:30', '00:00:00.5+01:30'), + ('23:59:59-23:59', '23:59:59-23:59'), + ('23:59:60Z', '23:59:60Z'), + ('24:00:00Z', exc), + ('29:00:00Z', exc), + ('99:99:99', exc), + ('12:60:00Z', exc), + ('12:00:61Z', exc), + ('12:00:00+24:00', exc), + ('12:00:00+00:60', exc), +]) +def test_time(asserter, value, expected): + asserter({ + '$schema': 'http://json-schema.org/draft-07/schema', + 'format': 'time', + }, value, expected) + + exc = JsonSchemaValueException('data must be date', value='{data}', name='data', definition='{definition}', rule='format') @pytest.mark.parametrize('value, expected', [ ('', exc),