Files
pure-note/internal/httpapi/upload_pixel_test.go
T
wangairnan 1272d680a8 fix(httpapi): no-store 接线、图片缓存头时序、解压炸弹上限与 RSS 空日期
/api/admin/* 挂 NoStore、/api/me 内联 no-store(P1-3); 图片缓存头移至数据读取成功后,404 不携带 public immutable(P2-9); 上传先 DecodeConfig 限制像素 ≤2^25 再解码(P2-12); 无公开笔记时省略 lastBuildDate(P2-10)。
2026-09-08 17:32:52 +08:00

72 lines
2.4 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 httpapi
import (
"bytes"
"encoding/binary"
"hash/crc32"
"image"
"net/http"
"testing"
)
// pngWithDims 构造仅含文件签名 + IHDR 的 PNG 头(DecodeConfig 只解析头部即可
// 得到宽高,无需真实像素数据;CRC 按 PNG 规范计算)。
func pngWithDims(w, h uint32) []byte {
ihdr := make([]byte, 13)
binary.BigEndian.PutUint32(ihdr[0:4], w)
binary.BigEndian.PutUint32(ihdr[4:8], h)
// 8bit / truecolor / deflate / adaptive / no interlace
ihdr[8], ihdr[9], ihdr[10], ihdr[11], ihdr[12] = 8, 2, 0, 0, 0
chunk := bytes.NewBuffer(nil)
_ = binary.Write(chunk, binary.BigEndian, uint32(len(ihdr)))
chunk.WriteString("IHDR")
chunk.Write(ihdr)
_ = binary.Write(chunk, binary.BigEndian, crc32.ChecksumIEEE(chunk.Bytes()[4:]))
out := bytes.NewBuffer(nil)
out.Write([]byte{0x89, 'P', 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A})
out.Write(chunk.Bytes())
return out.Bytes()
}
// TestUploadPixelBomb 解压炸弹防御(评审 round2 P2-12):小体积超大尺寸图片
// 在 DecodeConfig 阶段被 413 拒绝,不进入全量 Decode。
func TestUploadPixelBomb(t *testing.T) {
// 头部自证合法:DecodeConfig 可解析出宽高
bomb := pngWithDims(20000, 20000) // 4 亿像素 > 1<<25
if _, _, err := image.DecodeConfig(bytes.NewReader(bomb)); err != nil {
t.Fatalf("夹具应可解析出尺寸: %v", err)
}
// 正常小图不受影响
if _, _, err := image.DecodeConfig(bytes.NewReader(png1x1)); err != nil {
t.Fatalf("1x1 夹具应合法: %v", err)
}
e := newEnv(t)
c := e.loginAdmin()
// 手工 multipart(uploadPNG 辅助对非 201 会 Fatal)
var body bytes.Buffer
boundary := "bombboundary456"
body.WriteString("--" + boundary + "\r\n")
body.WriteString(`Content-Disposition: form-data; name="file"; filename="bomb.png"` + "\r\n")
body.WriteString("Content-Type: image/png\r\n\r\n")
body.Write(bomb)
body.WriteString("\r\n--" + boundary + "--\r\n")
req, err := http.NewRequest(http.MethodPost, e.ts.URL+"/api/admin/images", &body)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary)
req.Header.Set("Origin", e.ts.URL)
req.Header.Set("X-CSRF-Token", e.csrf)
resp, err := c.Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusRequestEntityTooLarge {
t.Fatalf("超大像素图片应 413,实际 %d", resp.StatusCode)
}
}