From 4d2a567597f9bca95534d0a76dbf302269b71510 Mon Sep 17 00:00:00 2001 From: Chuck Date: Thu, 14 May 2026 12:57:58 -0400 Subject: [PATCH] fix(security): escape user input in raw HTML responses in pages_v3.py plugin_id comes directly from the URL path (/partials/plugin-config/) and was interpolated into an HTML fragment without escaping. A crafted URL like /partials/plugin-config/ would inject that tag into the DOM via the HTMX partial response. Fix: wrap all user-controlled values in markupsafe.escape() before embedding in raw HTML strings. Affects the plugin-not-found 404 response and both error 500 responses in the plugin config partial. Co-Authored-By: Claude Sonnet 4.6 --- web_interface/blueprints/pages_v3.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/web_interface/blueprints/pages_v3.py b/web_interface/blueprints/pages_v3.py index fc4a2988..89e550de 100644 --- a/web_interface/blueprints/pages_v3.py +++ b/web_interface/blueprints/pages_v3.py @@ -1,4 +1,5 @@ from flask import Blueprint, render_template, flash +from markupsafe import escape import json import logging from pathlib import Path @@ -95,7 +96,7 @@ def load_plugin_config_partial(plugin_id): try: return _load_plugin_config_partial(plugin_id) except Exception as e: - return f'
Error loading plugin config: {str(e)}
', 500 + return f'
Error loading plugin config: {escape(str(e))}
', 500 def _load_overview_partial(): """Load overview partial with system stats""" @@ -352,7 +353,7 @@ def _load_plugin_config_partial(plugin_id): plugin_info = pages_v3.plugin_manager.get_plugin_info(plugin_id) if not plugin_info: - return f'
Plugin "{plugin_id}" not found
', 404 + return f'
Plugin "{escape(plugin_id)}" not found
', 404 # Get plugin instance (may be None if not loaded) plugin_instance = pages_v3.plugin_manager.get_plugin(plugin_id) @@ -454,7 +455,7 @@ def _load_plugin_config_partial(plugin_id): except Exception as e: import traceback traceback.print_exc() - return f'
Error loading plugin config: {str(e)}
', 500 + return f'
Error loading plugin config: {escape(str(e))}
', 500 def _load_starlark_config_partial(app_id):