diff --git a/enterprise/litellm_enterprise/enterprise_callbacks/secrets_plugins/credential_keyword.py b/enterprise/litellm_enterprise/enterprise_callbacks/secrets_plugins/credential_keyword.py index d9afc751daf..c1f578817ce 100644 --- a/enterprise/litellm_enterprise/enterprise_callbacks/secrets_plugins/credential_keyword.py +++ b/enterprise/litellm_enterprise/enterprise_callbacks/secrets_plugins/credential_keyword.py @@ -4,7 +4,7 @@ from typing import Final from detect_secrets.plugins.keyword import KeywordDetector -_CREDENTIAL_VALUE: Final = re.compile(r"[A-Za-z0-9_.~+/-]+={0,2}") +_CREDENTIAL_VALUE: Final = re.compile(r"[^\s()\[\]]+") _ENVIRONMENT_REFERENCE: Final = re.compile(r"os\.environ/\w+", re.IGNORECASE) _ENVIRONMENT_VARIABLE_NAME: Final = re.compile(r"[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+") _LOWERCASE_WORD_SEQUENCE: Final = re.compile(r"[a-z]+(?:[-._/][a-z]+)+") diff --git a/tests/test_litellm/enterprise/enterprise_callbacks/test_secret_detection.py b/tests/test_litellm/enterprise/enterprise_callbacks/test_secret_detection.py index 4def77173ed..fdc3acc203c 100644 --- a/tests/test_litellm/enterprise/enterprise_callbacks/test_secret_detection.py +++ b/tests/test_litellm/enterprise/enterprise_callbacks/test_secret_detection.py @@ -90,6 +90,12 @@ def test_scan_message_preserves_quoted_benign_identifiers(): "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", ), ("password=aB3dE6gH9jK2", "aB3dE6gH9jK2"), + ("api_key: hunter2!brahms", "hunter2!brahms"), + ('db_password: "p@ssw0rd!2026"', "p@ssw0rd!2026"), + ( + 'url: "postgresql://user:s3cr3t@db-host:5432/app"', + "postgresql://user:s3cr3t@db-host:5432/app", + ), ], ids=[ "env-password", @@ -108,6 +114,9 @@ def test_scan_message_preserves_quoted_benign_identifiers(): "django-secret-key", "slashed-aws-secret", "shortest-accepted-value", + "punctuation-bearing-password", + "symbol-heavy-password", + "connection-string-under-a-credential-key", ], ) def test_scan_message_redacts_credentials_assigned_to_credential_keys(content, secret): @@ -139,11 +148,11 @@ def test_scan_message_redacts_credentials_assigned_to_credential_keys(content, s "langfuse_secret: os.environ/LANGFUSE_PROJECT1_SECRET", "api_key = OPENAI_API_KEY", "password = pwd12345678", - "api_key: hunter2!brahms", "model_key: gpt-4o-mini-2024-07-18", "openrouter/anthropic/claude-3-5-sonnet-20240620", '{"content-type": "application/json"}', "passwordless_login: enabled-for-all-users", + 'password: "I forgot mine, can you reset it"', "secret_sauce: tomatoes-basil-garlic-oregano", "user_secret_question: what-was-your-first-pet", "password_reset_url: example.com/reset-password/flow", @@ -153,6 +162,12 @@ def test_scan_message_redacts_credentials_assigned_to_credential_keys(content, s 'api_key = "OPENAI_API_KEY"', "model_list:\n - litellm_params:\n api_key: 'PERPLEXITY_API_KEY'", 'config = build(_provider("ve_missing", api_key_env="VE_MISSING_KEY"))', + "api_key = get_api_key_from_env()", + "api_key = get_secret_str(MISTRAL_OCR_API_KEY_ENV_VAR)", + "secret_manager = MagicMock(spec=BaseSecretManager)", + "api_key = self.resolve_server_api_key(", + "api_key = sys.argv[1]", + "password = credentials[environment]", ], ids=[ "prose-password", @@ -175,11 +190,11 @@ def test_scan_message_redacts_credentials_assigned_to_credential_keys(content, s "env-reference-nested", "env-variable-name", "below-minimum-length", - "non-credential-charset", "model-name", "namespaced-model-name", "media-type", "hyphenated-english", + "quoted-sentence-under-a-credential-key", "hyphenated-phrase", "hyphenated-question", "url-under-credential-key", @@ -189,6 +204,12 @@ def test_scan_message_redacts_credentials_assigned_to_credential_keys(content, s "quoted-env-variable-name", "quoted-env-name-in-a-config", "quoted-env-name-in-a-code-paste", + "bare-call", + "call-with-an-argument", + "keyword-argument-call", + "unclosed-call", + "positional-subscript", + "keyed-subscript", ], ) def test_scan_message_keeps_benign_values(content):