From 5baeebe51601055749c58f66de5da138ec2b2e99 Mon Sep 17 00:00:00 2001 From: jesus Date: Sat, 17 Oct 2026 02:36:34 +0000 Subject: [PATCH] fix(guardrails): reject invalid UK NINO prefix letters Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../litellm_content_filter/patterns.json | 2 +- .../content_filter/test_uk_patterns.py | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) 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 62219c5c585..a547f92ad9e 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json @@ -628,7 +628,7 @@ { "name": "uk_nino", "display_name": "National Insurance Number (UK)", - "pattern": "\\b(?!BG|GB|KN|NK|NT|TN|ZZ)[A-CEGHJ-PR-TW-Z]{2}\\s?\\d{2}(?:\\s?\\d{2}){2}\\s?[A-D]\\b", + "pattern": "\\b(?!BG|GB|KN|NK|NT|TN|ZZ)[ABCEGHJKLMNOPRSTWXYZ][ABCEGHJKLMNPRSTWXYZ]\\s?\\d{2}(?:\\s?\\d{2}){2}\\s?[A-D]\\b", "category": "UK PII Patterns", "description": "Detects UK National Insurance numbers with valid prefixes and suffixes", "keyword_pattern": "\\b(?:national\\s*insurance\\s*number|national\\s*insurance|NI\\s*number|NI\\s*no\\.?|NINO)\\b", diff --git a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_uk_patterns.py b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_uk_patterns.py index 8ac48c12495..d6d4a796eb1 100644 --- a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_uk_patterns.py +++ b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_uk_patterns.py @@ -107,6 +107,37 @@ async def test_invalid_uk_pii_format_passes_unchanged( assert valid_value not in output +@pytest.mark.parametrize( + "invalid_prefix", + ( + "DA", + "FA", + "IA", + "QA", + "UA", + "VA", + "AD", + "AF", + "AI", + "AO", + "AQ", + "AU", + "AV", + ), +) +@pytest.mark.asyncio +async def test_uk_nino_rejects_disallowed_prefix_letters(invalid_prefix: str) -> None: + guardrail: Final = _guardrail_for("uk_nino") + text: Final = f"national insurance number: {invalid_prefix}123456A" + result: Final = await guardrail.apply_guardrail( + inputs={"texts": [text]}, + request_data={}, + input_type="request", + ) + + assert result["texts"][0] == text + + def test_uk_policy_templates_reference_available_prebuilt_patterns() -> None: patterns_file: Final = REPO_ROOT / "litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json" pattern_names: Final = {