package utils import ( "crypto/rand" "encoding/base64" "encoding/hex" "fmt" "strings" "time" ) // GenerateID generates a unique ID (UUID-like) func GenerateID() string { b := make([]byte, 16) rand.Read(b) return formatUUID(b) } // formatUUID formats bytes as UUID string func formatUUID(b []byte) string { uuid := make([]byte, 36) hex.Encode(uuid[0:8], b[0:4]) hex.Encode(uuid[9:13], b[4:6]) hex.Encode(uuid[14:18], b[6:8]) hex.Encode(uuid[19:23], b[8:10]) hex.Encode(uuid[24:], b[10:]) uuid[8] = '-' uuid[13] = '-' uuid[18] = '-' uuid[23] = '-' return string(uuid) } // FormatDuration formats duration in milliseconds func FormatDuration(d time.Duration) int64 { return d.Milliseconds() } // SanitizeSQL removes potentially dangerous SQL patterns // Note: This is not a replacement for parameterized queries func SanitizeSQL(sql string) string { // Remove multiple semicolons sql = strings.ReplaceAll(sql, ";;", ";") // Trim whitespace sql = strings.TrimSpace(sql) return sql } // TruncateString truncates a string to max length func TruncateString(s string, maxLen int) string { if len(s) <= maxLen { return s } return s[:maxLen] } // GenerateRandomBytes generates random bytes func GenerateRandomBytes(n int) ([]byte, error) { b := make([]byte, n) _, err := rand.Read(b) if err != nil { return nil, fmt.Errorf("failed to generate random bytes: %w", err) } return b, nil } // EncodeBase64 encodes bytes to base64 string func EncodeBase64(data []byte) string { return base64.StdEncoding.EncodeToString(data) } // DecodeBase64 decodes base64 string to bytes func DecodeBase64(s string) ([]byte, error) { return base64.StdEncoding.DecodeString(s) } // MaskPassword masks password for logging func MaskPassword(password string) string { if len(password) <= 4 { return strings.Repeat("*", len(password)) } return password[:2] + strings.Repeat("*", len(password)-2) } // ContainsAny checks if string contains any of the substrings func ContainsAny(s string, substrings ...string) bool { for _, sub := range substrings { if strings.Contains(s, sub) { return true } } return false } // IsReadOnlyQuery checks if SQL query is read-only func IsReadOnlyQuery(sql string) bool { sql = strings.TrimSpace(strings.ToUpper(sql)) // Read-only operations readOnlyPrefixes := []string{ "SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "WITH", } for _, prefix := range readOnlyPrefixes { if strings.HasPrefix(sql, prefix) { return true } } return false } // IsDDLQuery checks if SQL query is DDL func IsDDLQuery(sql string) bool { sql = strings.TrimSpace(strings.ToUpper(sql)) ddlKeywords := []string{ "CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME", } for _, keyword := range ddlKeywords { if strings.HasPrefix(sql, keyword) { return true } } return false }