From 3e2f489d8b2bdae27ada94c70b30244653e55cb6 Mon Sep 17 00:00:00 2001 From: iotux Date: Fri, 19 Dec 2025 14:33:10 +0100 Subject: [PATCH] Moved input filter to checkLinux and checkWindows functions --- server/monitor-types/system-service.js | 28 ++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/server/monitor-types/system-service.js b/server/monitor-types/system-service.js index ec31c58bd..af71177e6 100644 --- a/server/monitor-types/system-service.js +++ b/server/monitor-types/system-service.js @@ -18,14 +18,8 @@ class SystemServiceMonitorType extends MonitorType { // Use the new variable name 'system_service_name' to match the monitor type change const serviceName = monitor.system_service_name; - // Basic sanitization. - // We do not allow spaces to ensure users use the "Service Name" (wuauserv) and not "Display Name". - if (!serviceName || !/^[a-zA-Z0-9._\-@]+$/.test(serviceName)) { - heartbeat.status = DOWN; - heartbeat.msg = "Invalid service name. Please use the internal Service Name (no spaces)."; - throw new Error(heartbeat.msg); - } - + // Dispatch to OS-specific handler + // Security validation is handled inside the specific methods to prevent code duplication if (process.platform === "win32") { return this.checkWindows(serviceName, heartbeat); } else { @@ -41,6 +35,15 @@ class SystemServiceMonitorType extends MonitorType { */ async checkLinux(serviceName, heartbeat) { return new Promise((resolve, reject) => { + // SECURITY: Prevent Argument Injection (e.g. passing flags like --help) + // Only allow alphanumeric, dots, dashes, underscores, and @ (common in systemd services like user@1000) + if (!serviceName || !/^[a-zA-Z0-9._\-@]+$/.test(serviceName)) { + heartbeat.status = DOWN; + heartbeat.msg = "Invalid service name. Please use the internal Service Name (no spaces)."; + reject(new Error(heartbeat.msg)); + return; + } + // Linter requires spaces inside array brackets: [ "arg1", "arg2" ] execFile("systemctl", [ "is-active", serviceName ], (error, stdout, stderr) => { // Combine output and truncate to ~200 chars to prevent DB bloat @@ -70,6 +73,15 @@ class SystemServiceMonitorType extends MonitorType { */ async checkWindows(serviceName, heartbeat) { return new Promise((resolve, reject) => { + // SECURITY: Prevent Command Injection + // Only allow alphanumeric, dots, dashes, underscores, and @. + if (!serviceName || !/^[a-zA-Z0-9._\-@]+$/.test(serviceName)) { + heartbeat.status = DOWN; + heartbeat.msg = "Invalid service name. Please use the internal Service Name (no spaces)."; + reject(new Error(heartbeat.msg)); + return; + } + const cmd = "powershell"; // -NoProfile: Faster startup, -NonInteractive: No prompts const args = [