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
This commit is contained in:
parent
5a8e0836c3
commit
f85ccf9b99
3 changed files with 116 additions and 1 deletions
|
|
@ -945,5 +945,9 @@
|
|||
"supertuxkart-0001-0001": "UNDF-2026-000000944",
|
||||
"supertuxkart-0002-0002": "UNDF-2026-000000945",
|
||||
"minetest-0004-0004": "UNDF-2026-000000946",
|
||||
"minetest-0005-0005": "UNDF-2026-000000947"
|
||||
"minetest-0005-0005": "UNDF-2026-000000947",
|
||||
"open-webui-0001-0001": "UNDF-2026-000000948",
|
||||
"tiled-0001-0001": "UNDF-2026-000000949",
|
||||
"tiled-0002-0002": "UNDF-2026-000000950",
|
||||
"tiled-0003-0003": "UNDF-2026-000000951"
|
||||
}
|
||||
|
|
|
|||
24
defects/open-webui-0001/patch/open-webui-0001.patch
Normal file
24
defects/open-webui-0001/patch/open-webui-0001.patch
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# UNDF: UNDF-2026-000000948
|
||||
--- a/backend/open_webui/config.py
|
||||
+++ b/backend/open_webui/config.py
|
||||
@@ -266,10 +266,17 @@
|
||||
decoded_value = json.loads(redis_value)
|
||||
|
||||
# Update the in-memory value if different
|
||||
if self._state[key].value != decoded_value:
|
||||
self._state[key].value = decoded_value
|
||||
- log.info(f'Updated {key} from Redis: {decoded_value}')
|
||||
+ log.info(f'Updated {key} from Redis: {_redact_config_value(key, decoded_value)}')
|
||||
|
||||
except json.JSONDecodeError:
|
||||
log.error(f'Invalid JSON format in Redis for {key}: {redis_value}')
|
||||
|
||||
return self._state[key].value
|
||||
+
|
||||
+
|
||||
+_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
|
||||
87
defects/open-webui-0001/test/open-webui-0001-test.py
Normal file
87
defects/open-webui-0001/test/open-webui-0001-test.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
"""
|
||||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue