From 9ad9204588316cb9a4a6eb2b434001a227cebb51 Mon Sep 17 00:00:00 2001 From: Randi Ratnayake Date: Wed, 22 Jul 2026 14:23:07 +1000 Subject: [PATCH 1/2] Add AU bank details recognizer with tests --- CHANGELOG.md | 3 + docs/supported_entities.md | 1 + .../conf/default_recognizers.yaml | 7 ++ .../predefined_recognizers/__init__.py | 4 + .../country_specific/australia/__init__.py | 2 + .../australia/au_bank_details_recognizer.py | 88 +++++++++++++++++++ .../tests/test_au_bank_details_recognizer.py | 52 +++++++++++ 7 files changed, 157 insertions(+) create mode 100644 presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py create mode 100644 presidio-analyzer/tests/test_au_bank_details_recognizer.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3c11d975..0ea751e849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file. ## [unreleased] ### Analyzer +#### Added +- Australian bank details recognizer (`AU_BANK_DETAILS`) for detecting BSB + account-number pairs (e.g., `062-000 12345678`), disabled by default. + #### Fixed - `PhoneRecognizer.DEFAULT_SUPPORTED_REGIONS` used `"UK"`, which is not a valid `phonenumbers` (libphonenumber) region code — region codes are ISO 3166-1 alpha-2, where the United Kingdom is `"GB"`. The `"UK"` entry was a no-op, so UK numbers in national/local format (e.g. `020 7946 0958`) were never detected by default; only international-format `+44 …` numbers matched, because they carry the country code and match under any region. Replaced `"UK"` with `"GB"`. - Language model recognizers (`BasicLangExtractRecognizer`, `AzureOpenAILangExtractRecognizer`) configured in a recognizer registry YAML now honour `config_path` (and other recognizer-specific kwargs). Previously these entries were validated by the strict `PredefinedRecognizerConfig` schema, which has no `config_path` field and does not allow extra keys, so `config_path` was silently dropped and the recognizer fell back to its bundled default model configuration. Added a `LangExtractRecognizerConfig` model (`extra="allow"`) and registered both recognizer class names in `CONFIG_MODEL_MAP`. diff --git a/docs/supported_entities.md b/docs/supported_entities.md index e04f770f0f..4fb67dc6f0 100644 --- a/docs/supported_entities.md +++ b/docs/supported_entities.md @@ -88,6 +88,7 @@ For more information, refer to the [adding new recognizers documentation](analyz |AU_ACN| An Australian Company Number is a unique nine-digit number issued by the Australian Securities and Investments Commission to every company registered under the Commonwealth Corporations Act 2001 as an identifier. | Pattern match, context, and checksum | |AU_TFN| The tax file number (TFN) is a unique identifier issued by the Australian Taxation Office to each taxpaying entity | Pattern match, context, and checksum | |AU_MEDICARE| Medicare number is a unique identifier issued by Australian Government that enables the cardholder to receive a rebates of medical expenses under Australia's Medicare system| Pattern match, context, and checksum | +|AU_BANK_DETAILS| Australian bank details where both BSB (6 digits) and account number (6-10 digits) are present together (for example `062-000 12345678`).| Pattern match, context, and structural validation | ### India | FieldType | Description |Detection Method| diff --git a/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml b/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml index a82c62ddaa..c4d71e35ba 100644 --- a/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml +++ b/presidio-analyzer/presidio_analyzer/conf/default_recognizers.yaml @@ -167,6 +167,13 @@ recognizers: enabled: false country_code: au + - name: AuBankDetailsRecognizer + supported_languages: + - en + type: predefined + enabled: false + country_code: au + - name: NgNinRecognizer supported_languages: - en diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py index 3715da7c30..1e66738ece 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/__init__.py @@ -7,6 +7,9 @@ from .country_specific.australia.au_abn_recognizer import AuAbnRecognizer from .country_specific.australia.au_acn_recognizer import AuAcnRecognizer +from .country_specific.australia.au_bank_details_recognizer import ( + AuBankDetailsRecognizer, +) from .country_specific.australia.au_medicare_recognizer import AuMedicareRecognizer from .country_specific.australia.au_tfn_recognizer import AuTfnRecognizer @@ -211,6 +214,7 @@ "NLP_RECOGNIZERS", "AuAbnRecognizer", "AuAcnRecognizer", + "AuBankDetailsRecognizer", "AuTfnRecognizer", "AuMedicareRecognizer", "TransformersRecognizer", diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/__init__.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/__init__.py index 3821c180d3..f4999bcdd4 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/__init__.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/__init__.py @@ -2,12 +2,14 @@ from .au_abn_recognizer import AuAbnRecognizer from .au_acn_recognizer import AuAcnRecognizer +from .au_bank_details_recognizer import AuBankDetailsRecognizer from .au_medicare_recognizer import AuMedicareRecognizer from .au_tfn_recognizer import AuTfnRecognizer __all__ = [ "AuAbnRecognizer", "AuAcnRecognizer", + "AuBankDetailsRecognizer", "AuMedicareRecognizer", "AuTfnRecognizer", ] diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py new file mode 100644 index 0000000000..0e75a8fb4a --- /dev/null +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py @@ -0,0 +1,88 @@ +import re +from typing import List, Optional + +from presidio_analyzer import Pattern, PatternRecognizer + + +class AuBankDetailsRecognizer(PatternRecognizer): + """ + Recognizes Australian bank details (BSB and account number). + + This recognizer detects Australian bank details where both: + - a BSB (Bank State Branch) number, and + - an account number + are present together in the same token sequence. + + Typical formats include: + - BSB 062-000 Account 12345678 + - 062-000 12345678 + + Reference: + - https://en.wikipedia.org/wiki/Bank_state_branch + + :param patterns: List of patterns to be used by this recognizer + :param context: List of context words to increase confidence in detection + :param supported_language: Language this recognizer supports + :param supported_entity: The entity this recognizer can detect + :param name: Name of recognizer + """ + + COUNTRY_CODE = "au" + + PATTERNS = [ + Pattern( + "AU bank details (High)", + r"\b(?:bsb)\s*[:\-]?\s*\d{3}[- ]?\d{3}\s*(?:[,;]\s*)?(?:account(?:\s*number)?|acct|a/c|acc(?:ount)?)\s*[:\-]?\s*\d{6,10}\b", # noqa: E501 + 0.5, + ), + Pattern( + "AU bank details (Medium)", + r"\b\d{3}[- ]?\d{3}\s+\d{6,10}\b", + 0.2, + ), + ] + + CONTEXT = [ + "bsb", + "bank", + "account", + "account number", + "bank account", + ] + + def __init__( + self, + patterns: Optional[List[Pattern]] = None, + context: Optional[List[str]] = None, + supported_language: str = "en", + supported_entity: str = "AU_BANK_DETAILS", + name: Optional[str] = None, + ): + patterns = patterns if patterns else self.PATTERNS + context = context if context else self.CONTEXT + super().__init__( + supported_entity=supported_entity, + patterns=patterns, + context=context, + supported_language=supported_language, + name=name, + ) + + def validate_result(self, pattern_text: str) -> bool: + """ + Validate structural constraints for AU bank details. + + :param pattern_text: Matched text from the regex engine. + :return: True when text contains a plausible BSB + account pair. + """ + digits = re.sub(r"\D", "", pattern_text) + if len(digits) < 12 or len(digits) > 16: + return False + + bsb = digits[:6] + account = digits[6:] + if len(account) < 6 or len(account) > 10: + return False + + # Basic plausibility guard to reduce obvious false positives. + return bsb != "000000" and account != "0" * len(account) \ No newline at end of file diff --git a/presidio-analyzer/tests/test_au_bank_details_recognizer.py b/presidio-analyzer/tests/test_au_bank_details_recognizer.py new file mode 100644 index 0000000000..629bb919b4 --- /dev/null +++ b/presidio-analyzer/tests/test_au_bank_details_recognizer.py @@ -0,0 +1,52 @@ +import pytest + +from tests import assert_result_within_score_range +from presidio_analyzer.predefined_recognizers import AuBankDetailsRecognizer + + +@pytest.fixture(scope="module") +def recognizer(): + return AuBankDetailsRecognizer() + + +@pytest.fixture(scope="module") +def entities(): + return ["AU_BANK_DETAILS"] + + +@pytest.mark.parametrize( + "text, expected_len, expected_positions, expected_score_ranges", + [ + # Valid BSB + account formats. + ("062-000 12345678", 1, ((0, 16),), ((1.0, 1.0),),), + ("062 000 123456", 1, ((0, 14),), ((1.0, 1.0),),), + ("BSB: 062-000 Account: 12345678", 1, ((0, 30),), ((1.0, 1.0),),), + # Invalid account length. + ("062-000 12345", 0, (), (),), + # Invalid structural values. + ("000-000 12345678", 0, (), (),), + ("062-000 000000", 0, (), (),), + # Invalid formats. + ("06200012345678", 0, (), (),), + ("123 456\n789012", 0, (), (),), + ], +) +def test_when_all_au_bank_details_then_succeed( + text, + expected_len, + expected_positions, + expected_score_ranges, + recognizer, + entities, + max_score, +): + results = recognizer.analyze(text, entities) + assert len(results) == expected_len + for res, (st_pos, fn_pos), (st_score, fn_score) in zip( + results, expected_positions, expected_score_ranges + ): + if fn_score == "max": + fn_score = max_score + assert_result_within_score_range( + res, entities[0], st_pos, fn_pos, st_score, fn_score + ) \ No newline at end of file From 05b501af787b329ff0b0f0a9a2beec2911be8d02 Mon Sep 17 00:00:00 2001 From: Randi Ratnayake Date: Wed, 22 Jul 2026 14:43:17 +1000 Subject: [PATCH 2/2] Feedback Fixes --- .../australia/au_bank_details_recognizer.py | 6 +++--- presidio-analyzer/tests/test_au_bank_details_recognizer.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py index 0e75a8fb4a..7fd9bac51b 100644 --- a/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py +++ b/presidio-analyzer/presidio_analyzer/predefined_recognizers/country_specific/australia/au_bank_details_recognizer.py @@ -6,7 +6,7 @@ class AuBankDetailsRecognizer(PatternRecognizer): """ - Recognizes Australian bank details (BSB and account number). + Recognises Australian bank details (BSB and account number). This recognizer detects Australian bank details where both: - a BSB (Bank State Branch) number, and @@ -32,12 +32,12 @@ class AuBankDetailsRecognizer(PatternRecognizer): PATTERNS = [ Pattern( "AU bank details (High)", - r"\b(?:bsb)\s*[:\-]?\s*\d{3}[- ]?\d{3}\s*(?:[,;]\s*)?(?:account(?:\s*number)?|acct|a/c|acc(?:ount)?)\s*[:\-]?\s*\d{6,10}\b", # noqa: E501 + r"\b(?:bsb)[^\S\r\n]*[:\-]?[^\S\r\n]*\d{3}[- ]?\d{3}[^\S\r\n]*(?:[,;][^\S\r\n]*)?(?:account(?:[^\S\r\n]*number)?|acct|a/c|acc(?:ount)?)[^\S\r\n]*[:\-]?[^\S\r\n]*\d{6,10}\b", # noqa: E501 0.5, ), Pattern( "AU bank details (Medium)", - r"\b\d{3}[- ]?\d{3}\s+\d{6,10}\b", + r"\b\d{3}[- ]?\d{3}[^\S\r\n]+\d{6,10}\b", 0.2, ), ] diff --git a/presidio-analyzer/tests/test_au_bank_details_recognizer.py b/presidio-analyzer/tests/test_au_bank_details_recognizer.py index 629bb919b4..84e0bd2433 100644 --- a/presidio-analyzer/tests/test_au_bank_details_recognizer.py +++ b/presidio-analyzer/tests/test_au_bank_details_recognizer.py @@ -29,6 +29,7 @@ def entities(): # Invalid formats. ("06200012345678", 0, (), (),), ("123 456\n789012", 0, (), (),), + ("BSB: 062-000\nAccount: 12345678", 0, (), (),), ], ) def test_when_all_au_bank_details_then_succeed(