* Harden Windows command wrapper resolution
* clawsweeper: route Windows cmd.exe wrapper through getWindowsInstallRoots
Replace the local SystemRoot/windir/SYSTEMROOT/WINDIR scan in
resolveTrustedWindowsCmdExe with the shared getWindowsInstallRoots()
resolver from src/infra/windows-install-roots.ts. The shared resolver
already rejects UNC paths, root-relative values, semicolon-delimited
path-lists, and missing-drive-letter roots, and prefers registry-derived
roots over env, so the wrapper-launch trust boundary now matches the
existing Windows install-root boundary on main.
Tests:
- _resetWindowsInstallRootsForTests in beforeEach so cached roots track
per-test process.env mutations
- expectedTrustedCmdExe helper now joins the resolved systemRoot, so the
expected wrapper executable matches the production resolver on Linux
CI (where it falls back to DEFAULT_WINDOWS_SYSTEM_ROOT)
- new "rejects unsafe Windows root values" test covers UNC,
semicolon-delimited path-list, root-relative, and bare-relative
SystemRoot inputs
* Add CHANGELOG entry for #77472 Windows command wrapper hardening
* clawsweeper: stub registry probe in Windows wrapper tests
On real Windows CI runners getWindowsInstallRoots() reads the canonical
SystemRoot from the registry (e.g. C:\WINDOWS) before falling back to
process.env, which shadowed the env-only setup in the ComSpec-poisoning
and unsafe-root tests and produced casing mismatches like
"C:\WINDOWS\System32\cmd.exe" vs the expected "C:\Windows\...". Pass a
queryRegistryValue stub returning null in beforeEach (and inside the
unsafe-root loop) so install-root resolution is fully driven by the
test's process.env setup on every platform.
* clawsweeper: overwrite WINDIR alongside SystemRoot in unsafe-root test
Real Windows runners did not honor `delete process.env.windir`, so the
unsafe-root iteration's WINDIR fallback still resolved to the canonical
`C:\WINDOWS` and produced a casing mismatch against the expected default
`C:\Windows\System32\cmd.exe`. Set both `SystemRoot` and `WINDIR` to the
unsafe payload so every install-root env source is rejected by
`normalizeWindowsInstallRoot` and the resolver falls through to
`DEFAULT_WINDOWS_SYSTEM_ROOT`.