diff --git a/litellm/policy_templates_backup.json b/litellm/policy_templates_backup.json index 1341e86cc5c..5cad00a8c41 100644 --- a/litellm/policy_templates_backup.json +++ b/litellm/policy_templates_backup.json @@ -679,5 +679,97 @@ ], "guardrails_remove": [] } + }, + { + "id": "gdpr-eu-pii-protection", + "title": "GDPR Art. 32 — EU PII Protection", + "description": "GDPR Article 32 compliance for EU personal data protection. Masks French national IDs (NIR/INSEE), EU IBANs, French phone numbers, EU VAT numbers, EU passport numbers, and email addresses. Suitable for applications processing EU citizen data requiring GDPR compliance.", + "region": "EU", + "icon": "ShieldCheckIcon", + "iconColor": "text-indigo-500", + "iconBg": "bg-indigo-50", + "guardrails": [ + "gdpr-eu-national-identifiers", + "gdpr-eu-financial-data", + "gdpr-eu-contact-information", + "gdpr-eu-business-identifiers" + ], + "complexity": "Medium", + "guardrailDefinitions": [ + { + "guardrail_name": "gdpr-eu-national-identifiers", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "fr_nir", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "eu_passport_generic", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_france", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_germany", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_netherlands", "action": "MASK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks EU national identification numbers including French NIR/INSEE and EU passport numbers for GDPR compliance" + } + }, + { + "guardrail_name": "gdpr-eu-financial-data", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "eu_iban_enhanced", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "iban", "action": "MASK"} + ], + "pattern_redaction_format": "[IBAN_REDACTED]" + }, + "guardrail_info": { + "description": "Masks EU bank account numbers (IBANs) to protect financial data under GDPR Article 32" + } + }, + { + "guardrail_name": "gdpr-eu-contact-information", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "email", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "fr_phone", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "fr_postal_code", "action": "MASK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks contact information including emails, French phone numbers, and postal codes for EU data subjects" + } + }, + { + "guardrail_name": "gdpr-eu-business-identifiers", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "eu_vat", "action": "MASK"} + ], + "pattern_redaction_format": "[VAT_NUMBER_REDACTED]" + }, + "guardrail_info": { + "description": "Masks EU VAT identification numbers to protect business entity information under GDPR" + } + } + ], + "templateData": { + "policy_name": "gdpr-eu-pii-protection", + "description": "GDPR Article 32 compliance policy for EU personal data protection. Masks French national IDs, EU IBANs, phone numbers, VAT numbers, passports, and contact information.", + "guardrails_add": [ + "gdpr-eu-national-identifiers", + "gdpr-eu-financial-data", + "gdpr-eu-contact-information", + "gdpr-eu-business-identifiers" + ], + "guardrails_remove": [] + } } ] diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json index 97e4646898f..af671f04afb 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json @@ -415,7 +415,7 @@ { "name": "fr_phone", "display_name": "Phone Number (France)", - "pattern": "(?:\\+33|0033|0)[1-9][0-9]{8}\\b", + "pattern": "(?:(?:\\+33|0033)\\b|\\b0)[1-9][0-9]{8}\\b", "category": "EU PII Patterns", "description": "Detects French phone numbers in various formats (+33, 0033, or 0 prefix followed by 9 digits starting with 1-9)" }, @@ -424,14 +424,18 @@ "display_name": "VAT Number (EU)", "pattern": "\\b(AT|BE|BG|CY|CZ|DE|DK|EE|EL|ES|FI|FR|HR|HU|IE|IT|LT|LU|LV|MT|NL|PL|PT|RO|SE|SI|SK)[0-9A-Z]{8,12}\\b", "category": "EU PII Patterns", - "description": "Detects EU VAT identification numbers (2-letter country code + 8-12 alphanumeric characters covering all EU member states)" + "description": "Detects EU VAT identification numbers (2-letter country code + 8-12 alphanumeric characters covering all EU member states)", + "keyword_pattern": "\\b(?:VAT|V\\.A\\.T\\.|TVA|IVA|BTW|MWST|value\\s*added\\s*tax|tax\\s*number|tax\\s*id|fiscal\\s*number|fiscal\\s*code)\\b", + "allow_word_numbers": false }, { "name": "eu_passport_generic", "display_name": "Passport Number (EU Generic)", "pattern": "\\b[0-9]{2}[A-Z]{2}[0-9]{5}\\b", "category": "EU PII Patterns", - "description": "Detects generic EU passport format (2 digits + 2 letters + 5 digits) - covers France and similar EU formats" + "description": "Detects generic EU passport format (2 digits + 2 letters + 5 digits) - covers France and similar EU formats", + "keyword_pattern": "\\b(?:passport|passeport|travel\\s*document|document\\s*number|reisepass|paspoort|paszport)\\b", + "allow_word_numbers": false }, { "name": "fr_postal_code", diff --git a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_eu_patterns.py b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_eu_patterns.py index dfab059717c..80008128659 100644 --- a/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_eu_patterns.py +++ b/tests/test_litellm/proxy/guardrails/guardrail_hooks/content_filter/test_eu_patterns.py @@ -57,6 +57,14 @@ class TestEUVAT: assert pattern.search("DE123456789") is not None assert pattern.search("IT12345678901") is not None + def test_common_words_not_matched(self): + """Test that common English words starting with country codes are not matched""" + pattern = get_compiled_pattern("eu_vat") + # Common words that could false-positive without proper context + assert pattern.search("DEPARTMENT") is None + assert pattern.search("ITALY") is None + assert pattern.search("DECEMBER") is None + class TestEUPassportGeneric: """Test generic EU passport detection""" 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 26aabd463b7..a8d91495c80 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 @@ -10,8 +10,6 @@ import pytest sys.path.insert(0, os.path.abspath("../../")) -from fastapi import HTTPException - from litellm.proxy.guardrails.guardrail_hooks.litellm_content_filter.content_filter import ( ContentFilterGuardrail, )