From c0ef9ee80c02670b39e53841b481bd7c8c2a9537 Mon Sep 17 00:00:00 2001 From: Sepehr Date: Wed, 14 Jan 2026 13:27:33 -0800 Subject: [PATCH] feat(clientcache): add sort parameters and validation for search options --- .../clientcache/internal/cache/options.go | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/internal/clientcache/internal/cache/options.go b/internal/clientcache/internal/cache/options.go index f57ab71809..1a32c61519 100644 --- a/internal/clientcache/internal/cache/options.go +++ b/internal/clientcache/internal/cache/options.go @@ -5,10 +5,15 @@ package cache import ( stderrors "errors" + "fmt" + "strings" "github.com/hashicorp/go-dbw" ) +// unsafeSortChars contains characters that could break SQL ORDER BY clauses +const unsafeSortChars = " \t\n\r,;()'\"\\-" + type testRefreshWaitChs struct { firstSempahore chan struct{} secondSemaphore chan struct{} @@ -26,6 +31,8 @@ type options struct { withMaxResultSetSize int withTestRefreshWaitChs *testRefreshWaitChs withUseNonPagedListing bool + withSortBy SortBy // validated DB column name + withSortDirection SortDirection // "asc" or "desc" } // Option - how options are passed as args @@ -139,3 +146,35 @@ func WithUseNonPagedListing(b bool) Option { return nil } } + +// WithSort configures sorting for query results. +// Empty sortBy is silently ignored. Empty direction defaults to ascending in the repository layer. +// Validates column against sortableColumns and rejects SQL-unsafe characters. +func WithSort(sortBy SortBy, direction SortDirection, sortableColumns []SortBy) Option { + return func(o *options) error { + if sortBy == SortByDefault { + return nil + } + + switch { + case !sliceContains(sortableColumns, sortBy): + return fmt.Errorf("invalid sort column %q: not allowed for this resource type", sortBy) + case strings.ContainsAny(string(sortBy), unsafeSortChars): + return fmt.Errorf("invalid sort column %q: contains unsafe characters", sortBy) + } + + o.withSortBy = sortBy + o.withSortDirection = direction + return nil + } +} + +// sliceContains checks if a value exists in a slice. +func sliceContains[T comparable](slice []T, val T) bool { + for _, v := range slice { + if v == val { + return true + } + } + return false +}