fix(desktop): harden Windows installs against Defender block and orphaned Node - #4382
Merged
Conversation
…two-step shape Windows Defender's ML classifier (Trojan:Win32/Commando.A!ml) flags the bare `irm <url> | iex` command line as a dropper signature and denies the spawn with 'Access is denied. (os error 5)' before PowerShell runs. The block is sticky — Allow does not clear it. Replace all three Windows CLI install commands (Goose, Claude, Codex) with a two-step shape: download the vendor script to a named temp file with Invoke-RestMethod, then execute the file. Two invariants guard against the #2892 regression (success-on-download-failure): - $ErrorActionPreference='Stop' aborts on a failed download instead of falling through to a missing file and exiting 0. - exit $LASTEXITCODE propagates the vendor script's own exit code so a vendor failure of 3 does not flatten to 1. A single macro (windows_install_command!) in a new discovery/windows_install.rs submodule generates all three strings at compile time. One definition means the security shape cannot drift between runtimes as URLs change; a per-runtime literal would let one entry silently regress to irm|iex. Goose and Claude escaped by scoring under the classifier threshold — that is luck, not design. All three are hardened here. The three agent_discovery.rs test fixtures that pinned catalog command strings are updated to the new two-step shape. The routing and argv-parsing tests (is_powershell_command, install_powershell_command) that use irm|iex as representative PS input are unchanged — they test the routing function, not the catalog. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96
force-pushed
the
duncan/windows-install-hardening
branch
6 times, most recently
from
August 3, 2026 00:05
c86aae3 to
12ae0ee
Compare
Contributor
|
defender eating the windows install is a real footgun — hardening that path is overdue. will be curious how the orphaned-node cleanup behaves on a dirty upgrade. |
After a Node version pin bump (v24.11.0 → v24.18.0), existing installs keep the old directory on disk. The shims in the managed npm prefix remain as files, so resolve_command finds them and treats the adapter as installed. The shims then fail at run time with 'node not recognized' because they reference the now-gone old Node path. Two changes address this: 1. managed_node_runtime_ready() now delegates to probe_node(), a bounded seam that runs node --version with a 3-second process deadline (spawn + try_wait loop) using a temp-file stdout transport. The temp-file transport removes any dependency on inherited pipe handles: a descendant retaining a stdout write-end cannot cause probe_node to hang. The child runs in its own process group (Unix) or is terminated via taskkill /T /F (Windows) so all descendants are killed unconditionally on every exit path. 2. A new resolve_adapter_path() helper replaces the inline resolve_command call in install_acp_runtime_blocking. It resolves the candidate path first, then calls should_invalidate_adapter() — a pure seam that returns true only when the resolved path is under the Buzz-managed npm prefix AND the managed Node runtime is orphaned. External adapters outside the managed prefix are always preserved; only managed-prefix shims are invalidated when Node is orphaned. This forces plan_adapter_install to schedule a reinstall, which triggers ensure_managed_node_runtime_blocking to re-download the correct Node version before npm reinstalls the adapter shims. The re-download path already existed in ensure_managed_node_runtime_blocking; the gap was that it was only reachable when the adapter appeared missing. This change makes a stale Node dir the sufficient condition for triggering it, not only a missing adapter shim. Absence detection (the file-existence check in managed_node_runtime_ready) heals an orphaned Node dir; run-verification (the node --version probe) heals shims that exist but point at the wrong binary. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96
force-pushed
the
duncan/windows-install-hardening
branch
from
August 3, 2026 15:22
12ae0ee to
67d68e8
Compare
wesbillman
approved these changes
Aug 3, 2026
wpfleger96
pushed a commit
that referenced
this pull request
Aug 3, 2026
…ed-unread * origin/main: (44 commits) chore(release): release Buzz Desktop version 0.5.4 (#4562) test(mobile): assert follow boundary semantics (#4559) docs(release): align desktop handoff instructions (#3988) fix: report agent usage per provider round, not once per turn (#4545) fix(desktop): harden Windows installs against Defender block and orphaned Node (#4382) feat(desktop): improve channel template discovery (#4549) fix(desktop): save key backups to authorized path (#4022) Add channel activity hover menu (#3935) feat(desktop): show saved Run on settings when editing an agent (#4539) fix(desktop): disambiguate provider API key labels and annotate mint key (#4406) fix(desktop): make OpenAI key re-enterable after first save in card mint dialog (#4140) fix(config-bridge): add harness-definition env tier and fix equal-value model override (#3580) Polish mobile composer and messaging UI (#3918) ci(linux): enable mesh-llm feature in Linux release and canary builds (#4524) fix(desktop): stop the create-agent provider config probe from erasing keystrokes (#4411) fix(mobile): recover and pace live subscriptions (#3053) feat(acp): deliver system prompt via _meta.systemPrompt for claude-agent-acp (#4395) fix(security): bump nostr crates for RUSTSEC-2026-0225..0232 + default sprig image to published digest (#4392) fix(desktop): back/forward via keyboard chords, mouse X1/X2 buttons, and swipe gestures (#3778) feat(k8s): Kubernetes backend plugin + desktop deploy path (#4289) ... Signed-off-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
wpfleger96
added a commit
that referenced
this pull request
Aug 3, 2026
Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> * origin/main: (40 commits) fix(mobile): recover stale relay sessions (#4372) chore(release): release Buzz Desktop version 0.5.4 (#4562) test(mobile): assert follow boundary semantics (#4559) docs(release): align desktop handoff instructions (#3988) fix: report agent usage per provider round, not once per turn (#4545) fix(desktop): harden Windows installs against Defender block and orphaned Node (#4382) feat(desktop): improve channel template discovery (#4549) fix(desktop): save key backups to authorized path (#4022) Add channel activity hover menu (#3935) feat(desktop): show saved Run on settings when editing an agent (#4539) fix(desktop): disambiguate provider API key labels and annotate mint key (#4406) fix(desktop): make OpenAI key re-enterable after first save in card mint dialog (#4140) fix(config-bridge): add harness-definition env tier and fix equal-value model override (#3580) Polish mobile composer and messaging UI (#3918) ci(linux): enable mesh-llm feature in Linux release and canary builds (#4524) fix(desktop): stop the create-agent provider config probe from erasing keystrokes (#4411) fix(mobile): recover and pace live subscriptions (#3053) feat(acp): deliver system prompt via _meta.systemPrompt for claude-agent-acp (#4395) fix(security): bump nostr crates for RUSTSEC-2026-0225..0232 + default sprig image to published digest (#4392) fix(desktop): back/forward via keyboard chords, mouse X1/X2 buttons, and swipe gestures (#3778) ...
wpfleger96
added a commit
that referenced
this pull request
Aug 3, 2026
Syncs Cargo.lock and ci.yml updates from origin/main via the umbrella, which resolves the RUSTSEC-2026-0225..0229 nostr advisory failures in the Security gate. Co-authored-by: Will Pfleger <pfleger.will@gmail.com> Signed-off-by: Will Pfleger <pfleger.will@gmail.com> * commit 'ad9ae1dbb0c2d9b6839c1e18d6fd6a78238b61fb': (40 commits) fix(mobile): recover stale relay sessions (#4372) chore(release): release Buzz Desktop version 0.5.4 (#4562) test(mobile): assert follow boundary semantics (#4559) docs(release): align desktop handoff instructions (#3988) fix: report agent usage per provider round, not once per turn (#4545) fix(desktop): harden Windows installs against Defender block and orphaned Node (#4382) feat(desktop): improve channel template discovery (#4549) fix(desktop): save key backups to authorized path (#4022) Add channel activity hover menu (#3935) feat(desktop): show saved Run on settings when editing an agent (#4539) fix(desktop): disambiguate provider API key labels and annotate mint key (#4406) fix(desktop): make OpenAI key re-enterable after first save in card mint dialog (#4140) fix(config-bridge): add harness-definition env tier and fix equal-value model override (#3580) Polish mobile composer and messaging UI (#3918) ci(linux): enable mesh-llm feature in Linux release and canary builds (#4524) fix(desktop): stop the create-agent provider config probe from erasing keystrokes (#4411) fix(mobile): recover and pace live subscriptions (#3053) feat(acp): deliver system prompt via _meta.systemPrompt for claude-agent-acp (#4395) fix(security): bump nostr crates for RUSTSEC-2026-0225..0232 + default sprig image to published digest (#4392) fix(desktop): back/forward via keyboard chords, mouse X1/X2 buttons, and swipe gestures (#3778) ...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR fixes two Windows-specific install failures: Windows Defender blocking the bare
irm|iexPowerShell install command, and managed Node shims pointing at a version-bumped (now-absent) Node directory.The Defender block (Trojan:Win32/Commando.A!ml) fires before PowerShell runs and is not clearable via Allow. The Node orphaning means shims in the managed npm prefix resolve but fail at runtime with 'node not recognized' because they reference the deleted old Node path.
Invoke-RestMethodto a named temp file, then execute — to eliminate the dropper signature; a newwindows_install_command!macro indiscovery/windows_install.rsgenerates all three strings at compile time so the shape cannot drift between runtimes$ErrorActionPreference='Stop'aborts on download failure instead of falling through to a missing-file exit-0;exit $LASTEXITCODEpropagates the vendor script's own exit codeprobe_node(executable, expected_version, timeout)as a bounded seam: stdout goes to a temp file (not a pipe) so no exit path can block on an inherited handle; the child runs in its own process group on Unix so an unconditional group SIGKILL on every exit path terminates all descendants; on Windowstaskkill /T /Fprovides the same tree-wide cleanup;managed_node_runtime_ready()is a thin wrapper that resolves the managed Node path and calls the seamresolve_adapter_path()inmanaged_node.rs: resolves the candidate first, then callsshould_invalidate_adapter()— a pure predicate that returnstrueonly when the resolved path is underbuzz_managed_npm_bin_dir()AND the managed Node runtime is orphaned; external adapters outside the managed prefix are always preservedNote: CI cannot reproduce the Defender block (no live Defender ML classifier). Proof of fix is structural — the command shape no longer matches the dropper signature. Canary validation on a real Windows machine with Defender enabled is the definitive check.