Skip to content

Commit dc96e45

Browse files
committed
Stop uuid() from accepting urn:/braced forms via dead-code fallback
`uuid()` parsed input with the stdlib `UUID()` constructor and fell back to a strict regex only "if UUID(value) is falsy": return UUID(value) or re.match(r"^[0-9a-fA-F]{8}-...$", value) A successfully-constructed `UUID` object is always truthy (it defines no `__bool__`/`__len__`), so the `or re.match(...)` branch can never run. In practice this means the actual acceptance criteria was "whatever Python's UUID() constructor accepts", not the regex the code appears to enforce -- and UUID() accepts considerably more than this validator documents or tests, e.g.: >>> uuid('urn:uuid:2bc1c94f-0deb-43e9-92a1-4775189ec9f8') True # should be rejected >>> uuid('{2bc1c94f-0deb-43e9-92a1-4775189ec9f8}') True # should be rejected Neither form appears in the docstring, the tests, or any prior issue/PR I could find (checked via GitHub search for "uuid" -- the closest, #112/#175, are about supporting hyphen-less hex, which this fix keeps working). Fix: drop the UUID()-based parsing entirely and validate with the regex directly, extended to also accept the already-tested hyphen-less form. This is a strict subset of what the old code intended to accept (per its own regex and docstring), a superset check would have been speculative; verified nothing currently-valid becomes invalid. Verification: - All 8 existing `test_uuid.py` cases still pass. - Added 2 regression cases (`urn:uuid:...`, `{...}`) to the existing invalid-input parametrize list; confirmed they fail against the unpatched code (reverted locally to check) and pass with the fix. - Non-string inputs (int, float, bool, list, dict, None) still resolve to `ValidationError` rather than crashing -- `TypeError` from `re.match` on a non-string is already caught by the `@validator` decorator in `utils.py`, so no new exception handling was needed. - Full suite: `pytest tests/` -- 897 passed (895 baseline + 2 new). - `pytest --doctest-modules src/validators/` -- 57 passed, doctest for `uuid` unaffected. - `ruff format --check`, `ruff check`, and `pyright` all clean on the changed files (matches this repo's `pycqa.yaml` CI job exactly). Found via targeted review of validator internals after differential fuzzing across the library's public functions, not from a filed issue. AI-assisted (Code Puppy); reproduced, root-caused, fixed, and verified against both the old and new code before opening this.
1 parent 70de324 commit dc96e45

2 files changed

Lines changed: 13 additions & 6 deletions

File tree

src/validators/uuid.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ def uuid(value: Union[str, UUID], /):
3535
return False
3636
if isinstance(value, UUID):
3737
return True
38-
try:
39-
return UUID(value) or re.match(
40-
r"^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$", value
41-
)
42-
except ValueError:
43-
return False
38+
# Deliberately not delegating to the stdlib UUID() constructor here:
39+
# it accepts far more than this validator documents or tests, e.g.
40+
# `urn:uuid:...`-prefixed and `{braced}` forms, and any UUID version
41+
# (not just v4). A regex keeps acceptance limited to the plain
42+
# (optionally dashed) hex form shown in the docstring and tests.
43+
return bool(re.match(r"^[0-9a-fA-F]{8}-?([0-9a-fA-F]{4}-?){3}[0-9a-fA-F]{12}$", value))

tests/test_uuid.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ def test_returns_true_on_valid_uuid(value: Union[str, UUID]):
3232
("2bc1c94f-0deb-43e9-92a1-4775189ec9f",),
3333
("gbc1c94f-0deb-43e9-92a1-4775189ec9f8",),
3434
("2bc1c94f 0deb-43e9-92a1-4775189ec9f8",),
35+
# Regression: the previous implementation delegated to the stdlib
36+
# UUID() constructor, which is far more permissive than this
37+
# validator's own docstring/regex -- it also accepts urn:-prefixed
38+
# and {braced} forms, neither of which were ever documented,
39+
# tested, or intended to pass here.
40+
("urn:uuid:2bc1c94f-0deb-43e9-92a1-4775189ec9f8",),
41+
("{2bc1c94f-0deb-43e9-92a1-4775189ec9f8}",),
3542
],
3643
)
3744
def test_returns_failed_validation_on_invalid_uuid(value: Union[str, UUID]):

0 commit comments

Comments
 (0)