From 4018af0636908a6467f06863379a2ac0cdd64742 Mon Sep 17 00:00:00 2001 From: Hein Date: Wed, 27 May 2026 12:17:31 +0200 Subject: [PATCH] fix(validation): enhance filter logic for column validation * adjust handling of "all" filter to consider filtered columns fix(function_api): improve variable substitution in SQL queries * add safeSubstituteVar for context-aware value sanitization --- pkg/common/validation.go | 6 +++++- pkg/funcspec/function_api.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pkg/common/validation.go b/pkg/common/validation.go index 3c7e021..0264773 100644 --- a/pkg/common/validation.go +++ b/pkg/common/validation.go @@ -236,7 +236,11 @@ func (v *ColumnValidator) FilterRequestOptions(options RequestOptions) RequestOp validFilters := make([]FilterOption, 0, len(options.Filters)) for _, filter := range options.Filters { if strings.EqualFold(filter.Column, "all") { - for _, col := range v.Columns() { + allCols := v.Columns() + if len(filtered.Columns) > 0 { + allCols = filtered.Columns + } + for _, col := range allCols { expanded := filter expanded.Column = col expanded.LogicOperator = "OR" diff --git a/pkg/funcspec/function_api.go b/pkg/funcspec/function_api.go index 692dfd2..a2dcec1 100644 --- a/pkg/funcspec/function_api.go +++ b/pkg/funcspec/function_api.go @@ -174,7 +174,7 @@ func (h *Handler) SqlQueryList(sqlquery string, options SqlQueryOptions) HTTPFun varName := kw[1 : len(kw)-1] // strip [ and ] if val, ok := variables[varName]; ok { if strVal := fmt.Sprintf("%v", val); strVal != "" { - sqlquery = strings.ReplaceAll(sqlquery, kw, ValidSQL(strVal, "colvalue")) + sqlquery = strings.ReplaceAll(sqlquery, kw, safeSubstituteVar(sqlquery, kw, strVal)) continue } } @@ -533,7 +533,7 @@ func (h *Handler) SqlQuery(sqlquery string, options SqlQueryOptions) HTTPFuncTyp varName := kw[1 : len(kw)-1] // strip [ and ] if val, ok := variables[varName]; ok { if strVal := fmt.Sprintf("%v", val); strVal != "" { - sqlquery = strings.ReplaceAll(sqlquery, kw, ValidSQL(strVal, "colvalue")) + sqlquery = strings.ReplaceAll(sqlquery, kw, safeSubstituteVar(sqlquery, kw, strVal)) continue } } @@ -1006,6 +1006,37 @@ func IsNumeric(s string) bool { return err == nil } +// isInsideDollarQuote reports whether the first occurrence of placeholder in sqlquery +// is immediately surrounded by dollar-sign characters (i.e. inside a $...$-quoted string). +// Dollar-quoted strings pass content through literally — no backslash processing — so +// values placed there must NOT have their backslashes escaped. +func isInsideDollarQuote(sqlquery, placeholder string) bool { + idx := strings.Index(sqlquery, placeholder) + if idx < 0 { + return false + } + endIdx := idx + len(placeholder) + charBefore := byte(0) + charAfter := byte(0) + if idx > 0 { + charBefore = sqlquery[idx-1] + } + if endIdx < len(sqlquery) { + charAfter = sqlquery[endIdx] + } + return charBefore == '$' || charAfter == '$' +} + +// safeSubstituteVar returns value sanitised for the quoting context that surrounds +// placeholder in sqlquery: raw (no backslash escaping) for dollar-quoted contexts, +// ValidSQL("colvalue") escaping for everything else. +func safeSubstituteVar(sqlquery, placeholder, value string) string { + if isInsideDollarQuote(sqlquery, placeholder) { + return value + } + return ValidSQL(value, "colvalue") +} + // getReplacementForBlankParam determines the replacement value for an unused parameter // based on whether it appears within quotes in the SQL query. // It checks for PostgreSQL quotes: single quotes (”) and dollar quotes ($...$)