Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/validators/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def card_number(value: str, /):
(Literal[True]): If `value` is a valid generic card number.
(ValidationError): If `value` is an invalid generic card number.
"""
if not value:
if not value or not value.isascii():
# int() also accepts digits of other scripts, which are not card numbers
return False
try:
digits = list(map(int, value))
Expand Down
11 changes: 8 additions & 3 deletions src/validators/cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
from .utils import validator


def _is_number(text: str):
# str.isdecimal() also accepts digits of other scripts, e.g. "\u0663".
return text.isascii() and text.isdecimal()


def _validate_cron_item(item: str, min_val: int, max_val: int):
# An item may carry a step, e.g. "*/5", "1-30/2" or "5/10".
if "/" in item:
base, _, step = item.partition("/")
if not step.isdecimal() or int(step) < 1:
if not _is_number(step) or int(step) < 1:
return False
else:
base = item
Expand All @@ -18,12 +23,12 @@ def _validate_cron_item(item: str, min_val: int, max_val: int):

if "-" in base:
parts = base.split("-")
if len(parts) != 2 or not parts[0].isdecimal() or not parts[1].isdecimal():
if len(parts) != 2 or not _is_number(parts[0]) or not _is_number(parts[1]):
return False
start, end = int(parts[0]), int(parts[1])
return min_val <= start <= max_val and min_val <= end <= max_val and start <= end

if base.isdecimal():
if _is_number(base):
return min_val <= int(base) <= max_val

return False
Expand Down
3 changes: 3 additions & 0 deletions src/validators/uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ def uuid(value: Union[str, UUID], /):
return False
if isinstance(value, UUID):
return True
if isinstance(value, str) and not value.isascii():
# int(..., 16) inside UUID() also accepts digits of other scripts
return False
try:
return UUID(value) or re.match(
r"^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$", value
Expand Down
6 changes: 6 additions & 0 deletions tests/test_card.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,9 @@ def test_returns_true_on_valid_mir(value: str):
def test_returns_failed_on_valid_mir(value: str):
"""Test returns failed on invalid Mir card (other payment systems)."""
assert isinstance(mir(value), ValidationError)


@pytest.mark.parametrize("value", ["\u0664242424242424242", "424242424242424\u0662"])
def test_card_number_rejects_non_ascii_digits(value: str):
"""Test card_number does not accept digits of other scripts."""
assert isinstance(card_number(value), ValidationError)
14 changes: 14 additions & 0 deletions tests/test_cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,17 @@ def test_returns_true_on_valid_cron(value: str):
def test_returns_failed_validation_on_invalid_cron(value: str):
"""Test returns failed validation on invalid cron string."""
assert isinstance(cron(value), ValidationError)


@pytest.mark.parametrize(
"value",
[
"\u0665 * * * *",
"*/\u0665 * * * *",
"1-\u0665 * * * *",
"* \u0661\u0662 * * *",
],
)
def test_returns_failed_validation_on_non_ascii_digits_in_cron(value: str):
"""Test returns failed validation when a script other than ASCII supplies the digits."""
assert isinstance(cron(value), ValidationError)
13 changes: 13 additions & 0 deletions tests/test_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,16 @@ def test_returns_true_on_valid_uuid(value: Union[str, UUID]):
def test_returns_failed_validation_on_invalid_uuid(value: Union[str, UUID]):
"""Test returns failed validation on invalid uuid."""
assert isinstance(uuid(value), ValidationError)


@pytest.mark.parametrize(
("value",),
[
("\u0662bc1c94f-0deb-43e9-92a1-4775189ec9f8",),
("2bc1c94f-0deb-43e9-92a1-4775189ec9f\u0668",),
("\u0662bc1c94f0deb43e992a14775189ec9f8",),
],
)
def test_returns_failed_validation_on_non_ascii_digits_in_uuid(value: str):
"""Test returns failed validation when a script other than ASCII supplies the digits."""
assert isinstance(uuid(value), ValidationError)