From be16f320ac637cb9ea8f2b204daee2135a8990a9 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 19 Feb 2026 17:17:11 -0800 Subject: [PATCH] fix filter --- .../litellm_content_filter/content_filter.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index d82548363e..4d9472b0ca 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -965,7 +965,7 @@ class ContentFilterGuardrail(CustomGuardrail): # Convert asterisks (*) in keywords to regex wildcards # Asterisks are used in the source data to obfuscate profanity (e.g., "fu*c*k" -> "fuck") # We treat * as a wildcard matching zero or one character - keyword_pattern_str = keyword.replace("*", ".?") + keyword_pattern_str = self._keyword_to_regex_pattern(keyword) # Use word boundary matching for single words to avoid false positives # (e.g., "men" should not match "recommend") @@ -1116,7 +1116,7 @@ class ContentFilterGuardrail(CustomGuardrail): }, ) elif action == ContentFilterAction.MASK: - keyword_pattern_for_masking = keyword.replace("*", ".?") + keyword_pattern_for_masking = self._keyword_to_regex_pattern(keyword) text = re.sub( keyword_pattern_for_masking, self.keyword_redaction_tag, @@ -1200,7 +1200,7 @@ class ContentFilterGuardrail(CustomGuardrail): }, ) elif action == ContentFilterAction.MASK: - keyword_pattern_for_masking = keyword.replace("*", ".?") + keyword_pattern_for_masking = self._keyword_to_regex_pattern(keyword) text = re.sub( keyword_pattern_for_masking, self.keyword_redaction_tag, @@ -1266,7 +1266,7 @@ class ContentFilterGuardrail(CustomGuardrail): # Check blocked words - iterate through ALL blocked words text_lower = text.lower() for keyword, (action, description) in self.blocked_words.items(): - keyword_pattern_str = keyword.replace("*", ".?") + keyword_pattern_str = self._keyword_to_regex_pattern(keyword) if re.search(keyword_pattern_str, text_lower): text = self._handle_blocked_word_match( keyword, action, description, text, detections @@ -1275,6 +1275,17 @@ class ContentFilterGuardrail(CustomGuardrail): return text + @staticmethod + def _keyword_to_regex_pattern(keyword: str) -> str: + """ + Convert a keyword into a safe regex pattern. + + Escape all regex metacharacters to prevent malformed patterns from + user/template keywords. Preserve existing '*' wildcard semantics by + translating escaped '*' into '.?'. + """ + return re.escape(keyword).replace(r"\*", ".?") + def _mask_content(self, text: str, pattern_name: str) -> str: """ Mask sensitive content in text.