Release 2.7.3.1: harden proxy recovery, updates and local API.

Restore orphaned system proxy after crash, require update SHA-256, add macOS /api auth token, fix UDP ping false positives, HTTPS-only subscriptions, and keep the UI responsive during connect.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
M4
2026-07-29 18:48:54 +03:00
co-authored by Cursor
parent dc700f2bac
commit 041cbb1250
32 changed files with 346 additions and 108 deletions
+10 -1
View File
@@ -82,7 +82,16 @@ func (c *darwinController) Disable() error {
if !c.enabled {
return nil
}
services := c.services
return c.disableServices(c.services)
}
func (c *darwinController) ForceDisable() error {
c.mu.Lock()
defer c.mu.Unlock()
return c.disableServices(nil)
}
func (c *darwinController) disableServices(services []string) error {
if len(services) == 0 {
var err error
services, err = listNetworkServices()
+1
View File
@@ -9,3 +9,4 @@ func newPlatform() Controller { return stubController{} }
func (stubController) Enable(string) error { return ErrUnsupported }
func (stubController) Disable() error { return nil }
func (stubController) Enabled() bool { return false }
func (stubController) ForceDisable() error { return nil }
+39
View File
@@ -0,0 +1,39 @@
package sysproxy
import (
"os"
"path/filepath"
"strings"
)
// SentinelPath is written while Navis owns the OS system proxy.
// Survives crash/kill so the next launch can clear a stuck proxy.
func SentinelPath(cfgPath string) string {
dir := filepath.Dir(strings.TrimSpace(cfgPath))
if dir == "" || dir == "." {
dir, _ = os.UserConfigDir()
if dir == "" {
dir = os.TempDir()
}
dir = filepath.Join(dir, "Navis")
}
return filepath.Join(dir, ".navis-proxy-on")
}
// WriteSentinel marks that system proxy was enabled by Navis.
func WriteSentinel(cfgPath, httpHostPort string) {
path := SentinelPath(cfgPath)
_ = os.MkdirAll(filepath.Dir(path), 0o755)
_ = os.WriteFile(path, []byte(strings.TrimSpace(httpHostPort)+"\n"), 0o600)
}
// ClearSentinel removes the ownership marker after a clean Disable.
func ClearSentinel(cfgPath string) {
_ = os.Remove(SentinelPath(cfgPath))
}
// HasSentinel reports whether a previous session left the proxy marked on.
func HasSentinel(cfgPath string) bool {
_, err := os.Stat(SentinelPath(cfgPath))
return err == nil
}
+3
View File
@@ -7,6 +7,9 @@ type Controller interface {
Enable(httpHostPort string) error
Disable() error
Enabled() bool
// ForceDisable turns off proxy settings even if this process did not Enable them
// (used to recover after crash/kill when a sentinel file remains).
ForceDisable() error
}
// New returns a platform-specific controller.
+19 -2
View File
@@ -95,6 +95,14 @@ func (c *windowsController) Disable() error {
if !c.enabled {
return nil
}
return c.disableLocked(true)
}
func (c *windowsController) ForceDisable() error {
return c.disableLocked(false)
}
func (c *windowsController) disableLocked(restoreSaved bool) error {
key, err := registry.OpenKey(registry.CURRENT_USER,
`Software\Microsoft\Windows\CurrentVersion\Internet Settings`,
registry.QUERY_VALUE|registry.SET_VALUE)
@@ -103,7 +111,7 @@ func (c *windowsController) Disable() error {
}
defer key.Close()
if c.hadProxy {
if restoreSaved && c.hadProxy {
_ = key.SetDWordValue("ProxyEnable", uint32(c.oldEnable))
if c.oldServer != "" {
_ = key.SetStringValue("ProxyServer", c.oldServer)
@@ -114,12 +122,21 @@ func (c *windowsController) Disable() error {
_ = key.SetStringValue("ProxyOverride", c.oldOverride)
}
} else {
_ = key.SetDWordValue("ProxyEnable", 0)
// Crash recovery: if proxy still points at local Navis ports, clear it.
server, _, _ := key.GetStringValue("ProxyServer")
lower := strings.ToLower(server)
if strings.Contains(lower, "127.0.0.1") || strings.Contains(lower, "localhost") {
_ = key.SetDWordValue("ProxyEnable", 0)
_ = key.DeleteValue("ProxyServer")
} else if !restoreSaved {
_ = key.SetDWordValue("ProxyEnable", 0)
}
}
_ = notifyInternetSettingsChanged()
c.enabled = false
c.hadProxy = false
return nil
}