From 246ea54635cb3c57c01765c8e8b9410e869989eb Mon Sep 17 00:00:00 2001 From: Chuck Date: Tue, 12 May 2026 13:56:24 -0400 Subject: [PATCH] fix: address five review findings (Pillow CVEs, daemon exception narrowing, timeout handling, plugin store) - march-madness/requirements.txt: Pillow>=12.2.0 (patches CVE-2026-42308 and CVE-2026-42310; previous floor of 10.3.0 was insufficient) - wifi_monitor_daemon: narrow final except Exception to (subprocess.SubprocessError, OSError) so programming errors in the NM restart block are no longer silently swallowed - api_v3/execute_system_action: add explicit subprocess.TimeoutExpired handler before the generic Exception catch; returns action-specific message with 'status','message','returncode','stdout','stderr' fields so the UI receives a precise, actionable payload instead of the generic 'Failed to execute system action' string - plugins_manager.js: move searchPluginStore into .finally() so the plugin store renders regardless of whether loadInstalledPlugins succeeds or fails; .catch() still logs the error - first_time_install.sh: add safe_plugin_rm.sh NOPASSWD rule to the /tmp/ledmatrix_web_sudoers block; configure_web_sudo.sh had this rule but the standalone installer never granted it, leaving plugin removal broken after first-time install Co-Authored-By: Claude Sonnet 4.6 --- first_time_install.sh | 1 + plugin-repos/march-madness/requirements.txt | 2 +- scripts/utils/wifi_monitor_daemon.py | 2 +- web_interface/blueprints/api_v3.py | 7 +++++++ web_interface/static/v3/plugins_manager.js | 6 +++--- 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/first_time_install.sh b/first_time_install.sh index 3da73157..9f98c91e 100644 --- a/first_time_install.sh +++ b/first_time_install.sh @@ -1110,6 +1110,7 @@ $ACTUAL_USER ALL=(ALL) NOPASSWD: $SYSTEMCTL_PATH restart ledmatrix-web.service $ACTUAL_USER ALL=(ALL) NOPASSWD: $PYTHON_PATH $PROJECT_ROOT_DIR/display_controller.py $ACTUAL_USER ALL=(ALL) NOPASSWD: $BASH_PATH $PROJECT_ROOT_DIR/start_display.sh $ACTUAL_USER ALL=(ALL) NOPASSWD: $BASH_PATH $PROJECT_ROOT_DIR/stop_display.sh +$ACTUAL_USER ALL=(ALL) NOPASSWD: $BASH_PATH $PROJECT_ROOT_DIR/scripts/fix_perms/safe_plugin_rm.sh * EOF if [ -n "$JOURNALCTL_PATH" ]; then cat >> /tmp/ledmatrix_web_sudoers << EOF diff --git a/plugin-repos/march-madness/requirements.txt b/plugin-repos/march-madness/requirements.txt index ba79ba59..aa49c296 100644 --- a/plugin-repos/march-madness/requirements.txt +++ b/plugin-repos/march-madness/requirements.txt @@ -1,4 +1,4 @@ requests>=2.28.0 -Pillow>=10.3.0 +Pillow>=12.2.0 pytz>=2022.1 numpy>=1.24.0 diff --git a/scripts/utils/wifi_monitor_daemon.py b/scripts/utils/wifi_monitor_daemon.py index 05ed429c..53f0497c 100755 --- a/scripts/utils/wifi_monitor_daemon.py +++ b/scripts/utils/wifi_monitor_daemon.py @@ -155,7 +155,7 @@ class WiFiMonitorDaemon: logger.error(f"NetworkManager restart failed (rc={e.returncode}); " "resetting failure counter to avoid tight retry loop") self._consecutive_internet_failures = 0 - except Exception as e: + except (subprocess.SubprocessError, OSError) as e: logger.error(f"NetworkManager restart error: {e}; " "resetting failure counter to avoid tight retry loop") self._consecutive_internet_failures = 0 diff --git a/web_interface/blueprints/api_v3.py b/web_interface/blueprints/api_v3.py index aa2c5616..5911fd5a 100644 --- a/web_interface/blueprints/api_v3.py +++ b/web_interface/blueprints/api_v3.py @@ -1840,6 +1840,13 @@ def execute_system_action(): 'stderr': result.stderr }) + except subprocess.TimeoutExpired: + if action == 'start_display' and mode: + msg = f'Failed to start display in {mode} mode: timed out' + else: + msg = f'Action {action} timed out' + logger.warning("[System] execute_system_action timed out: action=%s", action) + return jsonify({'status': 'error', 'message': msg, 'returncode': -1, 'stdout': '', 'stderr': 'timeout'}), 500 except Exception as e: logger.exception("[System] execute_system_action failed") return jsonify({'status': 'error', 'message': 'Failed to execute system action'}), 500 diff --git a/web_interface/static/v3/plugins_manager.js b/web_interface/static/v3/plugins_manager.js index cc3e8b3d..19d25253 100644 --- a/web_interface/static/v3/plugins_manager.js +++ b/web_interface/static/v3/plugins_manager.js @@ -1225,10 +1225,10 @@ function initializePlugins() { window.pluginManager._reswap = false; // Await the installed-plugins fetch so window.installedPlugins is populated before // searchPluginStore renders Installed/Reinstall badges against it. - loadInstalledPlugins().then(() => { - searchPluginStore(!isReswapWarm); - }).catch(err => { + loadInstalledPlugins().catch(err => { console.error('[PluginStore] loadInstalledPlugins failed:', err); + }).finally(() => { + searchPluginStore(!isReswapWarm); }); // Setup search functionality (with guard against duplicate listeners)