diff --git a/sql/analyzer/analyzer.go b/sql/analyzer/analyzer.go index 6c26d3a4a3..10a5f755a7 100644 --- a/sql/analyzer/analyzer.go +++ b/sql/analyzer/analyzer.go @@ -294,63 +294,74 @@ func (a *Analyzer) Log(msg string, args ...interface{}) { if a != nil && a.Debug { if len(a.contextStack) > 0 { ctx := strings.Join(a.contextStack, "/") - log.Infof("%s: "+msg, append([]interface{}{ctx}, sanitizeArguments(args)...)...) + sanitizedArgs := sanitizeArguments(args) + log.Infof("%s: "+msg, append([]interface{}{ctx}, sanitizedArgs...)...) } else { - log.Infof(msg, sanitizeArguments(args)...) + sanitizedArgs := sanitizeArguments(args) + log.Infof(msg, sanitizedArgs...) } } } func sanitizeArguments(args []interface{}) []interface{} { + sanitizedArgs := make([]interface{}, len(args)) for i, arg := range args { switch v := arg.(type) { case string: if isSensitiveString(v) { - args[i] = "[REDACTED]" + sanitizedArgs[i] = "[REDACTED]" + } else { + sanitizedArgs[i] = v } case map[string]interface{}: - args[i] = sanitizeMap(v) + sanitizedArgs[i] = sanitizeMap(v) case []interface{}: - args[i] = sanitizeArguments(v) + sanitizedArgs[i] = sanitizeArguments(v) case plan.AuthenticationMysqlNativePassword: - args[i] = "[PASSWORD_REDACTED]" + sanitizedArgs[i] = "[PASSWORD_REDACTED]" default: // Use reflection to handle structs and pointers to structs rv := reflect.ValueOf(arg) if rv.Kind() == reflect.Ptr { rv = rv.Elem() if rv.Kind() == reflect.Struct { - args[i] = sanitizeStruct(rv) + sanitizedArgs[i] = sanitizeStruct(rv) continue } } if rv.Kind() == reflect.Struct { - args[i] = sanitizeStruct(rv) + sanitizedArgs[i] = sanitizeStruct(rv) } else if rv.Kind() == reflect.Slice { // Recursively sanitize slice elements slice := make([]interface{}, rv.Len()) for j := 0; j < rv.Len(); j++ { slice[j] = sanitizeArguments([]interface{}{rv.Index(j).Interface()})[0] } - args[i] = slice + sanitizedArgs[i] = slice } else if rv.Kind() == reflect.Map { // Recursively sanitize map values mapSanitized := make(map[interface{}]interface{}) for _, key := range rv.MapKeys() { val := rv.MapIndex(key).Interface() - if isSensitiveString(fmt.Sprintf("%v", key.Interface())) || isSensitive(val) { + keyStr := fmt.Sprintf("%v", key.Interface()) + if isSensitiveString(keyStr) || isSensitive(val) { mapSanitized[key.Interface()] = "[REDACTED]" } else { mapSanitized[key.Interface()] = sanitizeArguments([]interface{}{val})[0] } } - args[i] = mapSanitized + sanitizedArgs[i] = mapSanitized } else { - args[i] = "[REDACTED]" + // Catch-all for unhandled types + if strVal := fmt.Sprintf("%v", arg); isSensitiveString(strVal) { + sanitizedArgs[i] = "[REDACTED]" + } else { + sanitizedArgs[i] = arg + } } } } - return args + return sanitizedArgs } func sanitizeStruct(rv reflect.Value) interface{} { @@ -380,7 +391,8 @@ func sanitizeStruct(rv reflect.Value) interface{} { mapSanitized := make(map[interface{}]interface{}) for _, key := range rv.Field(i).MapKeys() { val := rv.Field(i).MapIndex(key).Interface() - if isSensitiveString(fmt.Sprintf("%v", key.Interface())) || isSensitive(val) { + keyStr := fmt.Sprintf("%v", key.Interface()) + if isSensitiveString(keyStr) || isSensitive(val) { mapSanitized[key.Interface()] = "[REDACTED]" } else { mapSanitized[key.Interface()] = sanitizeArguments([]interface{}{val})[0] @@ -396,9 +408,10 @@ func sanitizeStruct(rv reflect.Value) interface{} { } func sanitizeMap(m map[string]interface{}) map[string]interface{} { + result := make(map[string]interface{}) for key, value := range m { if isSensitiveString(key) || isSensitive(value) { - m[key] = "[REDACTED]" + result[key] = "[REDACTED]" } else { rv := reflect.ValueOf(value) switch rv.Kind() { @@ -407,31 +420,33 @@ func sanitizeMap(m map[string]interface{}) map[string]interface{} { subMap := make(map[interface{}]interface{}) for _, subKey := range rv.MapKeys() { val := rv.MapIndex(subKey).Interface() - if isSensitiveString(fmt.Sprintf("%v", subKey.Interface())) || isSensitive(val) { + keyStr := fmt.Sprintf("%v", subKey.Interface()) + if isSensitiveString(keyStr) || isSensitive(val) { subMap[subKey.Interface()] = "[REDACTED]" } else { subMap[subKey.Interface()] = sanitizeArguments([]interface{}{val})[0] } } - m[key] = subMap + result[key] = subMap case reflect.Slice: slice := make([]interface{}, rv.Len()) for j := 0; j < rv.Len(); j++ { slice[j] = sanitizeArguments([]interface{}{rv.Index(j).Interface()})[0] } - m[key] = slice + result[key] = slice case reflect.Struct: - m[key] = sanitizeStruct(rv) + result[key] = sanitizeStruct(rv) default: - m[key] = value + result[key] = value } } } - return m + return result } +// isSensitiveString checks if a string contains sensitive information func isSensitiveString(str string) bool { - sensitiveKeywords := []string{"password", "secret", "token", "key"} + sensitiveKeywords := []string{"password", "secret", "token", "key", "auth", "credential", "private"} str = strings.ToLower(str) for _, keyword := range sensitiveKeywords { if strings.Contains(str, keyword) { @@ -441,13 +456,19 @@ func isSensitiveString(str string) bool { return false } +// isSensitive checks if a value contains sensitive information func isSensitive(arg interface{}) bool { - // Add logic to identify sensitive data (e.g., passwords) - // This may involve checking types or specific fields - if str, ok := arg.(string); ok && strings.Contains(strings.ToLower(str), "password") { - return true + if arg == nil { + return false } - return false + + // Check strings directly + if str, ok := arg.(string); ok { + return isSensitiveString(str) + } + + // Check string representation + return isSensitiveString(fmt.Sprintf("%v", arg)) } // LogNode prints the node given if Verbose logging is enabled. diff --git a/sql/system_settype.go b/sql/system_settype.go index 0fe57e599c..45e8fcf157 100644 --- a/sql/system_settype.go +++ b/sql/system_settype.go @@ -90,15 +90,13 @@ func (t systemSetType) Convert(v interface{}) (interface{}, error) { case float64: // Float values aren't truly accepted, but the engine will give them when it should give ints. // Therefore, if the float doesn't have a fractional portion, we treat it as an int. - if value >= float64(math.MinInt64) && value <= float64(math.MaxInt64) { - if math.Trunc(value) == value { // Ensure no fractional part exists - // Additional bounds check for out-of-range values - if value < float64(math.MinInt64) || value > float64(math.MaxInt64) { - return nil, ErrInvalidSystemVariableValue.New(t.varName, v) // Reject out-of-range values - } - intValue := int64(value) - return t.SetType.Convert(intValue) + if math.Trunc(value) == value { // Ensure no fractional part exists + // Explicit bounds check for int64 range + if value < float64(math.MinInt64) || value > float64(math.MaxInt64) { + return nil, ErrInvalidSystemVariableValue.New(t.varName, v) // Reject out-of-range values } + intValue := int64(value) + return t.SetType.Convert(intValue) } return nil, ErrInvalidSystemVariableValue.New(t.varName, v) // Reject out-of-range values case decimal.Decimal: diff --git a/sql/timetype.go b/sql/timetype.go index ad3cface2c..7e9f97d479 100644 --- a/sql/timetype.go +++ b/sql/timetype.go @@ -175,6 +175,9 @@ func (t timespanType) ConvertToTimespan(v interface{}) (Timespan, error) { case float32: return t.ConvertToTimespan(float64(value)) case float64: + if value < float64(math.MinInt64) || value > float64(math.MaxInt64) { + return Timespan(0), fmt.Errorf("float64 value %f exceeds int64 bounds", value) + } intValue := int64(value) microseconds := int64Abs(int64(math.Round((value - float64(intValue)) * float64(microsecondsPerSecond)))) absValue := int64Abs(intValue)