From a4aabd1d33770d6199a8cb7bc87f639668617bc9 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 4 Sep 2026 16:08:06 +0200 Subject: fix(packaging): re-running setup no longer re-prompts for firewall access Every run of customInstall showed the "Allow MeshBay through Windows Firewall?" question and, on Yes, a fresh UAC prompt -- an upgrade or repair install would ask again even with all four rules already in place. customInstall now checks first: firewall.ps1 check, unelevated (Get-NetFirewallRule needs no admin, only New/Remove do), exits 0 if every rule already exists. Only a nonzero result reaches the MessageBox and the elevated add. A second run of setup on an already-configured machine now asks nothing. Verified unelevated: check exits 1 and logs which rules are missing on a machine with none of them (the fresh-install case); electron-builder compiles the nsExec::Exec / Pop $0 / ${If} wiring. Node suite 837 pass / 25 skip. Co-Authored-By: Claude Sonnet 5 --- packages/meshbay-client/build/installer.nsh | 34 ++++++++++++++--------- packages/meshbay-node/tests/test_packaging_win.py | 18 ++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) (limited to 'packages') diff --git a/packages/meshbay-client/build/installer.nsh b/packages/meshbay-client/build/installer.nsh index 67d103d..d2307c8 100644 --- a/packages/meshbay-client/build/installer.nsh +++ b/packages/meshbay-client/build/installer.nsh @@ -40,19 +40,27 @@ WriteRegExpandStr HKCU "Environment" "Path" "$1" SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 - ; Firewall. MeshBay.exe and meshbay-node.exe each bind UDP sockets for WebRTC, - ; and Windows prompts "Allow access" the first time each does. A per-user - ; installer cannot pre-create a firewall rule (that needs admin), so offer to - ; run one elevated helper now: one UAC prompt instead of two dialogs mid-use. - ; firewall.ps1 is idempotent and does nothing if the exes are missing. - ${IfNot} ${Silent} - MessageBox MB_YESNO|MB_ICONQUESTION \ - "Allow MeshBay through Windows Firewall now?$\n$\nMeshBay connects to other devices on your local network. Choosing Yes adds the rules in one step (Windows will ask for administrator confirmation). Choosing No is fine too -- Windows will ask you to allow access the first time MeshBay connects." \ - /SD IDYES IDNO mb_skip_fw - ExecShellWait "runas" "${MB_PWSH}" \ - '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\firewall.ps1" add' \ - SW_HIDE - mb_skip_fw: + ; Firewall. MeshBay.exe / meshbay-node.exe (WebRTC) and MeshBay.exe again + ; (LAN cast) each need an inbound allow, and Windows prompts "Allow access" + ; the first time each does. A per-user installer cannot pre-create a + ; firewall rule (that needs admin), so offer one elevated helper: one UAC + ; prompt instead of up to four dialogs spread across first use. + ; + ; Checked first, UNELEVATED (Get-NetFirewallRule needs no admin, only + ; New/Remove do) -- so re-running setup with the rules already in place + ; asks nothing and never pops UAC again. + nsExec::Exec '"${MB_PWSH}" -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\firewall.ps1" check' + Pop $0 + ${If} $0 != 0 + ${IfNot} ${Silent} + MessageBox MB_YESNO|MB_ICONQUESTION \ + "Allow MeshBay through Windows Firewall now?$\n$\nMeshBay connects to other devices on your local network. Choosing Yes adds the rules in one step (Windows will ask for administrator confirmation). Choosing No is fine too -- Windows will ask you to allow access the first time MeshBay connects." \ + /SD IDYES IDNO mb_skip_fw + ExecShellWait "runas" "${MB_PWSH}" \ + '-NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\resources\firewall.ps1" add' \ + SW_HIDE + mb_skip_fw: + ${EndIf} ${EndIf} !macroend diff --git a/packages/meshbay-node/tests/test_packaging_win.py b/packages/meshbay-node/tests/test_packaging_win.py index ba3568c..20a2505 100644 --- a/packages/meshbay-node/tests/test_packaging_win.py +++ b/packages/meshbay-node/tests/test_packaging_win.py @@ -199,6 +199,24 @@ def test_the_installer_offers_one_elevated_firewall_step_instead_of_two_dialogs( assert 'firewall.ps1" add' in install +def test_reinstalling_with_the_rules_already_in_place_asks_nothing(): + """ + Get-NetFirewallRule needs no admin, only New/Remove do — so customInstall + checks first, unelevated, and only reaches the MessageBox (and therefore + the UAC prompt) when something is actually missing. Without this, running + setup a second time — an upgrade, a repair install — would re-ask the + question and re-trigger UAC even though nothing needs to change. + """ + nsh = NSH.read_text(encoding="utf-8") + install = _macro_body(nsh, "customInstall") + + check_line = 'firewall.ps1" check' + assert check_line in install + # The check must run, and be evaluated, before the MessageBox — not after. + assert install.index(check_line) < install.index("MessageBox MB_YESNO") + assert "Pop $0" in install and "${If} $0 != 0" in install + + def test_the_uninstaller_offers_to_remove_the_firewall_rules_default_no(): """Opt-in on the way out too, and defaulting to No: a stale allow-rule for a deleted exe is inert, so this should not nag.""" -- cgit v1.2.3