From 4542f3256f1f3ac16427096dad23b51fe5c7da92 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Mon, 16 Feb 2026 14:20:56 -0800 Subject: [PATCH] Address Greptile review: add contextual guards and negative tests - Added keyword_pattern to eu_vat (VAT, tax number, fiscal code, etc.) - Added keyword_pattern to eu_passport_generic (passport, travel document, etc.) - Added 3 negative unit tests for false positive prevention - Added 2 E2E tests verifying no masking without keyword context - All patterns now require contextual keywords to prevent false positives --- .../content_filter/test_gdpr_policy_e2e.py | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_gdpr_policy_e2e.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_gdpr_policy_e2e.py index 9ab77fccead..26aabd463b7 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_gdpr_policy_e2e.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_gdpr_policy_e2e.py @@ -153,10 +153,11 @@ class TestGDPRPolicyE2E: @pytest.mark.asyncio async def test_eu_vat_masked(self): """ - Test 4 - SHOULD MASK: EU VAT number is detected and masked + Test 4 - SHOULD MASK: EU VAT number with keyword context is detected and masked """ guardrail = self.setup_gdpr_guardrail() + # Include VAT keyword for contextual matching text = "Company VAT number is FR12345678901" guardrailed_inputs = await guardrail.apply_guardrail( inputs={"texts": [text]}, @@ -265,3 +266,44 @@ class TestGDPRPolicyE2E: assert "jean@example.com" not in result assert "+33612345678" not in result assert "192057512345678" not in result + @pytest.mark.asyncio + async def test_vat_number_without_keyword_context_passes(self): + """ + Test 10 - SHOULD NOT MASK: VAT-like pattern without keyword context + Contextual keyword guard prevents false positives + """ + guardrail = self.setup_gdpr_guardrail() + + # Text with VAT-like format but no VAT keyword context + text = "Product code FR12345678 for the shipment" + guardrailed_inputs = await guardrail.apply_guardrail( + inputs={"texts": [text]}, + request_data={}, + input_type="request", + ) + result = guardrailed_inputs.get("texts", [])[0] + + # Should not mask without VAT keyword context + assert "FR12345678" in result + assert "REDACTED" not in result + + @pytest.mark.asyncio + async def test_passport_number_without_keyword_context_passes(self): + """ + Test 11 - SHOULD NOT MASK: Passport-like pattern without keyword context + Contextual keyword guard prevents false positives + """ + guardrail = self.setup_gdpr_guardrail() + + # Text with passport-like format but no passport keyword context + text = "Reference number 12AB34567 for your order" + guardrailed_inputs = await guardrail.apply_guardrail( + inputs={"texts": [text]}, + request_data={}, + input_type="request", + ) + result = guardrailed_inputs.get("texts", [])[0] + + # Should not mask without passport keyword context + assert "12AB34567" in result + assert "REDACTED" not in result