From 696e941792ed9e9666560ab13159e3a1fe898b98 Mon Sep 17 00:00:00 2001 From: Aman Singh Date: Sat, 4 Apr 2026 09:55:25 +0530 Subject: [PATCH] fix: sanitize API keys from LLM error messages before raising --- src/extension_shield/llm/clients/fallback.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/extension_shield/llm/clients/fallback.py b/src/extension_shield/llm/clients/fallback.py index 002d77d6..43264d73 100644 --- a/src/extension_shield/llm/clients/fallback.py +++ b/src/extension_shield/llm/clients/fallback.py @@ -1,6 +1,7 @@ """LLM fallback client module for multi-provider support with automatic failover.""" import os +import re import logging import threading from typing import Dict, Optional, Any, List @@ -12,6 +13,20 @@ logger = logging.getLogger(__name__) +# Patterns that may indicate leaked secrets in error messages +_SECRET_PATTERNS = [ + re.compile(r"sk-[A-Za-z0-9\-_]{10,}", re.IGNORECASE), # OpenAI/Groq keys + re.compile(r"Bearer\s+[A-Za-z0-9\-_\.]{10,}", re.IGNORECASE), # Bearer tokens + re.compile(r"key[=:\s]+['\"]?[A-Za-z0-9\-_]{20,}['\"]?", re.IGNORECASE), # Generic key=value +] + + +def _sanitize_error_message(msg: str) -> str: + """Remove potential secrets (API keys, tokens) from error messages.""" + for pattern in _SECRET_PATTERNS: + msg = pattern.sub("[REDACTED]", msg) + return msg + class LLMFallbackError(Exception): """Exception raised when all LLM providers fail.""" @@ -284,8 +299,8 @@ def invoke_with_fallback( # Don't retry non-retryable errors break - # Store error for final exception - errors[f"{provider_name} (attempt {attempt + 1})"] = f"{error_type}: {error_msg[:200]}" + # Store sanitized error for final exception (strip potential secrets) + errors[f"{provider_name} (attempt {attempt + 1})"] = f"{error_type}: {_sanitize_error_message(error_msg[:200])}" # If this was the last retry for this provider, try next provider if attempt < max_retries: