feat(sql): Add common whitespace characters, ASCII, HTML, backslashes to literal escaping

This commit is contained in:
Björn Benouarets
2025-11-06 19:34:48 +01:00
parent ad2eaa6ebb
commit fc982db3ef
9 changed files with 52 additions and 17 deletions

View File

@@ -120,14 +120,36 @@ func QuoteIdent(name string) string {
return `"` + strings.ReplaceAll(name, `"`, `""`) + `"`
}
// QuoteLiteral escapes a string literal for use in SQL queries
// It doubles single quotes to prevent SQL injection
func QuoteLiteral(s string) string {
return "'" + strings.ReplaceAll(s, "'", "''") + "'"
s = strings.ReplaceAll(s, "\x00", "")
var b strings.Builder
b.Grow(len(s) + 2)
b.WriteByte('\'')
for _, r := range s {
switch r {
case '\'':
b.WriteString("''")
case '\\':
b.WriteString("\\\\")
case '\x00':
continue
case '\t', '\n', '\r':
b.WriteRune(r)
default:
if r < 0x20 {
b.WriteString(fmt.Sprintf("\\%03o", r))
} else {
b.WriteRune(r)
}
}
}
b.WriteByte('\'')
return b.String()
}
// QuoteValue formats a value according to its type for use in SQL queries
// It handles strings, numbers, booleans, null, and UUIDs safely
func QuoteValue(v any) string {
if v == nil {
return "NULL"
@@ -135,7 +157,6 @@ func QuoteValue(v any) string {
switch val := v.(type) {
case string:
// Check if it's a valid UUID format
if matched, _ := regexp.MatchString(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`, strings.ToLower(val)); matched {
return QuoteLiteral(val)
}
@@ -150,14 +171,10 @@ func QuoteValue(v any) string {
}
return "FALSE"
default:
// For unknown types, convert to string and escape
return QuoteLiteral(fmt.Sprintf("%v", val))
}
}
// BuildWhereClause safely builds a WHERE clause from conditions
// conditions is a map of column names to values (uses = operator)
// Returns empty string if no conditions provided
func BuildWhereClause(conditions map[string]any) (string, error) {
if len(conditions) == 0 {
return "", nil