From e457cd26b7c926deb10adde153ad7010da76d69d Mon Sep 17 00:00:00 2001 From: Madhusudhan Gumbalapura Sudarshan Date: Mon, 15 Jun 2026 17:06:54 -0700 Subject: [PATCH] [SandboxTest] Improve input validation and WSB file generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Strengthen SandboxTest.ps1 parameter handling and WSB configuration output to ensure robust behavior with diverse input paths and options. ## Changes - **MapFolder validation**: Reject folder paths containing characters that are problematic for XML embedding (', ", <, >, &) with a clear error message identifying the forbidden characters - **WinGetOptions validation**: Add a dynamic allow-list that parses valid flags directly from winget install --help at runtime, ensuring the validation stays current with any installed WinGet version. Includes Write-Debug tracing for observability. Gracefully skips validation with a warning when WinGet is not installed on the host - **WSB XML generation**: Apply [System.Security.SecurityElement]::Escape() to all four interpolated paths (TestDataFolder, PrimaryMappedFolder, SandboxWorkingDirectory, SandboxBootstrapFile) before emitting into the .wsb configuration file, producing well-formed XML regardless of path content ## Impact - Paths containing XML-meaningful characters no longer produce malformed .wsb files that fail to launch - Invalid or unrecognized flags passed via -WinGetOptions are caught early with an actionable error message listing all accepted flags - No behavioral change for typical usage — standard paths and valid WinGet flags work exactly as before --- Tools/SandboxTest.ps1 | 46 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/Tools/SandboxTest.ps1 b/Tools/SandboxTest.ps1 index 57121784a733e..b84600af73d4d 100644 --- a/Tools/SandboxTest.ps1 +++ b/Tools/SandboxTest.ps1 @@ -23,6 +23,7 @@ param( [Parameter(HelpMessage = 'The folder to map in the Sandbox.')] [ValidateScript({ if (-not (Test-Path -Path $_ -PathType Container)) { throw "$_ is not a folder." } + if ($_ -match '[''"<>&]') { throw "MapFolder path contains forbidden characters (' `" < > &): $_" } return $true })] [String] $MapFolder = $pwd, @@ -31,6 +32,41 @@ param( [string] $WinGetVersion, # WinGetOptions [Parameter(HelpMessage = 'Additional options for WinGet')] + [ValidateScript({ + # Parse allowed flags only from flag definition lines in winget install --help + # These lines start with whitespace followed by a dash (e.g., " -h,--silent ...") + if (-not (Get-Command 'winget.exe' -ErrorAction SilentlyContinue)) { + Write-Warning 'WinGet is not installed on the host. Skipping WinGetOptions validation.' + return $true + } + $helpLines = winget install --help 2>&1 | Where-Object { $_ -match '^\s+-' } + $allowedFlags = @() + foreach ($line in $helpLines) { + # Extract the flag portion before the description (everything up to two consecutive spaces) + $flagPart = ($line.Trim() -split '\s{2,}')[0] + $parsedTokens = $flagPart -split ',' | ForEach-Object { $_.Trim() } + Write-Debug "WinGetOptions: Parsed line '$($line.Trim())' -> flags: $($parsedTokens -join ', ')" + $allowedFlags += $parsedTokens + } + $allowedFlags = $allowedFlags | Where-Object { $_ } | Select-Object -Unique + if (-not $allowedFlags) { + throw 'Could not parse winget install flags from --help output. Ensure winget is installed.' + } + Write-Debug "WinGetOptions: Allow-list ($($allowedFlags.Count) flags): $($allowedFlags -join ', ')" + $tokens = $_ -split '\s+' + foreach ($token in $tokens) { + if ($token.StartsWith('-')) { + $isAllowed = $token -in $allowedFlags + Write-Debug "WinGetOptions: Checking token '$token' -> $(if ($isAllowed) { 'ALLOWED' } else { 'BLOCKED' })" + if (-not $isAllowed) { + throw "WinGetOptions contains disallowed flag: $token. Allowed flags: $($allowedFlags -join ', ')" + } + } else { + Write-Debug "WinGetOptions: Skipping value token '$token'" + } + } + return $true + })] [string] $WinGetOptions, # Switches [switch] $SkipManifestValidation, @@ -872,19 +908,23 @@ Pop-Location # Create the WSB file # Although this could be done using the native XML processor, it's easier to just write the content directly as a string Write-Verbose 'Creating WSB file for launching the sandbox' +$escapedTestDataFolder = [System.Security.SecurityElement]::Escape($script:TestDataFolder) +$escapedPrimaryMappedFolder = [System.Security.SecurityElement]::Escape($script:PrimaryMappedFolder) +$escapedSandboxWorkingDirectory = [System.Security.SecurityElement]::Escape($script:SandboxWorkingDirectory) +$escapedSandboxBootstrapFile = [System.Security.SecurityElement]::Escape($script:SandboxBootstrapFile) @" Enable - $($script:TestDataFolder) + $($escapedTestDataFolder) - $($script:PrimaryMappedFolder) + $($escapedPrimaryMappedFolder) - PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$($script:SandboxWorkingDirectory)' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $($script:SandboxBootstrapFile)' + PowerShell Start-Process PowerShell -WindowStyle Maximized -WorkingDirectory '$($escapedSandboxWorkingDirectory)' -ArgumentList '-ExecutionPolicy Bypass -NoExit -NoLogo -File $($escapedSandboxBootstrapFile)' Disable Disable