Overview
CVE-2026-33142 describes a SQL injection risk in OneUptime where aggregate query paths were not consistently validating column names before interpolation. Although the fix added in version 10.0.34 validates columns in _aggregateBy, three other query construction paths (toSortStatement, toSelectStatement, toGroupByStatement) continued to accept user-controlled keys from API request bodies and interpolated them as ClickHouse Identifier parameters without ensuring those keys mapped to actual model columns. Because ClickHouse identifiers are substituted directly into queries without escaping, an attacker who can reach analytics endpoints could inject arbitrary SQL through crafted sort, select, or groupBy keys. CVE-2026-32306 likewise highlights SQL injection via aggregate query parameter paths. In practice, this class of vulnerabilities enables attackers to alter queries, expose data, or tamper with data integrity. This remediation guide uses those CVEs as a basis and shows how similar patterns can appear in Go (Gin) applications that construct queries from user input. CWE-89 is the applicable weakness family. The fix, as demonstrated by the patch, is to validate and constrain dynamic identifiers before interpolation and to avoid unsafely concatenating user input into SQL strings. The Go (Gin) guidance here maps those lessons to typical web service code patterns.
Affected Versions
OneUptime prior to 10.0.34 (CVE-2026-33142 and related CVE-2026-32306)
Code Fix Example
Go (Gin) API Security Remediation
package main
import (
"fmt"
"strings"
)
type QueryInput struct {
Select []string `json:"select"`
GroupBy []string `json:"groupBy"`
Sort string `json:"sort"`
}
// Whitelist of allowed identifier columns (example domain)
var allowedColumns = map[string]struct{}{
"id": {},
"name": {},
"created_at": {},
"status": {},
"amount": {},
}
// Vulnerable pattern: user-provided identifiers are interpolated directly into SQL
func vulnerableBuildQuery(in QueryInput) string {
selectClause := strings.Join(in.Select, ", ")
groupByClause := strings.Join(in.GroupBy, ", ")
q := fmt.Sprintf("SELECT %s FROM analytics", selectClause)
if groupByClause != "" {
q += fmt.Sprintf(" GROUP BY %s", groupByClause)
}
if in.Sort != "" {
q += fmt.Sprintf(" ORDER BY %s", in.Sort)
}
return q
}
// Fixed pattern: validate identifiers against a whitelist before interpolation
func safeBuildQuery(in QueryInput) (string, error) {
// Validate SELECT columns
var safeSelect []string
for _, c := range in.Select {
if _, ok := allowedColumns[c]; ok {
safeSelect = append(safeSelect, c)
} else {
return "", fmt.Errorf("invalid column in select: %s", c)
}
}
// Validate GROUP BY columns
var safeGroup []string
for _, c := range in.GroupBy {
if _, ok := allowedColumns[c]; ok {
safeGroup = append(safeGroup, c)
} else {
return "", fmt.Errorf("invalid column in groupBy: %s", c)
}
}
q := fmt.Sprintf("SELECT %s FROM analytics", strings.Join(safeSelect, ", "))
if len(safeGroup) > 0 {
q += fmt.Sprintf(" GROUP BY %s", strings.Join(safeGroup, ", "))
}
// Validate and apply ORDER BY only for whitelisted column
if in.Sort != "" {
if _, ok := allowedColumns[in.Sort]; !ok {
return "", fmt.Errorf("invalid sort column: %s", in.Sort)
}
q += fmt.Sprintf(" ORDER BY %s", in.Sort)
}
return q, nil
}
func main() {
// Example input that could be malicious if interpolated directly
in := QueryInput{
Select: []string{"id", "name; DROP TABLE users;--"},
GroupBy: []string{"name"},
Sort: "name; SELECT 1",
}
fmt.Println("VULNERABLE:\n", vulnerableBuildQuery(in))
if safe, err := safeBuildQuery(in); err != nil {
fmt.Println("SAFE: error ->", err)
} else {
fmt.Println("SAFE:\n", safe)
}
}