From 2d0074a8c9d7765ea392664239424d059997c412 Mon Sep 17 00:00:00 2001 From: Triskell Studio Date: Wed, 24 Jun 2026 15:20:52 +0200 Subject: [PATCH 1/2] fix(i18n/fr): reject comma as gender/Corsica suffix in fr_ssn The SSN regex used the character classes [1,2] and 2[A,B], which also match a literal comma. fr_ssn(', 84 12 76 451 089') was therefore accepted as valid. Drop the commas ([12] and [AB]); valid SSNs are unaffected. --- src/validators/i18n/fr.py | 4 ++-- tests/i18n/test_fr.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/validators/i18n/fr.py b/src/validators/i18n/fr.py index cba93bc1..10a5a2b1 100644 --- a/src/validators/i18n/fr.py +++ b/src/validators/i18n/fr.py @@ -13,10 +13,10 @@ def _ssn_pattern(): """SSN Pattern.""" return re.compile( - r"^([1,2])" # gender (1=M, 2=F) + r"^([12])" # gender (1=M, 2=F) r"\s(\d{2})" # year of birth r"\s(0[1-9]|1[0-2])" # month of birth - r"\s(\d{2,3}|2[A,B])" # department of birth + r"\s(\d{2,3}|2[AB])" # department of birth r"\s(\d{2,3})" # town of birth r"\s(\d{3})" # registration number r"(?:\s(\d{2}))?$", # control key (may or may not be provided) diff --git a/tests/i18n/test_fr.py b/tests/i18n/test_fr.py index 1e648222..30c1588a 100644 --- a/tests/i18n/test_fr.py +++ b/tests/i18n/test_fr.py @@ -34,6 +34,7 @@ def test_returns_true_on_valid_ssn(value: str): (None,), ("",), ("3 84 12 76 451 089 46",), # wrong gender number + (", 84 12 76 451 089",), # comma is not a valid gender (regex char-class bug) ("1 84 12 76 451 089 47",), # wrong control key ("1 84 00 76 451 089",), # invalid month ("1 84 13 76 451 089",), # invalid month From f0eae3d7708e7390d5584e9140dd8d6d63994947 Mon Sep 17 00:00:00 2001 From: Triskell Studio Date: Wed, 24 Jun 2026 15:24:27 +0200 Subject: [PATCH 2/2] fix(i18n/ind): anchor ind_pan so trailing characters are rejected ind_pan used re.match(r"[A-Z]{5}\d{4}[A-Z]{1}", ...) with no trailing $, so any string starting with a valid PAN was accepted -- e.g. ind_pan('ABCDE9999KEXTRA') returned a match. Anchor the pattern with ^...$, matching the ind_aadhar style just above it. --- src/validators/i18n/ind.py | 2 +- tests/i18n/test_ind.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/validators/i18n/ind.py b/src/validators/i18n/ind.py index c1d49400..b818a87a 100644 --- a/src/validators/i18n/ind.py +++ b/src/validators/i18n/ind.py @@ -44,4 +44,4 @@ def ind_pan(value: str): (Literal[True]): If `value` is a valid PAN card number. (ValidationError): If `value` is an invalid PAN card number. """ - return re.match(r"[A-Z]{5}\d{4}[A-Z]{1}", value) + return re.match(r"^[A-Z]{5}\d{4}[A-Z]{1}$", value) diff --git a/tests/i18n/test_ind.py b/tests/i18n/test_ind.py index 1164f131..ad2fea96 100644 --- a/tests/i18n/test_ind.py +++ b/tests/i18n/test_ind.py @@ -26,7 +26,9 @@ def test_returns_true_on_valid_ind_pan(value: str): assert ind_pan(value) -@pytest.mark.parametrize("value", ["ABC5d7896B", "417598346012", "AaaPL1234C"]) +@pytest.mark.parametrize( + "value", ["ABC5d7896B", "417598346012", "AaaPL1234C", "ABCDE9999KEXTRA"] +) def test_returns_failed_validation_on_invalid_ind_pan(value: str): """Test returns failed validation on invalid ind pan.""" assert isinstance(ind_pan(value), ValidationError)