Files
xlgo-core/utils/crypto.go
T
杭州明婳科技 6595e94677 fix: 多模型评审终审收口 — P0安全 + 主线A并发治理 + 文档对齐
经 deepseek/GLM/Claude 三轮对抗性评审 + 交叉核验 + 终审(见
last_report.md、cross_review_assessment.md),逐条回读源码核实并修复。
go build / vet / test -race ./... 全绿。

安全(P0):
- JWT alg confusion:WithValidMethods 固定 HMAC + keyfunc 方法断言
- JWT 空密钥 / 不支持算法 fail-closed(ErrEmptySecret / ErrUnsupportedAlgorithm)
- 上传大小实测封顶(enforceUploadSize),不信任客户端 file.Size
- HTTP headers/cookies map 竞态(写锁+快照)+ SSRF 防护(opt-in dialer.Control)

并发/资源(主线A 闭合):
- 包级可变全局一律 atomic.Pointer:DefaultRedis / DefaultStorage /
  DefaultManager / Validator / DefaultCache 等,消除裸指针数据竞争
- trace.Close 去 sync.Once(防 exporter 泄漏);app OnReady 失败走 Shutdown
- ws.Hub.Stop stopOnce 防 double-close;cron 接入 App 生命周期(WithCron)
- ratelimit failClosed atomic.Bool;Recover Written() 守卫
- cron checkAndRun 锁内收集锁外 spawn(wg.Add 在锁内防 Stop 竞态)

终审剩余项收口(H-A~M-H + L 系列):
- 副本连接池 MaxOpenConns/2 截断修复(replicaMaxOpenConns)
- JWT 黑名单 1s 超时 + 显式错误;ratelimit GetRedis 取一次复用
- GetPage 深分页上限;HashFile 流式;ReadFile 去 TOCTOU
- config Clone() 深拷贝;cache 锁操作返 ErrRedisNotReady
- compress 解压残留清理;正则预编译;EqualsIgnoreCase→EqualFold
- 删 redisLimiters 死代码;CLI 输入校验 + 回滚

文档:
- README/GUIDE/CHANGELOG 对齐当前 API(含破坏性变更迁移说明)
- docs/README 索引修正;config 注释修正(RS256 已不支持)

破坏性变更详见 CHANGELOG.md [Unreleased]。项目处于研发初期、无下游用户。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:16:03 +08:00

123 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"hash"
"io"
"os"
)
// MD5 计算字符串的 MD5 哈希值
// 注意: 不应用于密码存储
func MD5(s string) string {
return MD5Bytes([]byte(s))
}
// MD5Bytes 计算字节数组的 MD5 哈希值
func MD5Bytes(data []byte) string {
h := md5.New()
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// SHA1 计算字符串的 SHA1 哈希值
// 注意: 不应用于密码存储
func SHA1(s string) string {
h := sha1.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// SHA256 计算字符串的 SHA256 哈希值
func SHA256(s string) string {
h := sha256.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
// SHA256Bytes 计算字节数组的 SHA256 哈希值
func SHA256Bytes(data []byte) string {
h := sha256.New()
h.Write(data)
return hex.EncodeToString(h.Sum(nil))
}
// HashFile 计算文件的哈希值。
//
// M-F 修复:改为流式 io.Copy(h, f),不再经 ReadFile 把整文件读入内存——原实现大文件 OOM。
// 流式哈希内存占用恒定、可处理任意大小文件,与 hash.Hash 的 io.Writer 接口契合。
// 调用方负责选择哈希算法(如 sha256.New)。
func HashFile(path string, newHash func() hash.Hash) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
defer f.Close()
h := newHash()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return hex.EncodeToString(h.Sum(nil)), nil
}
// Base64Encode Base64 编码
func Base64Encode(data []byte) string {
return base64.StdEncoding.EncodeToString(data)
}
// Base64Decode Base64 解码
func Base64Decode(s string) ([]byte, error) {
return base64.StdEncoding.DecodeString(s)
}
// Base64URLEncode URL 安全的 Base64 编码
func Base64URLEncode(data []byte) string {
return base64.URLEncoding.EncodeToString(data)
}
// Base64URLDecode URL 安全的 Base64 解码
func Base64URLDecode(s string) ([]byte, error) {
return base64.URLEncoding.DecodeString(s)
}
// Nl2br 将换行符替换为 <br> 标签
func Nl2br(s string, isXhtml bool) string {
var br string
if isXhtml {
br = "<br />"
} else {
br = "<br>"
}
var buf bytes.Buffer
runes := []rune(s)
length := len(runes)
for i, r := range runes {
switch r {
case '\n':
// \n\r(罕见顺序)作为一个换行处理;普通 \n 单独处理。
// N4:原条件含 r == '\r' 半,但本 case 内 r 恒为 '\n',该半恒假,已清理。)
if i+1 < length && runes[i+1] == '\r' {
buf.WriteString(br)
continue
}
buf.WriteString(br)
case '\r':
// \r\n 由上面的 \n 分支处理(\r 在此跳过);单独 \r 作为一个换行。
if i+1 < length && runes[i+1] == '\n' {
continue
}
buf.WriteString(br)
default:
buf.WriteRune(r)
}
}
return buf.String()
}