From b14fbe3c9e2eb707f4592418cc7596edeb39a981 Mon Sep 17 00:00:00 2001 From: musi22 Date: Fri, 9 Oct 2026 00:50:48 +0530 Subject: [PATCH] test(presidio): add regression test for nested email/domain spans (#42130) --- .../guardrail_hooks/test_presidio.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py index 5561c44436d..6b40e97fe25 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/test_presidio.py @@ -3223,6 +3223,51 @@ def test_finalize_presidio_anonymize_numbered_tokens_nested_higher_score_span(): assert set(re.findall(r"<[A-Z_]+_\d+>", result)) == set(pii_tokens.keys()) +def test_finalize_presidio_anonymize_numbered_tokens_email_domain_nested(): + """Regression test for #42130 (comment 5935626289): + + Analyzer reporting a nested URL domain inside an EMAIL_ADDRESS must not cause + subsequent span offset miscalculations that delete surrounding text like " or ". + Asserts both that the surrounding text survives masking intact, and that unmasking + faithfully recovers the original prompt. + """ + import re + + from litellm.proxy.guardrails.guardrail_hooks.presidio import ( + _OPTIONAL_PresidioPIIMasking, + ) + + guardrail = _OPTIONAL_PresidioPIIMasking(mock_testing=True) + text = "Email dana.whitfield@example.com or call 555-234-9981." + analyze_results = [ + {"entity_type": "EMAIL_ADDRESS", "start": 6, "end": 32, "score": 1.0}, + {"entity_type": "URL", "start": 21, "end": 32, "score": 0.85}, + {"entity_type": "PHONE_NUMBER", "start": 41, "end": 53, "score": 0.85}, + ] + request_data = {"metadata": {}} + masked_entity_count = {} + result = guardrail._finalize_presidio_anonymize_numbered_tokens( + text=text, + analyze_results=analyze_results, + request_data=request_data, + masked_entity_count=masked_entity_count, + ) + + # Assert surrounding text " or " survives masking unchanged + assert " or " in result + assert result == "Email or call ." + pii_tokens = request_data["metadata"]["pii_tokens"] + assert pii_tokens[""] == "dana.whitfield@example.com" + assert pii_tokens[""] == "555-234-9981" + assert "", result)) == set(pii_tokens.keys()) + + # Assert unmasking recovers original text with " or " surviving unchanged + unmasked = guardrail._unmask_pii_text(result, pii_tokens) + assert unmasked == text + assert " or " in unmasked + + def test_resolve_overlapping_spans_empty_and_disjoint(): from litellm.proxy.guardrails.guardrail_hooks.presidio import _OPTIONAL_PresidioPIIMasking