str.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package common
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "math/rand"
  6. "net/url"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "unsafe"
  11. )
  12. func GetStringIfEmpty(str string, defaultValue string) string {
  13. if str == "" {
  14. return defaultValue
  15. }
  16. return str
  17. }
  18. func GetRandomString(length int) string {
  19. //rand.Seed(time.Now().UnixNano())
  20. key := make([]byte, length)
  21. for i := 0; i < length; i++ {
  22. key[i] = keyChars[rand.Intn(len(keyChars))]
  23. }
  24. return string(key)
  25. }
  26. func MapToJsonStr(m map[string]interface{}) string {
  27. bytes, err := json.Marshal(m)
  28. if err != nil {
  29. return ""
  30. }
  31. return string(bytes)
  32. }
  33. func StrToMap(str string) (map[string]interface{}, error) {
  34. m := make(map[string]interface{})
  35. err := Unmarshal([]byte(str), &m)
  36. if err != nil {
  37. return nil, err
  38. }
  39. return m, nil
  40. }
  41. func StrToJsonArray(str string) ([]interface{}, error) {
  42. var js []interface{}
  43. err := json.Unmarshal([]byte(str), &js)
  44. if err != nil {
  45. return nil, err
  46. }
  47. return js, nil
  48. }
  49. func IsJsonArray(str string) bool {
  50. var js []interface{}
  51. return json.Unmarshal([]byte(str), &js) == nil
  52. }
  53. func IsJsonObject(str string) bool {
  54. var js map[string]interface{}
  55. return json.Unmarshal([]byte(str), &js) == nil
  56. }
  57. func String2Int(str string) int {
  58. num, err := strconv.Atoi(str)
  59. if err != nil {
  60. return 0
  61. }
  62. return num
  63. }
  64. func StringsContains(strs []string, str string) bool {
  65. for _, s := range strs {
  66. if s == str {
  67. return true
  68. }
  69. }
  70. return false
  71. }
  72. // StringToByteSlice []byte only read, panic on append
  73. func StringToByteSlice(s string) []byte {
  74. tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))
  75. tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}
  76. return *(*[]byte)(unsafe.Pointer(&tmp2))
  77. }
  78. func EncodeBase64(str string) string {
  79. return base64.StdEncoding.EncodeToString([]byte(str))
  80. }
  81. func GetJsonString(data any) string {
  82. if data == nil {
  83. return ""
  84. }
  85. b, _ := json.Marshal(data)
  86. return string(b)
  87. }
  88. // MaskEmail masks a user email to prevent PII leakage in logs
  89. // Returns "***masked***" if email is empty, otherwise shows only the domain part
  90. func MaskEmail(email string) string {
  91. if email == "" {
  92. return "***masked***"
  93. }
  94. // Find the @ symbol
  95. atIndex := strings.Index(email, "@")
  96. if atIndex == -1 {
  97. // No @ symbol found, return masked
  98. return "***masked***"
  99. }
  100. // Return only the domain part with @ symbol
  101. return "***@" + email[atIndex+1:]
  102. }
  103. // maskHostTail returns the tail parts of a domain/host that should be preserved.
  104. // It keeps 2 parts for likely country-code TLDs (e.g., co.uk, com.cn), otherwise keeps only the TLD.
  105. func maskHostTail(parts []string) []string {
  106. if len(parts) < 2 {
  107. return parts
  108. }
  109. lastPart := parts[len(parts)-1]
  110. secondLastPart := parts[len(parts)-2]
  111. if len(lastPart) == 2 && len(secondLastPart) <= 3 {
  112. // Likely country code TLD like co.uk, com.cn
  113. return []string{secondLastPart, lastPart}
  114. }
  115. return []string{lastPart}
  116. }
  117. // maskHostForURL collapses subdomains and keeps only masked prefix + preserved tail.
  118. // Example: api.openai.com -> ***.com, sub.domain.co.uk -> ***.co.uk
  119. func maskHostForURL(host string) string {
  120. parts := strings.Split(host, ".")
  121. if len(parts) < 2 {
  122. return "***"
  123. }
  124. tail := maskHostTail(parts)
  125. return "***." + strings.Join(tail, ".")
  126. }
  127. // maskHostForPlainDomain masks a plain domain and reflects subdomain depth with multiple ***.
  128. // Example: openai.com -> ***.com, api.openai.com -> ***.***.com, sub.domain.co.uk -> ***.***.co.uk
  129. func maskHostForPlainDomain(domain string) string {
  130. parts := strings.Split(domain, ".")
  131. if len(parts) < 2 {
  132. return domain
  133. }
  134. tail := maskHostTail(parts)
  135. numStars := len(parts) - len(tail)
  136. if numStars < 1 {
  137. numStars = 1
  138. }
  139. stars := strings.TrimSuffix(strings.Repeat("***.", numStars), ".")
  140. return stars + "." + strings.Join(tail, ".")
  141. }
  142. // MaskSensitiveInfo masks sensitive information like URLs, IPs, and domain names in a string
  143. // Example:
  144. // http://example.com -> http://***.com
  145. // https://api.test.org/v1/users/123?key=secret -> https://***.org/***/***/?key=***
  146. // https://sub.domain.co.uk/path/to/resource -> https://***.co.uk/***/***
  147. // 192.168.1.1 -> ***.***.***.***
  148. // openai.com -> ***.com
  149. // www.openai.com -> ***.***.com
  150. // api.openai.com -> ***.***.com
  151. func MaskSensitiveInfo(str string) string {
  152. // Mask URLs
  153. urlPattern := regexp.MustCompile(`(http|https)://[^\s/$.?#].[^\s]*`)
  154. str = urlPattern.ReplaceAllStringFunc(str, func(urlStr string) string {
  155. u, err := url.Parse(urlStr)
  156. if err != nil {
  157. return urlStr
  158. }
  159. host := u.Host
  160. if host == "" {
  161. return urlStr
  162. }
  163. // Mask host with unified logic
  164. maskedHost := maskHostForURL(host)
  165. result := u.Scheme + "://" + maskedHost
  166. // Mask path
  167. if u.Path != "" && u.Path != "/" {
  168. pathParts := strings.Split(strings.Trim(u.Path, "/"), "/")
  169. maskedPathParts := make([]string, len(pathParts))
  170. for i := range pathParts {
  171. if pathParts[i] != "" {
  172. maskedPathParts[i] = "***"
  173. }
  174. }
  175. if len(maskedPathParts) > 0 {
  176. result += "/" + strings.Join(maskedPathParts, "/")
  177. }
  178. } else if u.Path == "/" {
  179. result += "/"
  180. }
  181. // Mask query parameters
  182. if u.RawQuery != "" {
  183. values, err := url.ParseQuery(u.RawQuery)
  184. if err != nil {
  185. // If can't parse query, just mask the whole query string
  186. result += "?***"
  187. } else {
  188. maskedParams := make([]string, 0, len(values))
  189. for key := range values {
  190. maskedParams = append(maskedParams, key+"=***")
  191. }
  192. if len(maskedParams) > 0 {
  193. result += "?" + strings.Join(maskedParams, "&")
  194. }
  195. }
  196. }
  197. return result
  198. })
  199. // Mask domain names without protocol (like openai.com, www.openai.com)
  200. domainPattern := regexp.MustCompile(`\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b`)
  201. str = domainPattern.ReplaceAllStringFunc(str, func(domain string) string {
  202. return maskHostForPlainDomain(domain)
  203. })
  204. // Mask IP addresses
  205. ipPattern := regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`)
  206. str = ipPattern.ReplaceAllString(str, "***.***.***.***")
  207. return str
  208. }