From bd124596bef7c0afcb5fce8846e129c95594e30d Mon Sep 17 00:00:00 2001 From: Chuck Date: Sat, 21 Mar 2026 13:17:46 -0400 Subject: [PATCH] fix(security): correct error inference, remove debug log leak, consolidate config handlers - _infer_error_code: map Config* exceptions to CONFIG_LOAD_FAILED (ConfigError is only raised by load_config(), so CONFIG_SAVE_FAILED produced wrong safe message and wrong suggested_fixes) - Remove leftover DEBUG logs in save_main_config that dumped full request body and all HTTP headers (Authorization, Cookie, etc.) - Replace dead FileNotFoundError/JSONDecodeError/IOError handlers in get_dim_schedule_config with single ConfigError catch (load_config already wraps these into ConfigError) - Remove redundant local `from src.exceptions import ConfigError` imports now covered by top-level import - Strip str(e) from client-facing error messages in dim schedule handler Co-Authored-By: Claude Opus 4.6 --- src/web_interface/errors.py | 2 +- web_interface/blueprints/api_v3.py | 29 +++++------------------------ 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/web_interface/errors.py b/src/web_interface/errors.py index a138412f..11397892 100644 --- a/src/web_interface/errors.py +++ b/src/web_interface/errors.py @@ -254,7 +254,7 @@ class WebInterfaceError: exception_name = type(exception).__name__ if "Config" in exception_name: - return ErrorCode.CONFIG_SAVE_FAILED + return ErrorCode.CONFIG_LOAD_FAILED elif "Plugin" in exception_name: return ErrorCode.PLUGIN_LOAD_FAILED elif "Permission" in exception_name or "Access" in exception_name: diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index c4f93d69..9e332f73 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -17,6 +17,7 @@ logger = logging.getLogger(__name__) # Import new infrastructure from src.web_interface.api_helpers import success_response, error_response, validate_request_json from src.web_interface.errors import ErrorCode +from src.exceptions import ConfigError from src.plugin_system.operation_types import OperationType from src.web_interface.logging_config import log_plugin_operation, log_config_change from src.web_interface.validators import ( @@ -440,32 +441,18 @@ def get_dim_schedule_config(): }) return success_response(data=dim_schedule_config) - except FileNotFoundError as e: - logger.error(f"[DIM SCHEDULE] Config file not found: {e}", exc_info=True) + except ConfigError as e: + logger.error(f"[DIM SCHEDULE] Config error: {e}", exc_info=True) return error_response( ErrorCode.CONFIG_LOAD_FAILED, - "Configuration file not found", - status_code=500 - ) - except json.JSONDecodeError as e: - logger.error(f"[DIM SCHEDULE] Invalid JSON in config file: {e}", exc_info=True) - return error_response( - ErrorCode.CONFIG_LOAD_FAILED, - "Configuration file contains invalid JSON", - status_code=500 - ) - except (IOError, OSError) as e: - logger.error(f"[DIM SCHEDULE] Error reading config file: {e}", exc_info=True) - return error_response( - ErrorCode.CONFIG_LOAD_FAILED, - f"Error reading configuration file: {str(e)}", + "Configuration file not found or invalid", status_code=500 ) except Exception as e: logger.error(f"[DIM SCHEDULE] Unexpected error loading config: {e}", exc_info=True) return error_response( ErrorCode.CONFIG_LOAD_FAILED, - f"Unexpected error loading dim schedule configuration: {str(e)}", + "Unexpected error loading dim schedule configuration", status_code=500 ) @@ -654,10 +641,6 @@ def save_main_config(): if not data: return jsonify({'status': 'error', 'message': 'No data provided'}), 400 - logger.error(f"DEBUG: save_main_config received data: {data}") - logger.error(f"DEBUG: Content-Type header: {request.content_type}") - logger.error(f"DEBUG: Headers: {dict(request.headers)}") - # Merge with existing config (similar to original implementation) current_config = api_v3.config_manager.load_config() @@ -1012,7 +995,6 @@ def save_raw_main_config(): except json.JSONDecodeError as e: return jsonify({'status': 'error', 'message': f'Invalid JSON: {str(e)}'}), 400 except Exception as e: - from src.exceptions import ConfigError logger.exception("[RawConfig] Failed to save raw main config") if isinstance(e, ConfigError): return error_response( @@ -1051,7 +1033,6 @@ def save_raw_secrets_config(): except json.JSONDecodeError as e: return jsonify({'status': 'error', 'message': f'Invalid JSON: {str(e)}'}), 400 except Exception as e: - from src.exceptions import ConfigError logger.exception("[RawSecrets] Failed to save raw secrets config") if isinstance(e, ConfigError): return error_response(