From d2b360de4763fae8c2a4c547fb789afe9c2474de Mon Sep 17 00:00:00 2001 From: yummybomb <19238148+yummybomb@users.noreply.github.com> Date: Fri, 10 Jul 2026 20:33:41 +0000 Subject: [PATCH 1/4] Use multi-method audit log exclusions --- cmd/audit_logs.go | 44 ++++++++++++------------------------- cmd/audit_logs_test.go | 49 +++++++++--------------------------------- go.mod | 2 +- 3 files changed, 25 insertions(+), 70 deletions(-) diff --git a/cmd/audit_logs.go b/cmd/audit_logs.go index c2de397a..4149500e 100644 --- a/cmd/audit_logs.go +++ b/cmd/audit_logs.go @@ -72,18 +72,9 @@ func (c AuditLogsCmd) Search(ctx context.Context, in AuditLogsSearchInput) error if in.Method != "" { params.Method = kernel.String(in.Method) } - // The API accepts a single exclude_method. When the default GET exclusion - // stacks with a user-provided one, GET goes server-side (it drops the most - // rows) and the user's method is filtered client-side. - excludeGetByDefault := in.Method == "" && !in.IncludeGet - var clientExclude string - serverExclude := in.ExcludeMethod - if excludeGetByDefault && !strings.EqualFold(in.ExcludeMethod, "GET") { - clientExclude = in.ExcludeMethod - serverExclude = "GET" - } - if serverExclude != "" { - params.ExcludeMethod = kernel.String(serverExclude) + excludeMethods := auditLogExcludeMethods(in.Method, in.ExcludeMethod, in.IncludeGet) + if len(excludeMethods) > 0 { + params.ExcludeMethod = excludeMethods } if in.Service != "" { params.Service = kernel.String(in.Service) @@ -100,13 +91,9 @@ func (c AuditLogsCmd) Search(ctx context.Context, in AuditLogsSearchInput) error hasMore := false pager := c.auditLogs.ListAutoPaging(ctx, params) for pager.Next() { - entry := pager.Current() - if clientExclude != "" && strings.EqualFold(entry.Method, clientExclude) { - continue - } - entries = append(entries, entry) + entries = append(entries, pager.Current()) if len(entries) >= in.Limit { - hasMore = peekForMore(pager, clientExclude) + hasMore = pager.Next() break } } @@ -143,20 +130,17 @@ func (c AuditLogsCmd) Search(ctx context.Context, in AuditLogsSearchInput) error return nil } -// peekForMore reports whether entries matching the client-side exclusion -// remain past the limit. The scan is capped at one page so a long run of -// excluded entries can't trigger unbounded fetching; hitting the cap assumes -// more may match. -func peekForMore(pager *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry], clientExclude string) bool { - for peeked := 0; peeked < auditLogsMaxPageSize; peeked++ { - if !pager.Next() { - return false - } - if clientExclude == "" || !strings.EqualFold(pager.Current().Method, clientExclude) { - return true +func auditLogExcludeMethods(method, excludeMethod string, includeGet bool) []string { + if method != "" || includeGet { + if excludeMethod == "" { + return nil } + return []string{excludeMethod} + } + if excludeMethod == "" || strings.EqualFold(excludeMethod, "GET") { + return []string{"GET"} } - return true + return []string{"GET", excludeMethod} } func parseAuditLogTime(value string) (time.Time, error) { diff --git a/cmd/audit_logs_test.go b/cmd/audit_logs_test.go index e5fe6667..9c77991a 100644 --- a/cmd/audit_logs_test.go +++ b/cmd/audit_logs_test.go @@ -52,7 +52,7 @@ func TestAuditLogsSearchBuildsParamsAndPrintsTable(t *testing.T) { assert.Equal(t, time.Date(2026, 7, 2, 15, 0, 0, 0, time.UTC), query.End) assert.Equal(t, "browsers", query.Search.Value) assert.Equal(t, "POST", query.Method.Value) - assert.Equal(t, "OPTIONS", query.ExcludeMethod.Value) + assert.Equal(t, []string{"OPTIONS"}, query.ExcludeMethod) assert.Equal(t, "api", query.Service.Value) assert.Equal(t, "api_key", query.AuthStrategy.Value) assert.Equal(t, []string{"user_123", "user_456"}, query.SearchUserID) @@ -160,7 +160,7 @@ func TestAuditLogsSearchExcludesGetByDefault(t *testing.T) { capturePtermOutput(t) fake := &FakeAuditLogsService{ ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - assert.Equal(t, "GET", query.ExcludeMethod.Value) + assert.Equal(t, []string{"GET"}, query.ExcludeMethod) return auditLogPager() }, } @@ -174,7 +174,7 @@ func TestAuditLogsSearchIncludeGetDisablesDefaultExclusion(t *testing.T) { capturePtermOutput(t) fake := &FakeAuditLogsService{ ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - assert.False(t, query.ExcludeMethod.Valid()) + assert.Empty(t, query.ExcludeMethod) return auditLogPager() }, } @@ -189,7 +189,7 @@ func TestAuditLogsSearchMethodDisablesDefaultExclusion(t *testing.T) { fake := &FakeAuditLogsService{ ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { assert.Equal(t, "GET", query.Method.Value) - assert.False(t, query.ExcludeMethod.Valid()) + assert.Empty(t, query.ExcludeMethod) return auditLogPager() }, } @@ -203,8 +203,11 @@ func TestAuditLogsSearchExcludeMethodStacksWithDefaultGetExclusion(t *testing.T) buf := capturePtermOutput(t) fake := &FakeAuditLogsService{ ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - assert.Equal(t, "GET", query.ExcludeMethod.Value) - return auditLogPager(sampleAuditLogEntry("POST", "/posted"), sampleAuditLogEntry("DELETE", "/deleted")) + assert.Equal(t, []string{"GET", "post"}, query.ExcludeMethod) + values, err := query.URLQuery() + require.NoError(t, err) + assert.Equal(t, "GET,post", values.Get("exclude_method")) + return auditLogPager(sampleAuditLogEntry("DELETE", "/deleted")) }, } c := AuditLogsCmd{auditLogs: fake} @@ -214,14 +217,13 @@ func TestAuditLogsSearchExcludeMethodStacksWithDefaultGetExclusion(t *testing.T) out := buf.String() assert.Contains(t, out, "/deleted") - assert.NotContains(t, out, "/posted") } func TestAuditLogsSearchIncludeGetWithExcludeMethodSendsUserExclusion(t *testing.T) { capturePtermOutput(t) fake := &FakeAuditLogsService{ ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - assert.Equal(t, "POST", query.ExcludeMethod.Value) + assert.Equal(t, []string{"POST"}, query.ExcludeMethod) return auditLogPager() }, } @@ -255,37 +257,6 @@ func TestAuditLogsSearchLimitTruncatesResults(t *testing.T) { assert.Contains(t, out, "Showing first 2 results") } -func TestAuditLogsSearchNoTruncationNoticeWhenOnlyExcludedEntriesRemain(t *testing.T) { - buf := capturePtermOutput(t) - fake := &FakeAuditLogsService{ - ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - return auditLogPager(sampleAuditLogEntry("DELETE", "/deleted"), sampleAuditLogEntry("POST", "/posted"), sampleAuditLogEntry("POST", "/posted-too")) - }, - } - c := AuditLogsCmd{auditLogs: fake} - - err := c.Search(context.Background(), AuditLogsSearchInput{ExcludeMethod: "POST", Limit: 1}) - require.NoError(t, err) - - out := buf.String() - assert.Contains(t, out, "/deleted") - assert.NotContains(t, out, "Showing first") -} - -func TestAuditLogsSearchTruncationNoticeWhenMatchRemainsPastExcludedEntries(t *testing.T) { - buf := capturePtermOutput(t) - fake := &FakeAuditLogsService{ - ListAutoPagingFunc: func(ctx context.Context, query kernel.AuditLogListParams, opts ...option.RequestOption) *pagination.PageTokenPaginationAutoPager[kernel.AuditLogEntry] { - return auditLogPager(sampleAuditLogEntry("DELETE", "/first"), sampleAuditLogEntry("POST", "/posted"), sampleAuditLogEntry("DELETE", "/second")) - }, - } - c := AuditLogsCmd{auditLogs: fake} - - err := c.Search(context.Background(), AuditLogsSearchInput{ExcludeMethod: "POST", Limit: 1}) - require.NoError(t, err) - assert.Contains(t, buf.String(), "Showing first 1 results") -} - func TestAuditLogsSearchPrintsEmptyMessage(t *testing.T) { buf := capturePtermOutput(t) c := AuditLogsCmd{auditLogs: &FakeAuditLogsService{}} diff --git a/go.mod b/go.mod index b3939d33..96399c8d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/joho/godotenv v1.5.1 - github.com/kernel/kernel-go-sdk v0.75.0 + github.com/kernel/kernel-go-sdk v0.77.0 github.com/klauspost/compress v1.18.5 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pterm/pterm v0.12.80 From d5ef9910d46415e456824e87c6173f03a57f99a7 Mon Sep 17 00:00:00 2001 From: yummybomb <19238148+yummybomb@users.noreply.github.com> Date: Mon, 13 Jul 2026 13:47:10 +0000 Subject: [PATCH 2/4] Test case-insensitive GET exclusion deduplication --- cmd/audit_logs_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/audit_logs_test.go b/cmd/audit_logs_test.go index 9c77991a..84093932 100644 --- a/cmd/audit_logs_test.go +++ b/cmd/audit_logs_test.go @@ -170,6 +170,10 @@ func TestAuditLogsSearchExcludesGetByDefault(t *testing.T) { require.NoError(t, err) } +func TestAuditLogExcludeMethodsDeduplicatesDefaultGetCaseInsensitively(t *testing.T) { + assert.Equal(t, []string{"GET"}, auditLogExcludeMethods("", "get", false)) +} + func TestAuditLogsSearchIncludeGetDisablesDefaultExclusion(t *testing.T) { capturePtermOutput(t) fake := &FakeAuditLogsService{ From 6c7b69710bfc1ccdc0f252a2e67de817395933ea Mon Sep 17 00:00:00 2001 From: yummybomb <19238148+yummybomb@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:41:30 +0000 Subject: [PATCH 3/4] Update SDK checksums --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index a182f786..d6b9e6a8 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kernel/kernel-go-sdk v0.75.0 h1:UTlBLOVGQIFa0Vcn9DrTbhR44IQRrcZuhaKcMTwHvms= -github.com/kernel/kernel-go-sdk v0.75.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= +github.com/kernel/kernel-go-sdk v0.77.0 h1:c7MGZ1Uv7TQ4LvZ2E7jH8/IoHkoEhuwRPMLy9mKzIQI= +github.com/kernel/kernel-go-sdk v0.77.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= From e459f2508adfa24a4091f0d0510ba298c3f2e79e Mon Sep 17 00:00:00 2001 From: yummybomb <19238148+yummybomb@users.noreply.github.com> Date: Mon, 13 Jul 2026 18:51:59 +0000 Subject: [PATCH 4/4] Update SDK to v0.78.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 96399c8d..35c8884a 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/joho/godotenv v1.5.1 - github.com/kernel/kernel-go-sdk v0.77.0 + github.com/kernel/kernel-go-sdk v0.78.0 github.com/klauspost/compress v1.18.5 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/pterm/pterm v0.12.80 diff --git a/go.sum b/go.sum index d6b9e6a8..8b66df6e 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kernel/kernel-go-sdk v0.77.0 h1:c7MGZ1Uv7TQ4LvZ2E7jH8/IoHkoEhuwRPMLy9mKzIQI= -github.com/kernel/kernel-go-sdk v0.77.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= +github.com/kernel/kernel-go-sdk v0.78.0 h1:ZUmQ7Pg9A0dz8SVosFScYPETg/E4e6y7JtNB30OLPZE= +github.com/kernel/kernel-go-sdk v0.78.0/go.mod h1:EeZzSuHZVeHKxKCPUzxou2bovNGhXaz0RXrSqKNf1AQ= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=