diff --git a/.github/codeql-custom-model.yml b/.github/codeql-custom-model.yml index 818ec167..f0fdab5b 100644 --- a/.github/codeql-custom-model.yml +++ b/.github/codeql-custom-model.yml @@ -42,3 +42,18 @@ extensions: # TestURLConnectivity validates URLs internally via security.ValidateExternalURL # and ssrfSafeDialer - it's a terminating function, not a pass-through - ["github.com/Wikid82/charon/backend/internal/utils", "TestURLConnectivity", "manual"] + + # Mark log sanitization functions as sanitizers for log injection (CWE-117) + # These functions remove newlines and control characters from user input before logging + - addsTo: + pack: codeql/go-all + extensible: summaryModel + data: + # util.SanitizeForLog sanitizes strings by: + # 1. Replacing \r\n and \n with spaces + # 2. Removing all control characters [\x00-\x1F\x7F] + # Input: Argument[0] (unsanitized string) + # Output: ReturnValue[0] (sanitized string - safe for logging) + - ["github.com/Wikid82/charon/backend/internal/util", "SanitizeForLog", "Argument[0]", "ReturnValue[0]", "taint", "manual"] + # handlers.sanitizeForLog is a local sanitizer with same behavior + - ["github.com/Wikid82/charon/backend/internal/api/handlers", "sanitizeForLog", "Argument[0]", "ReturnValue[0]", "taint", "manual"] diff --git a/backend/internal/api/handlers/proxy_host_handler.go b/backend/internal/api/handlers/proxy_host_handler.go index 91c54b47..e73da5a6 100644 --- a/backend/internal/api/handlers/proxy_host_handler.go +++ b/backend/internal/api/handlers/proxy_host_handler.go @@ -332,44 +332,46 @@ func (h *ProxyHostHandler) Update(c *gin.Context) { // Security Header Profile: update only if provided if v, ok := payload["security_header_profile_id"]; ok { logger := middleware.GetRequestLogger(c) - logger.WithField("host_uuid", uuidStr).WithField("raw_value", v).Debug("Processing security_header_profile_id update") + // Sanitize user-provided values for log injection protection (CWE-117) + safeUUID := sanitizeForLog(uuidStr) + logger.WithField("host_uuid", safeUUID).WithField("raw_value", fmt.Sprintf("%v", v)).Debug("Processing security_header_profile_id update") if v == nil { - logger.WithField("host_uuid", uuidStr).Debug("Setting security_header_profile_id to nil") + logger.WithField("host_uuid", safeUUID).Debug("Setting security_header_profile_id to nil") host.SecurityHeaderProfileID = nil } else { conversionSuccess := false switch t := v.(type) { case float64: - logger.WithField("host_uuid", uuidStr).WithField("type", "float64").WithField("value", t).Debug("Received security_header_profile_id as float64") + logger.WithField("host_uuid", safeUUID).WithField("type", "float64").WithField("value", t).Debug("Received security_header_profile_id as float64") if id, ok := safeFloat64ToUint(t); ok { host.SecurityHeaderProfileID = &id conversionSuccess = true - logger.WithField("host_uuid", uuidStr).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from float64") + logger.WithField("host_uuid", safeUUID).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from float64") } else { - logger.WithField("host_uuid", uuidStr).WithField("value", t).Warn("Failed to convert security_header_profile_id from float64: value is negative or not a valid uint") + logger.WithField("host_uuid", safeUUID).WithField("value", t).Warn("Failed to convert security_header_profile_id from float64: value is negative or not a valid uint") } case int: - logger.WithField("host_uuid", uuidStr).WithField("type", "int").WithField("value", t).Debug("Received security_header_profile_id as int") + logger.WithField("host_uuid", safeUUID).WithField("type", "int").WithField("value", t).Debug("Received security_header_profile_id as int") if id, ok := safeIntToUint(t); ok { host.SecurityHeaderProfileID = &id conversionSuccess = true - logger.WithField("host_uuid", uuidStr).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from int") + logger.WithField("host_uuid", safeUUID).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from int") } else { - logger.WithField("host_uuid", uuidStr).WithField("value", t).Warn("Failed to convert security_header_profile_id from int: value is negative") + logger.WithField("host_uuid", safeUUID).WithField("value", t).Warn("Failed to convert security_header_profile_id from int: value is negative") } case string: - logger.WithField("host_uuid", uuidStr).WithField("type", "string").WithField("value", t).Debug("Received security_header_profile_id as string") + logger.WithField("host_uuid", safeUUID).WithField("type", "string").WithField("value", sanitizeForLog(t)).Debug("Received security_header_profile_id as string") if n, err := strconv.ParseUint(t, 10, 32); err == nil { id := uint(n) host.SecurityHeaderProfileID = &id conversionSuccess = true - logger.WithField("host_uuid", uuidStr).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from string") + logger.WithField("host_uuid", safeUUID).WithField("profile_id", id).Info("Successfully converted security_header_profile_id from string") } else { - logger.WithField("host_uuid", uuidStr).WithField("value", t).WithError(err).Warn("Failed to parse security_header_profile_id from string") + logger.WithField("host_uuid", safeUUID).WithField("value", sanitizeForLog(t)).WithError(err).Warn("Failed to parse security_header_profile_id from string") } default: - logger.WithField("host_uuid", uuidStr).WithField("type", fmt.Sprintf("%T", v)).WithField("value", v).Warn("Unsupported type for security_header_profile_id") + logger.WithField("host_uuid", safeUUID).WithField("type", fmt.Sprintf("%T", v)).WithField("value", fmt.Sprintf("%v", v)).Warn("Unsupported type for security_header_profile_id") } if !conversionSuccess {