Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 14 additions & 30 deletions cmd/audit_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}
}
Expand Down Expand Up @@ -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}
Comment thread
yummybomb marked this conversation as resolved.
}

func parseAuditLogTime(value string) (time.Time, error) {
Expand Down
53 changes: 14 additions & 39 deletions cmd/audit_logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
},
}
Expand All @@ -170,11 +170,15 @@ 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{
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()
},
}
Expand All @@ -189,7 +193,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()
},
}
Expand All @@ -203,8 +207,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}
Expand All @@ -214,14 +221,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()
},
}
Expand Down Expand Up @@ -255,37 +261,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{}}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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.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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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.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=
Expand Down
Loading