From f53b93087f2198414256c9e615e3e8d5d2d559ff Mon Sep 17 00:00:00 2001 From: Dustin <204417361+Koraji95-coder@users.noreply.github.com> Date: Sat, 23 May 2026 18:00:20 -0500 Subject: [PATCH] fix(start.ps1): TryParse HERMES_WEBUI_PORT + exit AFTER try/finally cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two hardening fixes for start.ps1 — both flagged by Copilot's review on PR #2806, both touching lines that exist in #2783's scope rather than #2806's docs change, so split into this focused follow-up PR per the "keep diffs focused" guidance. ## Fix 1: TryParse on HERMES_WEBUI_PORT Before: $PortFinal = if ($Port) { $Port } elseif ($env:HERMES_WEBUI_PORT) { [int]$env:HERMES_WEBUI_PORT } else { 8787 } `[int]$env:HERMES_WEBUI_PORT` throws InvalidCastException with no actionable context when the env var contains a non-integer value (typo, accidental shell expansion, etc.). The cast also doesn't range-check — HERMES_WEBUI_PORT=999999 would pass the cast and then fail at the network layer with a confusing port-out-of-range error from Python's socket.bind(). After: explicit TryParse + range guard (1-65535), each failure mode gets a targeted Write-Error naming the offending value and the remedy: HERMES_WEBUI_PORT='abc' is not a valid integer port. Unset the variable to use the default (8787), or set it to a number 1-65535. HERMES_WEBUI_PORT=99999 is out of TCP-port range. Must be 1-65535. ## Fix 2: exit AFTER try/finally cleanup Before: Push-Location $RepoRoot try { & $Python $serverPath @args exit $LASTEXITCODE } finally { Pop-Location } `exit` inside the `try` block can prevent the `finally` from running in some termination paths (notably when the script is dot-sourced or invoked from an interactive PowerShell session). The result is a caller stuck at $RepoRoot instead of their original working directory. After: capture the exit code into a scoped variable, let the finally run cleanup, exit after the try/finally completes: $script:serverExitCode = 0 Push-Location $RepoRoot try { & $Python $serverPath @args $script:serverExitCode = $LASTEXITCODE } finally { Pop-Location } exit $script:serverExitCode ## Smoke test Verified all three port-error paths against the modified script on a Windows 11 Pro foundry-side workstation (PowerShell 7.5.4): Test A: HERMES_WEBUI_PORT='abc' -> exit 1 + 'not a valid integer port' PASS Test B: HERMES_WEBUI_PORT='99999' -> exit 1 + 'out of TCP-port range' PASS Test C: HERMES_WEBUI_PORT='0' -> exit 1 + 'out of TCP-port range' PASS Test environment used HERMES_WEBUI_AGENT_DIR override to bypass agent- discovery (this branch is based on #2783's 2-path candidate list which doesn't include the LOCALAPPDATA install path that #2805 adds; the hardening fix itself is orthogonal to discovery). Stacks on top of #2783. Once #2783 lands, rebases cleanly onto master. Co-Authored-By: Claude Opus 4.7 (1M context) --- start.ps1 | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/start.ps1 b/start.ps1 index 191db6c1..9512937d 100644 --- a/start.ps1 +++ b/start.ps1 @@ -119,7 +119,26 @@ if (Test-Path $agentVenvPython) { # === Resolve bind + state defaults ===================================== $BindHostFinal = if ($BindHost) { $BindHost } elseif ($env:HERMES_WEBUI_HOST) { $env:HERMES_WEBUI_HOST } else { '127.0.0.1' } -$PortFinal = if ($Port) { $Port } elseif ($env:HERMES_WEBUI_PORT) { [int]$env:HERMES_WEBUI_PORT } else { 8787 } +$PortFinal = if ($Port) { + $Port +} elseif ($env:HERMES_WEBUI_PORT) { + # TryParse + range guard on the env var. A plain [int] cast on the + # env var throws InvalidCastException with no actionable context when + # the env var is set to a non-integer (typo, accidental shell + # expansion, etc.) — surface a targeted error message instead. + $parsedPort = 0 + if (-not [int]::TryParse($env:HERMES_WEBUI_PORT, [ref]$parsedPort)) { + Write-Error "HERMES_WEBUI_PORT='$($env:HERMES_WEBUI_PORT)' is not a valid integer port. Unset the variable to use the default (8787), or set it to a number 1-65535." + exit 1 + } + if ($parsedPort -lt 1 -or $parsedPort -gt 65535) { + Write-Error "HERMES_WEBUI_PORT=$parsedPort is out of TCP-port range. Must be 1-65535." + exit 1 + } + $parsedPort +} else { + 8787 +} $env:HERMES_WEBUI_HOST = $BindHostFinal $env:HERMES_WEBUI_PORT = "$PortFinal" if (-not $env:HERMES_WEBUI_STATE_DIR) { @@ -147,10 +166,17 @@ if (-not (Test-Path $serverPath)) { exit 1 } +# Capture exit code, let finally{} run Pop-Location, exit AFTER the try. +# Plain `exit $LASTEXITCODE` inside the try block can prevent the finally +# from running in some termination paths (especially when dot-sourced or +# in interactive sessions), leaving the caller's working directory stuck +# at $RepoRoot. +$script:serverExitCode = 0 Push-Location $RepoRoot try { & $Python $serverPath @args - exit $LASTEXITCODE + $script:serverExitCode = $LASTEXITCODE } finally { Pop-Location } +exit $script:serverExitCode