From dae41620fe43277beed8f2189bebefe7e44f174f Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Thu, 21 May 2026 13:36:03 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=94=92=20Fix=20Command/Argument=20Inj?= =?UTF-8?q?ection=20Vulnerability=20in=20SafeProcess.LaunchApp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Views/SettingsPage.axaml.cs | 2 +- .../Services/SafeProcess.cs | 255 ++++++++++-------- 2 files changed, 138 insertions(+), 119 deletions(-) diff --git a/src/GregModmanager.Avalonia/Views/SettingsPage.axaml.cs b/src/GregModmanager.Avalonia/Views/SettingsPage.axaml.cs index cb7786e..87e9dff 100644 --- a/src/GregModmanager.Avalonia/Views/SettingsPage.axaml.cs +++ b/src/GregModmanager.Avalonia/Views/SettingsPage.axaml.cs @@ -202,7 +202,7 @@ private static void OnRestartApp(object? sender, RoutedEventArgs e) { var exe = Environment.ProcessPath; if (string.IsNullOrEmpty(exe)) return; - SafeProcess.LaunchApp(exe); + SafeProcess.LaunchApp(exe, null); if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) desktop.Shutdown(); } diff --git a/src/GregModmanager.Core/Services/SafeProcess.cs b/src/GregModmanager.Core/Services/SafeProcess.cs index 12f4ec5..e87754e 100644 --- a/src/GregModmanager.Core/Services/SafeProcess.cs +++ b/src/GregModmanager.Core/Services/SafeProcess.cs @@ -1,118 +1,137 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; - -namespace GregModmanager.Services; - -public static class SafeProcess -{ - /// - /// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed. - /// - public static Task OpenUrlAsync(string url) - { - if (string.IsNullOrWhiteSpace(url)) return Task.CompletedTask; - - try - { - if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && - (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) - { - Process.Start(new ProcessStartInfo - { - FileName = uri.ToString(), - UseShellExecute = true - }); - } - else - { - AppFileLog.Warn($"Blocked attempt to open insecure or invalid URL: {url}"); - } - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open URL: {url}", ex); - } - - return Task.CompletedTask; - } - - /// - /// Opens a folder in the system's file explorer. - /// - public static void OpenFolder(string path) - { - if (string.IsNullOrWhiteSpace(path)) return; - - try - { - if (OperatingSystem.IsWindows()) - { - Process.Start(new ProcessStartInfo - { - FileName = "explorer.exe", - Arguments = $"\"{path}\"", - UseShellExecute = false - }); - } - else - { - // For other OS, we might still need UseShellExecute for some scenarios, - // but we should be careful. MAUI doesn't have a direct "OpenFolder" that works everywhere. - Process.Start(new ProcessStartInfo - { - FileName = path, - UseShellExecute = true - }); - } - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open folder: {path}", ex); - } - } - - /// - /// Specifically for Windows, opens explorer and selects a file. - /// - public static void OpenExplorerAndSelect(string filePath) - { - if (!OperatingSystem.IsWindows() || string.IsNullOrWhiteSpace(filePath)) return; - - try - { - Process.Start(new ProcessStartInfo - { - FileName = "explorer.exe", - Arguments = $"/select,\"{filePath}\"", - UseShellExecute = false - }); - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to open explorer and select: {filePath}", ex); - } - } - - /// - /// Launches an executable with UseShellExecute = false. - /// - public static void LaunchApp(string exePath, string arguments = "") - { - if (string.IsNullOrWhiteSpace(exePath)) return; - - try - { - Process.Start(new ProcessStartInfo - { - FileName = exePath, - Arguments = arguments, - UseShellExecute = false - }); - } - catch (Exception ex) - { - AppFileLog.Error($"Failed to launch app: {exePath}", ex); - } - } -} +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +namespace GregModmanager.Services; + +public static class SafeProcess +{ + /// + /// Opens a URL in the default browser safely, ensuring only http and https schemes are allowed. + /// + public static Task OpenUrlAsync(string url) + { + if (string.IsNullOrWhiteSpace(url)) return Task.CompletedTask; + + try + { + if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && + (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)) + { + Process.Start(new ProcessStartInfo + { + FileName = uri.ToString(), + UseShellExecute = true + }); + } + else + { + AppFileLog.Warn($"Blocked attempt to open insecure or invalid URL: {url}"); + } + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open URL: {url}", ex); + } + + return Task.CompletedTask; + } + + /// + /// Opens a folder in the system's file explorer. + /// + public static void OpenFolder(string path) + { + if (string.IsNullOrWhiteSpace(path)) return; + + try + { + if (OperatingSystem.IsWindows()) + { + var startInfo = new ProcessStartInfo + { + FileName = "explorer.exe", + UseShellExecute = false + }; + startInfo.ArgumentList.Add(path); + Process.Start(startInfo); + } + else + { + // For other OS, we might still need UseShellExecute for some scenarios, + // but we should be careful. MAUI doesn't have a direct "OpenFolder" that works everywhere. + Process.Start(new ProcessStartInfo + { + FileName = path, + UseShellExecute = true + }); + } + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open folder: {path}", ex); + } + } + + /// + /// Specifically for Windows, opens explorer and selects a file. + /// + public static void OpenExplorerAndSelect(string filePath) + { + if (!OperatingSystem.IsWindows() || string.IsNullOrWhiteSpace(filePath)) return; + + try + { + var startInfo = new ProcessStartInfo + { + FileName = "explorer.exe", + UseShellExecute = false + }; + startInfo.ArgumentList.Add("/select," + filePath); + Process.Start(startInfo); + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to open explorer and select: {filePath}", ex); + } + } + + /// + /// Launches an executable with UseShellExecute = false. + /// + public static void LaunchApp(string exePath, IEnumerable? arguments = null) + { + if (string.IsNullOrWhiteSpace(exePath)) return; + + if (!Path.IsPathRooted(exePath) || !File.Exists(exePath)) + { + AppFileLog.Error($"Blocked attempt to launch unverified or non-absolute executable: {exePath}"); + return; + } + + try + { + var startInfo = new ProcessStartInfo + { + FileName = exePath, + UseShellExecute = false + }; + + if (arguments != null) + { + foreach (var arg in arguments) + { + startInfo.ArgumentList.Add(arg); + } + } + + Process.Start(startInfo); + } + catch (Exception ex) + { + AppFileLog.Error($"Failed to launch app: {exePath}", ex); + } + } +} From e70de3e2eeafb7007c224dac700948ea962be5ca Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Thu, 21 May 2026 14:00:43 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=92=20Fix=20Command/Argument=20Inj?= =?UTF-8?q?ection=20Vulnerability=20in=20SafeProcess.LaunchApp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/scripts/linux/build-avalonia-packages.sh | 2 +- global.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 build/scripts/linux/build-avalonia-packages.sh diff --git a/build/scripts/linux/build-avalonia-packages.sh b/build/scripts/linux/build-avalonia-packages.sh old mode 100644 new mode 100755 index 6a35814..8a3b868 --- a/build/scripts/linux/build-avalonia-packages.sh +++ b/build/scripts/linux/build-avalonia-packages.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" VERSION="${2:-1.1.0}" diff --git a/global.json b/global.json index c071e6f..badcd44 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.313", + "version": "10.0.103", "rollForward": "minor" } }