From 2d58b0ad2fbe0383b7eb56e6ee58325c4ac24f12 Mon Sep 17 00:00:00 2001 From: Luuk Wubben Date: Thu, 8 Oct 2026 15:17:47 +0200 Subject: [PATCH 1/4] fix: prevent aws_secret_key pattern from matching certain filepaths --- .../litellm_content_filter/patterns.json | 4 +- .../content_filter/test_content_filter.py | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) 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 db6dd36726c..f5cf9a4fe95 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json @@ -73,9 +73,9 @@ { "name": "aws_secret_key", "display_name": "AWS Secret Key", - "pattern": "\\b([A-Za-z0-9/+=]{40})\\b", + "pattern": "(? Date: Thu, 8 Oct 2026 16:46:40 +0200 Subject: [PATCH 2/4] fix: remove bypass of guardrail with 4 slashes or no digits --- .../litellm_content_filter/patterns.json | 2 +- tempscript.sh | 51 ++++++++++++++++ .../content_filter/test_content_filter.py | 58 ++++++++++++++++--- 3 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 tempscript.sh 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 f5cf9a4fe95..b9d7bdd76af 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json @@ -73,7 +73,7 @@ { "name": "aws_secret_key", "display_name": "AWS Secret Key", - "pattern": "(?" +PORT=4123 +MODEL="ollama/gemma4:31b" +API_BASE="http://localhost:11434" +MSG="${1:-Please review /usr/local/Documents/CodingProjects/PyCharmProjects/README.md}" + +TMP=$(mktemp -d) +trap 'kill $PID 2>/dev/null; rm -rf "$TMP"' EXIT + +cat > "$TMP/config.yaml" < "$TMP/proxy.log" 2>&1 & +PID=$! + +for _ in $(seq 90); do + curl -sf "http://127.0.0.1:$PORT/health/liveliness" > /dev/null && break + kill -0 $PID 2>/dev/null || { echo "Proxy failed to start:"; tail -20 "$TMP/proxy.log"; exit 1; } + sleep 1 +done + +# escape backslashes (Windows paths) and quotes for JSON +J=${MSG//\\/\\\\}; J=${J//\"/\\\"} +CODE=$(curl -s -o "$TMP/out.json" -w '%{http_code}' "http://127.0.0.1:$PORT/chat/completions" \ + -H 'Content-Type: application/json' \ + -d "{\"model\":\"test-model\",\"messages\":[{\"role\":\"user\",\"content\":\"$J\"}]}") + +echo "commit : $(git rev-parse --short HEAD)" +echo "message: $MSG" +echo "status : $CODE" +[ "$CODE" -ge 400 ] && echo "result : BLOCKED by guardrail" || echo "result : ALLOWED (model replied)" +echo "body : $(head -c 300 "$TMP/out.json")" \ No newline at end of file diff --git a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index b4626b8203a..73f18322790 100644 --- a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -397,7 +397,7 @@ class TestContentFilterGuardrail: """ Test AWS Secret Key pattern detection for actual key """ - patterns = [ + patterns: Final = [ ContentFilterPattern( pattern_type="prebuilt", pattern_name="aws_secret_key", @@ -405,13 +405,12 @@ class TestContentFilterGuardrail: ), ] - guardrail = ContentFilterGuardrail( + guardrail: Final = ContentFilterGuardrail( guardrail_name="test-aws-secret-key", patterns=patterns, ) - # Test AWS Secret Key - result = guardrail._check_patterns("My key is wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY") + result: Final = guardrail._check_patterns("My key is wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY") assert result is not None assert result[1] == "aws_secret_key" @@ -419,7 +418,7 @@ class TestContentFilterGuardrail: """ Test AWS Secret Key pattern detection to ensure it doesn't match long filepaths """ - patterns = [ + patterns: Final = [ ContentFilterPattern( pattern_type="prebuilt", pattern_name="aws_secret_key", @@ -427,15 +426,56 @@ class TestContentFilterGuardrail: ), ] - guardrail = ContentFilterGuardrail( + guardrail: Final = ContentFilterGuardrail( guardrail_name="test-aws-secret-key-filepath", patterns=patterns, ) - # Test AWS Secret Key in file path - result = guardrail._check_patterns("Please read the file at " + + result: Final = guardrail._check_patterns("Please read the file at " + "coding/projects/files/others/file/coders.txt") - assert result is None # Should not match as AWS Secret Key + assert result is None + + def test_aws_secret_key_patterns_slash_check(self): + """ + Test AWS Secret Key pattern detection to verify it rejects even with exactly 4 slashes + """ + patterns: Final = [ + ContentFilterPattern( + pattern_type="prebuilt", + pattern_name="aws_secret_key", + action=ContentFilterAction.BLOCK, + ), + ] + + guardrail: Final = ContentFilterGuardrail( + guardrail_name="test-aws-secret-key-slash-check", + patterns=patterns, + ) + + result: Final = guardrail._check_patterns("My key is w/alrXUtnFEMI/K7MDENG/bPxRfiCY/XAMPLEKEY") + assert result is not None + assert result[1] == "aws_secret_key" + + def test_aws_secret_key_patterns_digit_check(self): + """ + Test AWS Secret Key pattern detection to verify it rejects even with no digits + """ + patterns: Final = [ + ContentFilterPattern( + pattern_type="prebuilt", + pattern_name="aws_secret_key", + action=ContentFilterAction.BLOCK, + ), + ] + + guardrail: Final = ContentFilterGuardrail( + guardrail_name="test-aws-secret-key-digit-check", + patterns=patterns, + ) + + result: Final = guardrail._check_patterns("My key is w/alrXUtnFEMI/KdMDENG/bPxRfiCY/XAMPLEKEY") + assert result is not None + assert result[1] == "aws_secret_key" @pytest.mark.asyncio async def test_streaming_hook_mask(self): From d8796bfb91b499948e4cf738dbf69f3572a38a86 Mon Sep 17 00:00:00 2001 From: Luuk Wubben Date: Thu, 8 Oct 2026 16:57:11 +0200 Subject: [PATCH 3/4] fix: remove test script that was not meant to be pushed --- tempscript.sh | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 tempscript.sh diff --git a/tempscript.sh b/tempscript.sh deleted file mode 100644 index c8329dcf2d4..00000000000 --- a/tempscript.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -# E2E check: start LiteLLM proxy with the aws_secret_key content filter, send one message. -# Usage (repo root, litellm installed from this checkout): ./e2e_aws_secret_key.sh "" -PORT=4123 -MODEL="ollama/gemma4:31b" -API_BASE="http://localhost:11434" -MSG="${1:-Please review /usr/local/Documents/CodingProjects/PyCharmProjects/README.md}" - -TMP=$(mktemp -d) -trap 'kill $PID 2>/dev/null; rm -rf "$TMP"' EXIT - -cat > "$TMP/config.yaml" < "$TMP/proxy.log" 2>&1 & -PID=$! - -for _ in $(seq 90); do - curl -sf "http://127.0.0.1:$PORT/health/liveliness" > /dev/null && break - kill -0 $PID 2>/dev/null || { echo "Proxy failed to start:"; tail -20 "$TMP/proxy.log"; exit 1; } - sleep 1 -done - -# escape backslashes (Windows paths) and quotes for JSON -J=${MSG//\\/\\\\}; J=${J//\"/\\\"} -CODE=$(curl -s -o "$TMP/out.json" -w '%{http_code}' "http://127.0.0.1:$PORT/chat/completions" \ - -H 'Content-Type: application/json' \ - -d "{\"model\":\"test-model\",\"messages\":[{\"role\":\"user\",\"content\":\"$J\"}]}") - -echo "commit : $(git rev-parse --short HEAD)" -echo "message: $MSG" -echo "status : $CODE" -[ "$CODE" -ge 400 ] && echo "result : BLOCKED by guardrail" || echo "result : ALLOWED (model replied)" -echo "body : $(head -c 300 "$TMP/out.json")" \ No newline at end of file From 6763236aeb8d1abc758d40169c1972b3d23d1f68 Mon Sep 17 00:00:00 2001 From: Luuk Wubben Date: Thu, 8 Oct 2026 17:00:06 +0200 Subject: [PATCH 4/4] fix: add digit to filepath test case for better coverage --- .../guardrail_hooks/content_filter/test_content_filter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py index 73f18322790..578ace197c9 100644 --- a/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py +++ b/tests/unit/proxy/guardrails/guardrail_hooks/content_filter/test_content_filter.py @@ -432,7 +432,7 @@ class TestContentFilterGuardrail: ) result: Final = guardrail._check_patterns("Please read the file at " + - "coding/projects/files/others/file/coders.txt") + "coding2/projects/files/others/file/coders.txt") assert result is None def test_aws_secret_key_patterns_slash_check(self):