Skip to content

Commit cc7ded5

Browse files
joocursoragent
andcommitted
fix(core): make proxy debug locality configurable
Keep LAN proxy diagnostics available by default while allowing users to opt into loopback-only debug endpoints with proxy.debug_local_only. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 460b01a commit cc7ded5

6 files changed

Lines changed: 54 additions & 23 deletions

File tree

core/internal/buildinfo/buildinfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "strings"
44

55
// Set at link time via -ldflags (see .goreleaser.yaml).
66
var (
7-
Version = "dev0.1.45"
7+
Version = "dev0.1.46"
88
Commit = "none"
99
Date = "unknown"
1010
)

core/internal/desktop/profiles.go

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import (
1010
"github.com/clovapi/switcher/internal/profile"
1111
)
1212

13+
func boolPtr(v bool) *bool {
14+
return &v
15+
}
16+
1317
type UIModel struct {
1418
ID string `json:"id"`
1519
Label string `json:"label"`
@@ -33,9 +37,10 @@ type UIVendor struct {
3337
}
3438

3539
type UIProxyConfig struct {
36-
Enabled bool `json:"enabled"`
37-
Host string `json:"host"`
38-
Port int `json:"port"`
40+
Enabled bool `json:"enabled"`
41+
Host string `json:"host"`
42+
Port int `json:"port"`
43+
DebugLocalOnly *bool `json:"debugLocalOnly,omitempty"`
3944
}
4045

4146
type LoadResult struct {
@@ -145,9 +150,10 @@ func storeToUI(s *profile.Store) LoadResult {
145150
Version: s.Version,
146151
Active: active,
147152
Proxy: UIProxyConfig{
148-
Enabled: s.Proxy.Enabled,
149-
Host: s.Proxy.Host,
150-
Port: s.Proxy.Port,
153+
Enabled: s.Proxy.Enabled,
154+
Host: s.Proxy.Host,
155+
Port: s.Proxy.Port,
156+
DebugLocalOnly: boolPtr(s.Proxy.DebugLocalOnly),
151157
},
152158
Profiles: profiles,
153159
}
@@ -197,9 +203,13 @@ func SaveProfiles(input SaveInput) SaveResult {
197203
}
198204
if input.Proxy != nil {
199205
current.Proxy = profile.ProxyConfig{
200-
Enabled: input.Proxy.Enabled,
201-
Host: strings.TrimSpace(input.Proxy.Host),
202-
Port: input.Proxy.Port,
206+
Enabled: input.Proxy.Enabled,
207+
Host: strings.TrimSpace(input.Proxy.Host),
208+
Port: input.Proxy.Port,
209+
DebugLocalOnly: current.Proxy.DebugLocalOnly,
210+
}
211+
if input.Proxy.DebugLocalOnly != nil {
212+
current.Proxy.DebugLocalOnly = *input.Proxy.DebugLocalOnly
203213
}
204214
}
205215
return true, nil
@@ -229,9 +239,10 @@ func proxyConfigFromStore(s *profile.Store) UIProxyConfig {
229239
return UIProxyConfig{}
230240
}
231241
return UIProxyConfig{
232-
Enabled: s.Proxy.Enabled,
233-
Host: s.Proxy.Host,
234-
Port: s.Proxy.Port,
242+
Enabled: s.Proxy.Enabled,
243+
Host: s.Proxy.Host,
244+
Port: s.Proxy.Port,
245+
DebugLocalOnly: boolPtr(s.Proxy.DebugLocalOnly),
235246
}
236247
}
237248

@@ -248,9 +259,13 @@ func LoadProxyConfig() ProxyConfigResult {
248259
func SaveProxyConfig(input UIProxyConfig) ProxyConfigResult {
249260
saved, err := profile.WithLockedDesktopStore(func(current *profile.Store) (bool, error) {
250261
current.Proxy = profile.ProxyConfig{
251-
Enabled: input.Enabled,
252-
Host: strings.TrimSpace(input.Host),
253-
Port: input.Port,
262+
Enabled: input.Enabled,
263+
Host: strings.TrimSpace(input.Host),
264+
Port: input.Port,
265+
DebugLocalOnly: current.Proxy.DebugLocalOnly,
266+
}
267+
if input.DebugLocalOnly != nil {
268+
current.Proxy.DebugLocalOnly = *input.DebugLocalOnly
254269
}
255270
return true, nil
256271
})

core/internal/profile/types.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ type Model struct {
4646

4747
// ProxyConfig describes the built-in local proxy/daemon endpoint.
4848
type ProxyConfig struct {
49-
Enabled bool `json:"enabled"`
50-
Host string `json:"host"`
51-
Port int `json:"port"`
49+
Enabled bool `json:"enabled"`
50+
Host string `json:"host"`
51+
Port int `json:"port"`
52+
DebugLocalOnly bool `json:"debug_local_only,omitempty"`
5253
}
5354

5455
// ActiveSelection is the persisted provider/model selected for one local agent.

core/internal/proxy/server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func NewServer(cfg profile.ProxyConfig) *Server {
8585

8686
func (s *Server) localOnly(next http.HandlerFunc) http.HandlerFunc {
8787
return func(w http.ResponseWriter, r *http.Request) {
88-
if !isLocalRequest(r) {
88+
if s.Config.DebugLocalOnly && !isLocalRequest(r) {
8989
writeJSON(w, http.StatusForbidden, map[string]string{"error": "debug endpoints are only available from loopback clients"})
9090
return
9191
}

core/internal/proxy/server_test.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,22 @@ func TestDebugCallLogPaginatesDefaultLimit(t *testing.T) {
122122
}
123123
}
124124

125-
func TestDebugRoutesRejectNonLoopbackClients(t *testing.T) {
125+
func TestDebugRoutesAllowNonLoopbackClientsByDefault(t *testing.T) {
126126
s := NewServer(profile.ProxyConfig{Host: "0.0.0.0", Port: 27483})
127+
s.CallLogs = newCallLogStoreAt(t.TempDir())
128+
req := httptest.NewRequest(http.MethodGet, "http://example.com/__debug/call-log", nil)
129+
req.RemoteAddr = "203.0.113.10:45678"
130+
rec := httptest.NewRecorder()
131+
132+
s.Server.Handler.ServeHTTP(rec, req)
133+
134+
if rec.Code != http.StatusOK {
135+
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
136+
}
137+
}
138+
139+
func TestDebugRoutesRejectNonLoopbackClientsWhenLocalOnly(t *testing.T) {
140+
s := NewServer(profile.ProxyConfig{Host: "0.0.0.0", Port: 27483, DebugLocalOnly: true})
127141
req := httptest.NewRequest(http.MethodGet, "http://example.com/__debug/call-log", nil)
128142
req.RemoteAddr = "203.0.113.10:45678"
129143
rec := httptest.NewRecorder()
@@ -135,8 +149,8 @@ func TestDebugRoutesRejectNonLoopbackClients(t *testing.T) {
135149
}
136150
}
137151

138-
func TestDebugRoutesAllowLoopbackClients(t *testing.T) {
139-
s := NewServer(profile.ProxyConfig{Host: "0.0.0.0", Port: 27483})
152+
func TestDebugRoutesAllowLoopbackClientsWhenLocalOnly(t *testing.T) {
153+
s := NewServer(profile.ProxyConfig{Host: "0.0.0.0", Port: 27483, DebugLocalOnly: true})
140154
s.CallLogs = newCallLogStoreAt(t.TempDir())
141155
req := httptest.NewRequest(http.MethodGet, "http://example.com/__debug/call-log", nil)
142156
req.RemoteAddr = "127.0.0.1:45678"

electron/ui/src/global.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ type ProxyConfig = {
9696
enabled?: boolean;
9797
host?: string;
9898
port?: number;
99+
debugLocalOnly?: boolean;
99100
};
100101

101102
type ProxyStatusResult = {

0 commit comments

Comments
 (0)