From 6a3d9c96f8a2cd785d4973449059b2624ea3ffce Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Fri, 21 Mar 2025 14:13:38 -0700 Subject: [PATCH] feat: enhance UnraidCheckExec security * Added security headers to the setupEnvironment method for improved security. * Implemented stricter validation for the altUrl parameter to ensure it matches allowed domains and uses HTTPS. * Enhanced script execution checks to validate permissions before running the unraidcheck script. --- .../include/UnraidCheckExec.php | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php index 15523e19a..7b2881cd2 100644 --- a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php +++ b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php @@ -12,31 +12,57 @@ class UnraidCheckExec { private const SCRIPT_PATH = '/usr/local/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck'; + private const ALLOWED_DOMAIN = 'releases.unraid.net'; private function setupEnvironment(): void { header('Content-Type: application/json'); + header('X-Content-Type-Options: nosniff'); + header('X-Frame-Options: DENY'); + header('Content-Security-Policy: default-src \'none\''); $params = [ 'json' => 'true', ]; - // allows the web components to determine the OS_RELEASES url - if (isset($_GET['altUrl']) && filter_var($_GET['altUrl'], FILTER_VALIDATE_URL)) { - $params['altUrl'] = $_GET['altUrl']; + + if (isset($_GET['altUrl'])) { + $url = filter_var($_GET['altUrl'], FILTER_VALIDATE_URL); + if ($url !== false) { + $host = parse_url($url, PHP_URL_HOST); + $scheme = parse_url($url, PHP_URL_SCHEME); + + if ($host && $scheme === 'https' && ( + $host === self::ALLOWED_DOMAIN || + str_ends_with($host, '.' . self::ALLOWED_DOMAIN) + )) { + $params['altUrl'] = $url; + } + } } - // pass the params to the unraidcheck script for usage in UnraidCheck.php + putenv('QUERY_STRING=' . http_build_query($params)); } public function execute(): string { + // Validate script with all necessary permissions + if (!is_file(self::SCRIPT_PATH) || + !is_readable(self::SCRIPT_PATH) || + !is_executable(self::SCRIPT_PATH)) { + throw new RuntimeException('Script not found or not executable'); + } + $this->setupEnvironment(); $output = []; - exec(self::SCRIPT_PATH, $output); + $command = escapeshellcmd(self::SCRIPT_PATH); + if (exec($command, $output) === false) { + throw new RuntimeException('Script execution failed'); + } + return implode("\n", $output); } } // Usage $checker = new UnraidCheckExec(); -echo $checker->execute(); \ No newline at end of file +echo $checker->execute();