From 7aaabf4b779e4ebe771b3afad65472f3cccb6351 Mon Sep 17 00:00:00 2001 From: Snider Date: Wed, 15 Apr 2026 22:51:07 +0100 Subject: [PATCH] Harden GUI storage and browser inputs --- pkg/browser/service.go | 3 +++ pkg/browser/service_test.go | 11 +++++++++++ pkg/display/scheme.go | 3 +++ pkg/display/scheme_test.go | 11 +++++++++++ pkg/display/storage.go | 5 ++++- pkg/display/storage_test.go | 13 +++++++++++-- 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/pkg/browser/service.go b/pkg/browser/service.go index 4126f44b..d5b99dae 100644 --- a/pkg/browser/service.go +++ b/pkg/browser/service.go @@ -64,6 +64,9 @@ func validatedOpenURL(raw string) (string, error) { if parsed.Host == "" { return "", coreerr.E("browser.openURL", "url host is required", nil) } + if parsed.User != nil { + return "", coreerr.E("browser.openURL", "url must not include credentials", nil) + } return parsed.String(), nil } diff --git a/pkg/browser/service_test.go b/pkg/browser/service_test.go index 1e7d08d3..a9122ca5 100644 --- a/pkg/browser/service_test.go +++ b/pkg/browser/service_test.go @@ -67,6 +67,17 @@ func TestTaskOpenURL_Bad_Scheme(t *testing.T) { assert.Empty(t, mp.lastURL) } +func TestTaskOpenURL_Bad_Credentials(t *testing.T) { + mp := &mockPlatform{} + _, c := newTestBrowserService(t, mp) + + r := c.Action("browser.openURL").Run(context.Background(), core.NewOptions( + core.Option{Key: "url", Value: "https://user:pass@example.com"}, + )) + assert.False(t, r.OK) + assert.Empty(t, mp.lastURL) +} + func TestTaskOpenURL_Bad_PlatformError(t *testing.T) { mp := &mockPlatform{urlErr: core.NewError("browser not found")} _, c := newTestBrowserService(t, mp) diff --git a/pkg/display/scheme.go b/pkg/display/scheme.go index 5c12fde4..e7008604 100644 --- a/pkg/display/scheme.go +++ b/pkg/display/scheme.go @@ -590,6 +590,9 @@ func (s *Service) renderStoreSearchPage(query string, results []StorageEntry) st } func (s *Service) searchAllStorage(query string) []StorageEntry { + if strings.TrimSpace(query) == "" { + return nil + } results := s.storage.Search(query) if conversations := s.Core().QUERY(chat.QueryConversationSearch{Query: query}); conversations.OK { switch list := conversations.Value.(type) { diff --git a/pkg/display/scheme_test.go b/pkg/display/scheme_test.go index 40cee721..f35c9d7f 100644 --- a/pkg/display/scheme_test.go +++ b/pkg/display/scheme_test.go @@ -172,6 +172,17 @@ func TestScheme_ResolveScheme_Ugly(t *testing.T) { assert.Contains(t, searchPayload["body"].(string), "No matches found in Core storage.") } +func TestScheme_HandleStoreSearch_BlankQueryReturnsNoResults(t *testing.T) { + svc, _ := newTestDisplayService(t) + svc.storage.Set("origin-a", "local", "theme", "dark") + + result := svc.handleStoreSearch(context.Background(), url.Values{}) + require.True(t, result.OK) + payload := result.Value.(map[string]any) + assert.Empty(t, payload["results"]) + assert.Contains(t, payload["body"].(string), "Enter a search term") +} + func TestScheme_ResolveScheme_ServiceBackedRoute_Good(t *testing.T) { c := core.New( core.WithService(Register(nil)), diff --git a/pkg/display/storage.go b/pkg/display/storage.go index 875c673c..7174df51 100644 --- a/pkg/display/storage.go +++ b/pkg/display/storage.go @@ -115,7 +115,7 @@ func storageOriginForPageURL(pageURL string) string { } func makeStorageEntryKey(origin, bucket, key string) string { - return strings.Join([]string{origin, bucket, key}, "\x00") + return storageCompositeKey(origin, bucket, key) } func storageCompositeKey(origin, bucket, key string) string { @@ -278,6 +278,9 @@ func (r *StorageRegistry) Snapshot(pageURL string) map[string]map[string]string defer r.mu.RUnlock() origin := storageOriginForPageURL(pageURL) + if strings.TrimSpace(origin) == "" { + return map[string]map[string]string{} + } snapshot := make(map[string]map[string]string) for _, entry := range r.entries { if origin != "" && !strings.EqualFold(entry.Origin, origin) { diff --git a/pkg/display/storage_test.go b/pkg/display/storage_test.go index b39a7dda..45117419 100644 --- a/pkg/display/storage_test.go +++ b/pkg/display/storage_test.go @@ -11,7 +11,7 @@ import ( ) func storageEntryKey(origin, bucket, key string) string { - return strings.Join([]string{origin, bucket, key}, "\x00") + return makeStorageEntryKey(origin, bucket, key) } func setStorageEntryTime(r *StorageRegistry, origin, bucket, key string, ts time.Time) { @@ -149,6 +149,15 @@ func TestStorage_StorageOriginForPageURL_Ugly(t *testing.T) { assert.Equal(t, "", storageOriginForPageURL(" ")) } +func TestStorage_Snapshot_BlankOriginReturnsEmpty(t *testing.T) { + r := NewStorageRegistry() + r.Set("core://settings", "localStorage", "theme", "dark") + + snapshot := r.Snapshot("") + + assert.Empty(t, snapshot) +} + func TestStorage_CompositeKey_Good(t *testing.T) { key := storageCompositeKey("origin", "bucket", "item") @@ -157,7 +166,7 @@ func TestStorage_CompositeKey_Good(t *testing.T) { assert.Equal(t, "origin", origin) assert.Equal(t, "bucket", bucket) assert.Equal(t, "item", item) - assert.Equal(t, "origin\x00bucket\x00item", makeStorageEntryKey("origin", "bucket", "item")) + assert.Equal(t, key, makeStorageEntryKey("origin", "bucket", "item")) } func TestStorage_CompositeKey_Bad(t *testing.T) {