java-topology/defects/open-webui-0001/test/open-webui-0001-test.py
russell@unturf.com f85ccf9b99 open-webui-0001: CWE-312 logged secret in PersistentConfig Redis sync
AppConfig.__getattr__ logs raw decoded values when config keys change
via Redis, including OPENAI_API_KEYS, GOOGLE_CLIENT_SECRET, and 20+
other API key/token/password PersistentConfig entries. Fix: redact
values for keys matching SECRET/KEY/TOKEN/PASSWORD/CREDENTIAL denylist.

13/13 PASS
2026-03-31 11:43:57 -04:00

87 lines
3.2 KiB
Python

"""
Unit test for open-webui-0001: CWE-312 Logged Secret in PersistentConfig Redis sync.
The defect: AppConfig.__getattr__ logs the decoded value of ANY PersistentConfig
key when it detects a change from Redis, including OPENAI_API_KEYS,
GOOGLE_CLIENT_SECRET, MICROSOFT_CLIENT_SECRET, RAG_OPENAI_API_KEY, etc.
log.info(f'Updated {key} from Redis: {decoded_value}')
The fix: redact values for keys whose names contain SECRET, KEY, TOKEN,
PASSWORD, or CREDENTIAL.
"""
import unittest
# --- Inline the fix so the test is self-contained ---
_SENSITIVE_KEY_SUBSTRINGS = ('SECRET', 'KEY', 'TOKEN', 'PASSWORD', 'CREDENTIAL')
def _redact_config_value(key: str, value):
if any(s in key.upper() for s in _SENSITIVE_KEY_SUBSTRINGS):
return '***REDACTED***'
return value
class TestRedactConfigValue(unittest.TestCase):
"""Verify that sensitive config keys are redacted before logging."""
def test_openai_api_keys_redacted(self):
result = _redact_config_value('OPENAI_API_KEYS', ['sk-abc123', 'sk-def456'])
self.assertEqual(result, '***REDACTED***')
def test_google_client_secret_redacted(self):
result = _redact_config_value('GOOGLE_CLIENT_SECRET', 'GOCspx-secret-value')
self.assertEqual(result, '***REDACTED***')
def test_microsoft_client_secret_redacted(self):
result = _redact_config_value('MICROSOFT_CLIENT_SECRET', 'ms-secret-value')
self.assertEqual(result, '***REDACTED***')
def test_rag_openai_api_key_redacted(self):
result = _redact_config_value('RAG_OPENAI_API_KEY', 'sk-rag-key')
self.assertEqual(result, '***REDACTED***')
def test_datalab_marker_api_key_redacted(self):
result = _redact_config_value('DATALAB_MARKER_API_KEY', 'marker-key-123')
self.assertEqual(result, '***REDACTED***')
def test_jwt_token_redacted(self):
result = _redact_config_value('JWT_TOKEN', 'eyJhbGciOi...')
self.assertEqual(result, '***REDACTED***')
def test_database_password_redacted(self):
result = _redact_config_value('DATABASE_PASSWORD', 'hunter2')
self.assertEqual(result, '***REDACTED***')
def test_credential_path_redacted(self):
result = _redact_config_value('OAUTH_CREDENTIAL', 'cred-value')
self.assertEqual(result, '***REDACTED***')
def test_non_sensitive_key_not_redacted(self):
result = _redact_config_value('ENABLE_OPENAI_API', True)
self.assertEqual(result, True)
def test_model_name_not_redacted(self):
result = _redact_config_value('DEFAULT_MODEL', 'gpt-4')
self.assertEqual(result, 'gpt-4')
def test_base_urls_not_redacted(self):
urls = ['https://api.openai.com/v1']
result = _redact_config_value('OPENAI_API_BASE_URLS', urls)
self.assertEqual(result, urls)
def test_cache_ttl_not_redacted(self):
result = _redact_config_value('MODELS_CACHE_TTL', 300)
self.assertEqual(result, 300)
def test_case_insensitive(self):
"""Keys with mixed case should still be redacted."""
result = _redact_config_value('My_Api_Key', 'value')
self.assertEqual(result, '***REDACTED***')
if __name__ == '__main__':
unittest.main()