Files

112 lines
3.0 KiB
Go

package aihttp
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"sort"
"strings"
"unicode"
"github.com/1Panel-dev/1Panel/agent/utils/re"
)
func SafeError(err error, sensitiveValues ...string) error {
if err == nil {
return nil
}
for _, known := range []error{ErrTarget, ErrResponseTooLarge, ErrResponse} {
if errors.Is(err, known) {
return known
}
}
var urlErr *url.Error
if errors.As(err, &urlErr) {
sensitiveValues = append(sensitiveValues, urlErr.URL)
err = urlErr.Err
}
message := sanitizeErrorMessage(err.Error(), sensitiveValues...)
if message == "" {
return ErrRequest
}
return errors.New(message)
}
func HTTPError(statusCode int, body []byte, sensitiveValues ...string) error {
message := fmt.Sprintf("request failed: status %d", statusCode)
var payload struct {
Error json.RawMessage `json:"error"`
Message string `json:"message"`
}
if json.Unmarshal(body, &payload) != nil {
return errors.New(message)
}
var detail string
if json.Unmarshal(payload.Error, &detail) != nil {
var nested struct {
Message string `json:"message"`
}
if json.Unmarshal(payload.Error, &nested) == nil {
detail = nested.Message
}
}
if detail == "" {
detail = payload.Message
}
if detail = sanitizeErrorMessage(detail, sensitiveValues...); detail != "" {
message += ": " + detail
}
return errors.New(message)
}
func sanitizeErrorMessage(message string, sensitiveValues ...string) string {
values := append([]string(nil), sensitiveValues...)
for _, value := range sensitiveValues {
parsed, err := url.Parse(value)
if err != nil || parsed.Host == "" {
continue
}
if parsed.User != nil {
password, _ := parsed.User.Password()
values = append(values, parsed.User.Username(), password)
}
for _, queryValues := range parsed.Query() {
values = append(values, queryValues...)
}
}
for _, value := range append([]string(nil), values...) {
encoded, _ := json.Marshal(value)
values = append(values, url.QueryEscape(value), url.PathEscape(value), string(encoded[1:len(encoded)-1]))
}
sort.SliceStable(values, func(i, j int) bool { return len(values[i]) > len(values[j]) })
replacements := make([]string, 0, len(values)*2)
for _, value := range values {
if value != "" {
replacements = append(replacements, value, "[REDACTED]")
}
}
if len(replacements) > 0 {
message = strings.NewReplacer(replacements...).Replace(message)
}
message = re.AIErrorURLRegex.ReplaceAllString(message, "[REDACTED URL]")
message = re.AIErrorAuthRegex.ReplaceAllString(message, "$1 [REDACTED]")
message = re.AIErrorSecretRegex.ReplaceAllString(message, "$1[REDACTED]")
if strings.ContainsAny(message, "<>") {
return ""
}
message, _, _ = strings.Cut(strings.TrimSpace(message), "\n")
message, _, _ = strings.Cut(message, "\r")
message = strings.Map(func(r rune) rune {
if unicode.IsControl(r) {
return -1
}
return r
}, message)
runes := []rune(strings.TrimSpace(message))
if len(runes) > 512 {
return string(runes[:512]) + "…"
}
return string(runes)
}