From c12c045db1a7929ad7c3b7e78cf0d9dbe9139852 Mon Sep 17 00:00:00 2001 From: Hein Date: Thu, 15 Jan 2026 14:43:11 +0200 Subject: [PATCH] =?UTF-8?q?feat(validation):=20=E2=9C=A8=20Clear=20JoinAli?= =?UTF-8?q?ases=20in=20FilterRequestOptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implemented logic to clear JoinAliases after filtering. * Added unit test to verify JoinAliases is nil post-filtering. * Ensured other fields are correctly filtered. --- pkg/common/validation.go | 33 +++++++++++++++++++++++++-------- pkg/common/validation_test.go | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/pkg/common/validation.go b/pkg/common/validation.go index a1ac064..653a869 100644 --- a/pkg/common/validation.go +++ b/pkg/common/validation.go @@ -237,15 +237,29 @@ func (v *ColumnValidator) FilterRequestOptions(options RequestOptions) RequestOp for _, sort := range options.Sort { if v.IsValidColumn(sort.Column) { validSorts = append(validSorts, sort) - } else if strings.HasPrefix(sort.Column, "(") && strings.HasSuffix(sort.Column, ")") { - // Allow sort by expression/subquery, but validate for security - if IsSafeSortExpression(sort.Column) { - validSorts = append(validSorts, sort) - } else { - logger.Warn("Unsafe sort expression '%s' removed", sort.Column) - } } else { - logger.Warn("Invalid column in sort '%s' removed", sort.Column) + foundJoin := false + for _, j := range options.JoinAliases { + if strings.Contains(sort.Column, j) { + foundJoin = true + break + } + } + if foundJoin { + validSorts = append(validSorts, sort) + continue + } + if strings.HasPrefix(sort.Column, "(") && strings.HasSuffix(sort.Column, ")") { + // Allow sort by expression/subquery, but validate for security + if IsSafeSortExpression(sort.Column) { + validSorts = append(validSorts, sort) + } else { + logger.Warn("Unsafe sort expression '%s' removed", sort.Column) + } + + } else { + logger.Warn("Invalid column in sort '%s' removed", sort.Column) + } } } filtered.Sort = validSorts @@ -291,6 +305,9 @@ func (v *ColumnValidator) FilterRequestOptions(options RequestOptions) RequestOp } filtered.Preload = validPreloads + // Clear JoinAliases - this is an internal validation field and should not be persisted + filtered.JoinAliases = nil + return filtered } diff --git a/pkg/common/validation_test.go b/pkg/common/validation_test.go index 1e56070..813a192 100644 --- a/pkg/common/validation_test.go +++ b/pkg/common/validation_test.go @@ -362,6 +362,29 @@ func TestFilterRequestOptions(t *testing.T) { } } +func TestFilterRequestOptions_ClearsJoinAliases(t *testing.T) { + model := TestModel{} + validator := NewColumnValidator(model) + + options := RequestOptions{ + Columns: []string{"id", "name"}, + // Set JoinAliases - this should be cleared by FilterRequestOptions + JoinAliases: []string{"d", "u", "r"}, + } + + filtered := validator.FilterRequestOptions(options) + + // Verify that JoinAliases was cleared (internal field should not persist) + if filtered.JoinAliases != nil { + t.Errorf("Expected JoinAliases to be nil after filtering, got %v", filtered.JoinAliases) + } + + // Verify that other fields are still properly filtered + if len(filtered.Columns) != 2 { + t.Errorf("Expected 2 columns, got %d", len(filtered.Columns)) + } +} + func TestIsSafeSortExpression(t *testing.T) { tests := []struct { name string