[CmdletBinding()] param( [switch]$CheckOnly ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $RepoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..")) $AllowedPowerShellFiles = @( "scripts/audit-windows-smoke.ps1", "scripts/check-runtime-powershell-boundary.ps1", "scripts/prepare-release.ps1", "scripts/update-component-bundle.ps1" ) $ExpectedNsisFlags = @( "--nsis-uninstall-managed", "--nsis-verify-upgrade" ) $IgnoredPathPattern = '^(?:\.git|node_modules|dist|releases|src-tauri/target)(?:/|$)' function Get-RelativeRepoPath { param([string]$Path) $rootUri = [Uri]($RepoRoot.TrimEnd("\", "/") + [IO.Path]::DirectorySeparatorChar) $pathUri = [Uri][IO.Path]::GetFullPath($Path) [Uri]::UnescapeDataString($rootUri.MakeRelativeUri($pathUri).ToString()).Replace("\", "/") } function New-Violation { param( [string]$Rule, [string]$Path, [string]$Message, [int]$Line = 0 ) [ordered]@{ rule = $Rule path = $Path line = $Line message = $Message } } function Get-ProductionLines { param([string]$Path) $lines = @(Get-Content -LiteralPath $Path) for ($index = 0; $index -lt $lines.Count; $index++) { if ($lines[$index] -match '^\s*#\s*\[\s*cfg\s*\(\s*test\s*\)\s*\]') { if ($index -eq 0) { return @() } return @($lines[0..($index - 1)]) } } return $lines } function Write-Result { param( [bool]$Success, [string]$Message, [object[]]$Violations, [int]$PowerShellFileCount, [int]$ProductionFileCount, [string[]]$ObservedNsisFlags ) [ordered]@{ success = $Success action = "runtime-powershell-boundary.check" changed = $false message = $Message details = [ordered]@{ allowlistedPowerShellFiles = $AllowedPowerShellFiles scannedPowerShellFileCount = $PowerShellFileCount scannedProductionFileCount = $ProductionFileCount expectedNsisFlags = $ExpectedNsisFlags observedNsisFlags = $ObservedNsisFlags violations = $Violations } } | ConvertTo-Json -Depth 8 } $violations = New-Object System.Collections.Generic.List[object] $powerShellFileCount = 0 $productionFileCount = 0 $observedNsisFlags = @() try { if (-not $CheckOnly) { [void]$violations.Add((New-Violation ` -Rule "check-only-required" ` -Path "scripts/check-runtime-powershell-boundary.ps1" ` -Message "Invoke this read-only boundary as -CheckOnly.")) } $powerShellFiles = @( Get-ChildItem -LiteralPath $RepoRoot -Recurse -File | Where-Object { $_.Extension -in @(".ps1", ".psm1", ".psd1") } | ForEach-Object { [ordered]@{ fullPath = $_.FullName relativePath = Get-RelativeRepoPath -Path $_.FullName } } | Where-Object { $_.relativePath -notmatch $IgnoredPathPattern } | Sort-Object relativePath ) $powerShellFileCount = $powerShellFiles.Count foreach ($file in $powerShellFiles) { if ($file.relativePath -notin $AllowedPowerShellFiles) { [void]$violations.Add((New-Violation ` -Rule "unexpected-powershell-file" ` -Path $file.relativePath ` -Message "PowerShell is allowed only for the exact build/release/QA allowlist.")) } } foreach ($allowedPath in $AllowedPowerShellFiles) { if ($allowedPath -notin $powerShellFiles.relativePath) { [void]$violations.Add((New-Violation ` -Rule "missing-allowlisted-tool" ` -Path $allowedPath ` -Message "Required build/release/QA tool is missing.")) } } $forbiddenRuntimeFiles = @( "src-tauri/src/elevated_scripts.rs", "src-tauri/src/helper.rs", "src-tauri/src/powershell.rs", "src-tauri/src/proxifyre_scripts.rs", "src-tauri/bundled/cleanup/uninstall-managed-components.ps1" ) foreach ($relativePath in $forbiddenRuntimeFiles) { if (Test-Path -LiteralPath (Join-Path $RepoRoot $relativePath.Replace("/", "\"))) { [void]$violations.Add((New-Violation ` -Rule "legacy-runtime-file" ` -Path $relativePath ` -Message "Legacy runtime PowerShell owner must be deleted after the native cutover.")) } } $tauriConfigPath = Join-Path $RepoRoot "src-tauri\tauri.conf.json" if ((Get-Content -LiteralPath $tauriConfigPath -Raw) -match '(?i)bundled[\\/]cleanup') { [void]$violations.Add((New-Violation ` -Rule "bundled-cleanup-resource" ` -Path "src-tauri/tauri.conf.json" ` -Message "The installer must not package the displaced PowerShell cleanup resource.")) } $productionFiles = @( Get-ChildItem -LiteralPath (Join-Path $RepoRoot "src-tauri\src") -Recurse -File -Filter "*.rs" Get-ChildItem -LiteralPath (Join-Path $RepoRoot "src-tauri\bundled\installer-hooks") -Recurse -File | Where-Object { $_.Extension -in @(".nsh", ".nsi") } ) $productionFileCount = $productionFiles.Count $rules = @( [ordered]@{ name = "powershell-process"; pattern = '(?i)(?:command_no_window|Command::new).*\b(?:powershell|pwsh)(?:\.exe)?\b' }, [ordered]@{ name = "powershell-command-line"; pattern = '(?i)\b(?:powershell|pwsh)(?:\.exe)?\b\s+-[A-Za-z]' }, [ordered]@{ name = "powershell-policy-bypass"; pattern = '(?i)-ExecutionPolicy\b' }, [ordered]@{ name = "powershell-script-path"; pattern = '(?i)\.ps1\b' }, [ordered]@{ name = "powershell-runtime-helper"; pattern = '(?i)\b(?:run|write)_powershell_(?:command|file|script)\b' }, [ordered]@{ name = "legacy-module-declaration"; pattern = '(?i)\b(?:pub\s+)?mod\s+(?:elevated_scripts|helper|powershell|proxifyre_scripts)\s*;' }, [ordered]@{ name = "legacy-module-reexport"; pattern = '(?i)\bpub\s+use\s+crate::(?:elevated_scripts|helper|powershell|proxifyre_scripts)\b' } ) $productionTextParts = New-Object System.Collections.Generic.List[string] $rustTextParts = New-Object System.Collections.Generic.List[string] foreach ($file in $productionFiles) { $relativePath = Get-RelativeRepoPath -Path $file.FullName $lines = @(Get-ProductionLines -Path $file.FullName) for ($index = 0; $index -lt $lines.Count; $index++) { $line = [string]$lines[$index] [void]$productionTextParts.Add($line) if ($file.Extension -ieq ".rs") { [void]$rustTextParts.Add($line) } foreach ($rule in $rules) { if ($line -match $rule.pattern) { [void]$violations.Add((New-Violation ` -Rule $rule.name ` -Path $relativePath ` -Line ($index + 1) ` -Message "Production code still contains a PowerShell runtime boundary.")) } } } } $productionText = $productionTextParts -join "`n" $rustText = $rustTextParts -join "`n" $observedNsisFlags = @( [regex]::Matches($productionText, '--nsis-[a-z0-9-]+', [Text.RegularExpressions.RegexOptions]::IgnoreCase) | ForEach-Object { $_.Value.ToLowerInvariant() } | Sort-Object -Unique ) foreach ($flag in $ExpectedNsisFlags) { if (-not $rustText.Contains($flag)) { [void]$violations.Add((New-Violation ` -Rule "missing-nsis-runtime-mode" ` -Path "src-tauri/src" ` -Message "Rust early-mode parser is missing fixed NSIS mode: $flag")) } } foreach ($flag in $observedNsisFlags) { if ($flag -notin $ExpectedNsisFlags) { [void]$violations.Add((New-Violation ` -Rule "unexpected-nsis-mode" ` -Path "src-tauri" ` -Message "Unexpected reserved NSIS early mode: $flag")) } } $hookPath = Join-Path $RepoRoot "src-tauri\bundled\installer-hooks\proxywarden-hooks.nsh" $hookText = Get-Content -LiteralPath $hookPath -Raw foreach ($flag in $ExpectedNsisFlags) { if (-not $hookText.Contains($flag)) { [void]$violations.Add((New-Violation ` -Rule "missing-nsis-hook-mode" ` -Path "src-tauri/bundled/installer-hooks/proxywarden-hooks.nsh" ` -Message "Installer hook does not call fixed early mode: $flag")) } } $success = $violations.Count -eq 0 $message = if ($success) { "Runtime PowerShell boundary is clean." } else { "Runtime PowerShell boundary has $($violations.Count) violation(s)." } Write-Result ` -Success $success ` -Message $message ` -Violations $violations.ToArray() ` -PowerShellFileCount $powerShellFileCount ` -ProductionFileCount $productionFileCount ` -ObservedNsisFlags $observedNsisFlags if (-not $success) { exit 1 } } catch { $failure = New-Violation ` -Rule "checker-error" ` -Path "scripts/check-runtime-powershell-boundary.ps1" ` -Message $_.Exception.Message Write-Result ` -Success $false ` -Message "Runtime PowerShell boundary check could not complete." ` -Violations @($failure) ` -PowerShellFileCount $powerShellFileCount ` -ProductionFileCount $productionFileCount ` -ObservedNsisFlags $observedNsisFlags exit 1 }