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
87 lines
3.2 KiB
Python
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()
|