diff --git a/CHANGELOG.md b/CHANGELOG.md index 74c1db3..ec79c08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- `is_valid_legal_process` retornava erro ou aceitava dígitos não ASCII para números de processo malformados; agora retorna `False`, e `format_legal_process` retorna `None` nesses casos [#793](https://github.com/brazilian-utils/python/issues/793) - Cálculo do dígito verificador em `is_valid_legal_process` e `generate_legal_process`, que rejeitava números de processo reais e gerava números inválidos [#785](https://github.com/brazilian-utils/python/pull/785) ## [2.5.0] - 2026-06-30 diff --git a/brutils/legal_process.py b/brutils/legal_process.py index 5657bfa..ce2ca6c 100644 --- a/brutils/legal_process.py +++ b/brutils/legal_process.py @@ -56,7 +56,7 @@ def format_legal_process(legal_process_id: str) -> str | None: None """ - if legal_process_id.isdigit() and len(legal_process_id) == 20: + if _is_twenty_digits(legal_process_id): capture_fields = r"(\d{7})(\d{2})(\d{4})(\d)(\d{2})(\d{4})" include_chars = r"\1-\2.\3.\4.\5.\6" @@ -69,6 +69,29 @@ def format_legal_process(legal_process_id: str) -> str | None: ############ +def _is_twenty_digits(legal_process_id: str) -> bool: + """ + Checks whether the string has exactly 20 ASCII digits. + + `str.isdigit` is not enough: it also accepts characters such as "²" and + "٣", which `int` either rejects or reads as a different digit. + + Args: + legal_process_id (str): The string to be checked. + + Returns: + bool: True if the string has exactly 20 ASCII digits, False otherwise. + + Example: + >>> _is_twenty_digits("68476506120233030000") + True + >>> _is_twenty_digits("6847650612023303000٣") + False + """ + + return re.fullmatch(r"\d{20}", legal_process_id, flags=re.ASCII) is not None + + def is_valid(legal_process_id: str) -> bool: """ Check if a legal process ID is valid. @@ -92,7 +115,13 @@ def is_valid(legal_process_id: str) -> bool: False """ + if not isinstance(legal_process_id, str): + return False + clean_legal_process_id = remove_symbols(legal_process_id) + if not _is_twenty_digits(clean_legal_process_id): + return False + DD = clean_legal_process_id[7:9] J = clean_legal_process_id[13:14] TR = clean_legal_process_id[14:16] diff --git a/tests/test_legal_process.py b/tests/test_legal_process.py index 7eb8f9c..6a9d344 100644 --- a/tests/test_legal_process.py +++ b/tests/test_legal_process.py @@ -94,6 +94,25 @@ def test_is_valid(self): self.assertIs(is_valid("455323423QQWEQWSsasd&*(()"), False) self.assertIsInstance(is_valid("455323423QQWEQWSsasd&*(()"), bool) + def test_is_valid_returns_false_for_malformed_input(self): + # 5046512-94.2016.4.04.7000 is a real, valid legal process ID + self.assertIs(is_valid("50465129420164047000"), True) + # Truncated: the court (TR) or origin (OOOO) fields are empty + self.assertIs(is_valid("50465129420164"), False) + self.assertIs(is_valid("5046512942016404"), False) + # Letters where digits are expected + self.assertIs(is_valid("ABCDEFG9420164047000"), False) + # Characters that str.isdigit accepts but are not ASCII digits + self.assertIs(is_valid("²0465129420164047000"), False) + self.assertIs(is_valid("5046512942016404700٠"), False) + # Not a string + self.assertIs(is_valid(50465129420164047000), False) + self.assertIs(is_valid(None), False) + + def test_format_legal_process_rejects_non_ascii_digits(self): + self.assertIsNone(format_legal_process("5046512942016404700٠")) + self.assertIsNone(format_legal_process("²" * 20)) + if __name__ == "__main__": main()