Files
platforms/backend/api/internal/logic/upload/avatar_test.go
czl231 7242048abf feat: 新增账户资料页与头像上传
将工作人员和用户的详情、编辑改为独立账户资料页。

增加受控头像上传与读取、图片安全校验、接口测试,并优化只读及编辑布局。

同步更新平台需求、接口安全说明、项目文档和操作日志。
2026-08-10 22:35:47 +08:00

71 lines
2.3 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 upload 测试头像上传的格式、尺寸与路径安全边界。
// 版本v1.0.0
package upload
import (
"bytes"
"image"
"image/color"
"image/png"
"path/filepath"
"strings"
"testing"
)
// pngBytes 生成指定尺寸的有效 PNG 测试图片。
func pngBytes(t *testing.T, width, height int) []byte {
t.Helper()
picture := image.NewRGBA(image.Rect(0, 0, width, height))
picture.Set(0, 0, color.RGBA{R: 32, G: 96, B: 192, A: 255})
var buffer bytes.Buffer
if err := png.Encode(&buffer, picture); err != nil {
t.Fatalf("encode PNG: %v", err)
}
return buffer.Bytes()
}
// TestValidateAvatarAcceptsRealPNG 验证真实 PNG 可通过并规范化类型。
func TestValidateAvatarAcceptsRealPNG(t *testing.T) {
extension, contentType, err := validateAvatar("头像.PNG", pngBytes(t, 2, 2))
if err != nil {
t.Fatalf("valid PNG was rejected: %v", err)
}
if extension != ".png" || contentType != "image/png" {
t.Fatalf("avatar metadata = (%q, %q)", extension, contentType)
}
}
// TestValidateAvatarRejectsSpoofedAndOversizedImage 验证伪造扩展名与超大像素尺寸会被拒绝。
func TestValidateAvatarRejectsSpoofedAndOversizedImage(t *testing.T) {
if _, _, err := validateAvatar("fake.png", []byte("not an image")); err == nil {
t.Fatal("spoofed PNG was accepted")
}
if _, _, err := validateAvatar("wide.png", pngBytes(t, maxAvatarDimension+1, 1)); err == nil {
t.Fatal("oversized image dimensions were accepted")
}
}
// TestAvatarPathFromURIStaysInsideControlledRoot 验证头像 URI 不能逃逸受控目录。
func TestAvatarPathFromURIStaysInsideControlledRoot(t *testing.T) {
root := t.TempDir()
t.Setenv("HEQI_UPLOAD_DIR", root)
path, err := avatarPathFromURI("/uploads/avatars/2026/08/10/example.png")
if err != nil {
t.Fatalf("controlled URI was rejected: %v", err)
}
expectedRoot := filepath.Join(root, "avatars")
relative, err := filepath.Rel(expectedRoot, path)
if err != nil || relative == ".." || strings.HasPrefix(relative, ".."+string(filepath.Separator)) {
t.Fatalf("avatar path escaped root: %q", path)
}
for _, uri := range []string{
"https://example.com/avatar.png",
"/uploads/example.png",
"/uploads/avatars/../../secret.png",
} {
if _, err := avatarPathFromURI(uri); err == nil {
t.Fatalf("unsafe avatar URI was accepted: %q", uri)
}
}
}