chore: baseline snapshot before moderation-strictness feature

Repo .git was damaged (refs/ missing, index truncated, object store near-empty).
Repaired via git init (rebuilt refs), stale index preserved as .git/index.corrupt-20260910.bak,
then full worktree committed as a rollback baseline.
This commit is contained in:
tamakyi
2026-09-10 10:56:27 +08:00
commit e3e59e7c69
296 changed files with 67458 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
// Package imagecheck 提供图片尺寸校验,用于在上传/解码前拦截"解压炸弹"。
package imagecheck
import (
"errors"
"fmt"
"image"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io"
"math"
)
// maxImageSide 是单边最大像素,MaxImagePixels 为其平方。
const maxImageSide = 4096
// MaxImageSide 导出单边最大像素,供上层在错误提示中向用户展示具体限制。
const MaxImageSide = maxImageSide
// MaxImagePixels 限制解码后的最大像素总量(4096×4096),用于拦截"解压炸弹":
// 超大纯色 PNG/GIF 压缩后体积很小,但 image.Decode 会按宽×高分配内存导致 OOM。
const MaxImagePixels = maxImageSide * maxImageSide
// ErrImageTooLarge 表示图片像素总量超过 MaxImagePixels,
// 是所有"图片尺寸超限"的统一错误,供各上传入口识别并展示统一文案。
var ErrImageTooLarge = errors.New(fmt.Sprintf(
"图片尺寸过大,最大支持 %d×%d 像素,请压缩后重新上传",
maxImageSide, maxImageSide,
))
// CheckPixelLimit 仅读取图片头部(image.DecodeConfig)校验宽高与像素总量,
// 在完整解码前拦截超大图片,避免内存耗尽。
//
// 说明:对 GIF 同样适用——image.DecodeConfig 对 GIF 只读取逻辑屏幕宽高(不解码帧),
// 成本极低;但大逻辑屏幕的 GIF 在后续 image.Decode 时仍会按屏幕尺寸分配内存,故必须校验。
//
// 注意:该方法会读取(消耗)传入的 reader,调用方需自行重建/复位 reader。
func CheckPixelLimit(r io.Reader) error {
cfg, _, err := image.DecodeConfig(r)
if err != nil {
return err
}
if cfg.Width <= 0 || cfg.Height <= 0 {
return nil
}
if int64(cfg.Width)*int64(cfg.Height) > MaxImagePixels {
return ErrImageTooLarge
}
return nil
}
// ErrBadAspectRatio 表示图片宽高比不符合用途要求(如头像必须 1:1、背景必须 16:9)。
// 由 storage 在头像/背景上传入口校验后返回,route 层负责映射为对应语言的提示文案。
var ErrBadAspectRatio = errors.New("image aspect ratio does not meet the requirement")
// DefaultAspectTolerance 是宽高比允许的绝对偏差。
// 客户端裁剪输出即为精确比例,此处仅用于拦截绕过裁剪直传的明显不符图片,
// 因此使用窄容差(±0.01);宽高比越界(如 4:3 传成 16:9)会稳定触发拒绝。
const DefaultAspectTolerance = 0.01
// CheckAspectRatio 仅读取图片头部校验宽高比是否接近 wantW:wantH(绝对偏差 ≤ tolerance)。
// 与 CheckPixelLimit 一致:只做 DecodeConfig 级轻量检查,不解码全图。
// 注意:该方法会读取(消耗)传入的 reader,调用方需自行重建/复位 reader。
func CheckAspectRatio(r io.Reader, wantW, wantH float64, tolerance float64) error {
if wantW <= 0 || wantH <= 0 {
return nil
}
cfg, _, err := image.DecodeConfig(r)
if err != nil {
return err
}
if cfg.Width <= 0 || cfg.Height <= 0 {
return nil
}
actual := float64(cfg.Width) / float64(cfg.Height)
if math.Abs(actual-wantW/wantH) > tolerance {
return ErrBadAspectRatio
}
return nil
}