This commit introduces a new function, MaskEmail, to mask user email addresses in logs, preventing PII leakage. Additionally, the RelayInfo logging has been updated to utilize this new masking function, ensuring sensitive information is properly handled. The channel test logic has also been improved to dynamically determine the relay format based on the request path.
239 lines
5.6 KiB
Go
239 lines
5.6 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"math/rand"
|
|
"net/url"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"unsafe"
|
|
)
|
|
|
|
func GetStringIfEmpty(str string, defaultValue string) string {
|
|
if str == "" {
|
|
return defaultValue
|
|
}
|
|
return str
|
|
}
|
|
|
|
func GetRandomString(length int) string {
|
|
//rand.Seed(time.Now().UnixNano())
|
|
key := make([]byte, length)
|
|
for i := 0; i < length; i++ {
|
|
key[i] = keyChars[rand.Intn(len(keyChars))]
|
|
}
|
|
return string(key)
|
|
}
|
|
|
|
func MapToJsonStr(m map[string]interface{}) string {
|
|
bytes, err := json.Marshal(m)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(bytes)
|
|
}
|
|
|
|
func StrToMap(str string) (map[string]interface{}, error) {
|
|
m := make(map[string]interface{})
|
|
err := Unmarshal([]byte(str), &m)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func StrToJsonArray(str string) ([]interface{}, error) {
|
|
var js []interface{}
|
|
err := json.Unmarshal([]byte(str), &js)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return js, nil
|
|
}
|
|
|
|
func IsJsonArray(str string) bool {
|
|
var js []interface{}
|
|
return json.Unmarshal([]byte(str), &js) == nil
|
|
}
|
|
|
|
func IsJsonObject(str string) bool {
|
|
var js map[string]interface{}
|
|
return json.Unmarshal([]byte(str), &js) == nil
|
|
}
|
|
|
|
func String2Int(str string) int {
|
|
num, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return num
|
|
}
|
|
|
|
func StringsContains(strs []string, str string) bool {
|
|
for _, s := range strs {
|
|
if s == str {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// StringToByteSlice []byte only read, panic on append
|
|
func StringToByteSlice(s string) []byte {
|
|
tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))
|
|
tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}
|
|
return *(*[]byte)(unsafe.Pointer(&tmp2))
|
|
}
|
|
|
|
func EncodeBase64(str string) string {
|
|
return base64.StdEncoding.EncodeToString([]byte(str))
|
|
}
|
|
|
|
func GetJsonString(data any) string {
|
|
if data == nil {
|
|
return ""
|
|
}
|
|
b, _ := json.Marshal(data)
|
|
return string(b)
|
|
}
|
|
|
|
// MaskEmail masks a user email to prevent PII leakage in logs
|
|
// Returns "***masked***" if email is empty, otherwise shows only the domain part
|
|
func MaskEmail(email string) string {
|
|
if email == "" {
|
|
return "***masked***"
|
|
}
|
|
|
|
// Find the @ symbol
|
|
atIndex := strings.Index(email, "@")
|
|
if atIndex == -1 {
|
|
// No @ symbol found, return masked
|
|
return "***masked***"
|
|
}
|
|
|
|
// Return only the domain part with @ symbol
|
|
return "***@" + email[atIndex+1:]
|
|
}
|
|
|
|
// MaskSensitiveInfo masks sensitive information like URLs, IPs, and domain names in a string
|
|
// Example:
|
|
// http://example.com -> http://***.com
|
|
// https://api.test.org/v1/users/123?key=secret -> https://***.org/***/***/?key=***
|
|
// https://sub.domain.co.uk/path/to/resource -> https://***.co.uk/***/***
|
|
// 192.168.1.1 -> ***.***.***.***
|
|
// openai.com -> ***.com
|
|
// www.openai.com -> ***.***.com
|
|
// api.openai.com -> ***.***.com
|
|
func MaskSensitiveInfo(str string) string {
|
|
// Mask URLs
|
|
urlPattern := regexp.MustCompile(`(http|https)://[^\s/$.?#].[^\s]*`)
|
|
str = urlPattern.ReplaceAllStringFunc(str, func(urlStr string) string {
|
|
u, err := url.Parse(urlStr)
|
|
if err != nil {
|
|
return urlStr
|
|
}
|
|
|
|
host := u.Host
|
|
if host == "" {
|
|
return urlStr
|
|
}
|
|
|
|
// Split host by dots
|
|
parts := strings.Split(host, ".")
|
|
if len(parts) < 2 {
|
|
// If less than 2 parts, just mask the whole host
|
|
return u.Scheme + "://***" + u.Path
|
|
}
|
|
|
|
// Keep the TLD (Top Level Domain) and mask the rest
|
|
var maskedHost string
|
|
if len(parts) == 2 {
|
|
// example.com -> ***.com
|
|
maskedHost = "***." + parts[len(parts)-1]
|
|
} else {
|
|
// Handle cases like sub.domain.co.uk or api.example.com
|
|
// Keep last 2 parts if they look like country code TLD (co.uk, com.cn, etc.)
|
|
lastPart := parts[len(parts)-1]
|
|
secondLastPart := parts[len(parts)-2]
|
|
|
|
if len(lastPart) == 2 && len(secondLastPart) <= 3 {
|
|
// Likely country code TLD like co.uk, com.cn
|
|
maskedHost = "***." + secondLastPart + "." + lastPart
|
|
} else {
|
|
// Regular TLD like .com, .org
|
|
maskedHost = "***." + lastPart
|
|
}
|
|
}
|
|
|
|
result := u.Scheme + "://" + maskedHost
|
|
|
|
// Mask path
|
|
if u.Path != "" && u.Path != "/" {
|
|
pathParts := strings.Split(strings.Trim(u.Path, "/"), "/")
|
|
maskedPathParts := make([]string, len(pathParts))
|
|
for i := range pathParts {
|
|
if pathParts[i] != "" {
|
|
maskedPathParts[i] = "***"
|
|
}
|
|
}
|
|
if len(maskedPathParts) > 0 {
|
|
result += "/" + strings.Join(maskedPathParts, "/")
|
|
}
|
|
} else if u.Path == "/" {
|
|
result += "/"
|
|
}
|
|
|
|
// Mask query parameters
|
|
if u.RawQuery != "" {
|
|
values, err := url.ParseQuery(u.RawQuery)
|
|
if err != nil {
|
|
// If can't parse query, just mask the whole query string
|
|
result += "?***"
|
|
} else {
|
|
maskedParams := make([]string, 0, len(values))
|
|
for key := range values {
|
|
maskedParams = append(maskedParams, key+"=***")
|
|
}
|
|
if len(maskedParams) > 0 {
|
|
result += "?" + strings.Join(maskedParams, "&")
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
})
|
|
|
|
// Mask domain names without protocol (like openai.com, www.openai.com)
|
|
domainPattern := regexp.MustCompile(`\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b`)
|
|
str = domainPattern.ReplaceAllStringFunc(str, func(domain string) string {
|
|
// Skip if it's already been processed as part of a URL
|
|
if strings.Contains(str, "://"+domain) {
|
|
return domain
|
|
}
|
|
|
|
parts := strings.Split(domain, ".")
|
|
if len(parts) < 2 {
|
|
return domain
|
|
}
|
|
|
|
// Handle different domain patterns
|
|
if len(parts) == 2 {
|
|
// openai.com -> ***.com
|
|
return "***." + parts[1]
|
|
} else {
|
|
// www.openai.com -> ***.***.com
|
|
// api.openai.com -> ***.***.com
|
|
lastPart := parts[len(parts)-1]
|
|
return "***.***." + lastPart
|
|
}
|
|
})
|
|
|
|
// Mask IP addresses
|
|
ipPattern := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
|
|
str = ipPattern.ReplaceAllString(str, "***.***.***.***")
|
|
|
|
return str
|
|
}
|