Security fixes from audit: - Escape user-supplied strings (ext_relation_id, interface_name) with url.PathEscape before interpolating into API URL paths, preventing path traversal via crafted values like "../admin" or "foo/bar" - Mark auth token URL attributes as Sensitive in both virtfusion_user_auth_token and virtfusion_user_server_auth_token resources, since the URL embeds the signed token - Truncate raw API error response bodies to 500 bytes in error messages to prevent leaking sensitive data from verbose Laravel error responses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
44 lines
1.2 KiB
Go
44 lines
1.2 KiB
Go
// Copyright (c) EZSCALE.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package client
|
|
|
|
import "fmt"
|
|
|
|
// APIError represents an error returned by the VirtFusion API.
|
|
type APIError struct {
|
|
StatusCode int
|
|
Status string
|
|
Body string
|
|
Errors map[string][]string
|
|
}
|
|
|
|
// maxErrorBodyLen is the maximum number of bytes from the API response body
|
|
// to include in error messages, to avoid leaking sensitive data from verbose
|
|
// error responses.
|
|
const maxErrorBodyLen = 500
|
|
|
|
func (e *APIError) Error() string {
|
|
if len(e.Errors) > 0 {
|
|
return fmt.Sprintf("VirtFusion API error %d (%s): %v", e.StatusCode, e.Status, e.Errors)
|
|
}
|
|
if e.Body != "" {
|
|
body := e.Body
|
|
if len(body) > maxErrorBodyLen {
|
|
body = body[:maxErrorBodyLen] + "... (truncated)"
|
|
}
|
|
return fmt.Sprintf("VirtFusion API error %d (%s): %s", e.StatusCode, e.Status, body)
|
|
}
|
|
return fmt.Sprintf("VirtFusion API error %d (%s)", e.StatusCode, e.Status)
|
|
}
|
|
|
|
// IsNotFound returns true if the error is a 404 Not Found response.
|
|
func (e *APIError) IsNotFound() bool {
|
|
return e.StatusCode == 404
|
|
}
|
|
|
|
// IsValidationError returns true if the error is a 422 Unprocessable Entity response.
|
|
func (e *APIError) IsValidationError() bool {
|
|
return e.StatusCode == 422
|
|
}
|