From b970bf22ed5b57d0b5d5c1834b135123c0bcdb25 Mon Sep 17 00:00:00 2001 From: John Kearney Date: Sun, 14 Jun 2026 18:19:58 -0500 Subject: [PATCH] fix(analyzer): detect Mastercard 2-series and 18-19 digit credit cards The CreditCardRecognizer PAN regex only matched leading digits 4/5/6/1/3 and a 13-16 digit window, so two families of real, Luhn-valid cards produced no CREDIT_CARD result and leaked unredacted: - Mastercard 2-series (BIN range 2221-2720, issued since 2017) - 18-19 digit PANs (ISO/IEC 7812 allows up to 19, e.g. UnionPay/Maestro) Widen the regex to cover these ranges and raise the length ceiling to 19. Luhn (validate_result) still gates every match, so non-card numbers are not flagged. Existing detections and Unix-timestamp/Luhn-invalid rejections are unchanged. --- CHANGELOG.md | 1 + .../generic/credit_card_recognizer.py | 9 ++++++++- .../tests/test_credit_card_recognizer.py | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f6e29ec8..e02d6f246e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ All notable changes to this project will be documented in this file. - Added `supported_entity` parameter to `PhoneRecognizer`. Previously, this recognizer hard-coded `["PHONE_NUMBER"]` as the only possible supported entity. #### Fixed +- Fixed a false-negative in `CreditCardRecognizer` where real, Luhn-valid cards were passing as clean (no `CREDIT_CARD` result) and leaking unredacted. The PAN regex now also matches Mastercard 2-series cards (BIN range 2221-2720, the first four digits; issued since 2017) and 18-19 digit PANs (ISO/IEC 7812 allows up to 19 digits, e.g. UnionPay/Maestro), in addition to the existing ranges. Luhn (`validate_result`) still gates every match, so the widened range does not introduce false positives on non-card numbers. - Fixed an issue where the CreditCardRecognizer regex could incorrectly identify 13-digit Unix timestamps as credit card numbers. Validated that 13 digit numbers that start with `1` and have no separators (e.g. `1748503543012`) are not flagged as credit cards. - Enhance NlpEngineProvider with validation methods for NLP engines, configuration, and conf file path. - Fixed `PhoneRecognizer._get_recognizer_result` to use the constructor-provided `supported_entity` instead of the hard-coded `"PHONE_NUMBER"` string, making the `supported_entity` parameter from PR #2014 fully functional. diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/credit_card_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/credit_card_recognizer.py index 912bf8d197..eeff7c617b 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/credit_card_recognizer.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/generic/credit_card_recognizer.py @@ -17,9 +17,16 @@ class CreditCardRecognizer(PatternRecognizer): """ PATTERNS = [ + # Prefix branches cover the major issuer ranges, including the + # Mastercard 2-series BINs (first four digits 2221-2720, live since + # 2017) via the 2(22[1-9]|2[3-9]\d|[3-6]\d\d|7[01]\d|720) alternative. + # The final group accepts up to 7 digits so the pattern spans 13-19 + # digit PANs (ISO/IEC 7812 allows up to 19, e.g. UnionPay/Maestro). + # Luhn (validate_result) still gates every match, so widening the + # range does not flag non-card numbers. Pattern( "All Credit Cards (weak)", - r"\b(?!1\d{12}(?!\d))((4\d{3})|(5[0-5]\d{2})|(6\d{3})|(1\d{3})|(3\d{3}))[- ]?(\d{3,4})[- ]?(\d{3,4})[- ]?(\d{3,5})\b", # noqa: E501 + r"\b(?!1\d{12}(?!\d))((4\d{3})|(5[0-5]\d{2})|(2(22[1-9]|2[3-9]\d|[3-6]\d\d|7[01]\d|720))|(6\d{3})|(1\d{3})|(3\d{3}))[- ]?(\d{3,4})[- ]?(\d{3,4})[- ]?(\d{3,7})\b", # noqa: E501 0.3, ), ] diff --git a/presidio-analyzer/tests/test_credit_card_recognizer.py b/presidio-analyzer/tests/test_credit_card_recognizer.py index acdf27a7b5..63c5ff2b68 100644 --- a/presidio-analyzer/tests/test_credit_card_recognizer.py +++ b/presidio-analyzer/tests/test_credit_card_recognizer.py @@ -39,6 +39,17 @@ def entities(): ("4111111111111111", 1, (), ((0, 16),),), ("4917300800000000", 1, (), ((0, 16),),), ("4484070000000000", 1, (1.0,), ((0, 16),),), + # Mastercard 2-series (BINs 2221-2720, live since 2017). + ("2221000000000009", 1, (1.0,), ((0, 16),),), + ("2720990000000007", 1, (1.0,), ((0, 16),),), + ("2223000048400011", 1, (1.0,), ((0, 16),),), + ("my credit card: 2221000000000009", 1, (), ((16, 32),),), + # 18-19 digit PANs (ISO/IEC 7812, e.g. UnionPay/Maestro). + ("4109906958483040118", 1, (1.0,), ((0, 19),),), + ("6298036494205552661", 1, (1.0,), ((0, 19),),), + ("675919345145061238", 1, (1.0,), ((0, 18),),), + # Luhn-invalid 2-series PAN is still rejected. + ("2221000000000001", 0, (), (),), ("4012-8888-8888-1882", 0, (), (),), ("my credit card number is 4012-8888-8888-1882", 0, (), (),), ("36168002586008", 0, (), (),),