Skip to content

Commit ad841b7

Browse files
authored
Merge pull request #1 from Anusha0501/fix/1558-regex-format-uncaught-exceptions
Report regex compile failures as invalid format, not crashes.
2 parents 865c27f + 4792e3e commit ad841b7

3 files changed

Lines changed: 37 additions & 1 deletion

File tree

‎CHANGELOG.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
v4.27.0
22
=======
33

4+
* The ``regex`` format checker now reports conflicting inline flags, deeply nested patterns, and oversized repeat counts as invalid instances instead of letting ``ValueError``, ``RecursionError``, or ``OverflowError`` escape from ``re.compile`` (#1526, #1538, #1558).
45
* Improve ``best_match`` for ``anyOf`` / ``oneOf`` errors: the most relevant error is now found within each subschema separately before the deepest of those is picked (#1257, #1300).
56
In particular an applicator with a single subschema now produces the same best match as the bare subschema would.
67
``relevance`` no longer considers an error's position amongst its siblings.

‎jsonschema/_format.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,12 @@ def is_time(instance: object) -> bool:
413413
return is_datetime("1970-01-01T" + instance)
414414

415415

416-
@_checks_drafts(name="regex", raises=re.error)
416+
# re.compile can raise more than re.error (conflicting flags, deep
417+
# nesting, oversized repeats). Treat those as an invalid regex instance.
418+
@_checks_drafts(
419+
name="regex",
420+
raises=(re.error, ValueError, RecursionError, OverflowError),
421+
)
417422
def is_regex(instance: object) -> bool:
418423
if not isinstance(instance, str):
419424
return True

‎jsonschema/tests/test_format.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,33 @@ def test_repr(self):
8989
repr(checker),
9090
"<FormatChecker checkers=['bar', 'baz', 'foo']>",
9191
)
92+
93+
def test_regex_format_rejects_unterminated_pattern(self):
94+
checker = FormatChecker()
95+
with self.assertRaises(FormatError):
96+
checker.check("[unterminated", "regex")
97+
self.assertFalse(checker.conforms("[unterminated", "regex"))
98+
99+
def test_regex_format_rejects_conflicting_inline_flags(self):
100+
checker = FormatChecker()
101+
for pattern in ("(?u)(?a)", "(?a)(?u)"):
102+
with self.assertRaises(FormatError):
103+
checker.check(pattern, "regex")
104+
self.assertFalse(checker.conforms(pattern, "regex"))
105+
106+
def test_regex_format_rejects_deeply_nested_pattern(self):
107+
checker = FormatChecker()
108+
pattern = "(" * 500
109+
with self.assertRaises(FormatError):
110+
checker.check(pattern, "regex")
111+
self.assertFalse(checker.conforms(pattern, "regex"))
112+
113+
def test_regex_format_rejects_overflowing_repeat_count(self):
114+
checker = FormatChecker()
115+
with self.assertRaises(FormatError):
116+
checker.check("a{99999999999999}", "regex")
117+
self.assertFalse(checker.conforms("a{99999999999999}", "regex"))
118+
119+
def test_regex_format_accepts_valid_pattern(self):
120+
checker = FormatChecker()
121+
self.assertTrue(checker.conforms(r"^\d+$", "regex"))

0 commit comments

Comments
 (0)