fix: harden avatar upload path and sync review truth

This commit is contained in:
Your Name
2026-05-29 07:33:19 +08:00
parent 9cc5892565
commit 80c59e2c2c
4 changed files with 139 additions and 3 deletions

View File

@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
"github.com/gin-gonic/gin"
@@ -42,6 +43,21 @@ func generateSecureToken(length int) (string, error) {
return hex.EncodeToString(bytes)[:length], nil
}
func resolveAvatarUploadDir(baseDir string) (string, error) {
if baseDir == "" {
baseDir = "./uploads"
}
cleanRoot := filepath.Clean(baseDir)
if !filepath.IsAbs(cleanRoot) {
absRoot, err := filepath.Abs(cleanRoot)
if err != nil {
return "", fmt.Errorf("resolve upload root: %w", err)
}
cleanRoot = absRoot
}
return filepath.Join(cleanRoot, "avatars"), nil
}
// UploadAvatar 上传用户头像
// @Summary 上传用户头像
// @Description 上传并更新用户头像(仅本人或管理员)
@@ -92,7 +108,7 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
}
// Validate file type
ext := filepath.Ext(file.Filename)
ext := strings.ToLower(filepath.Ext(file.Filename))
allowedExts := map[string]bool{".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true}
if !allowedExts[ext] {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "invalid file type, allowed: jpg, jpeg, png, gif, webp"})
@@ -139,7 +155,11 @@ func (h *AvatarHandler) UploadAvatar(c *gin.Context) {
return
}
avatarFilename := fmt.Sprintf("avatar_%d_%s%s", userID, token, ext)
uploadDir := "./uploads/avatars"
uploadDir, err := resolveAvatarUploadDir("")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "failed to resolve upload directory"})
return
}
// Create upload directory if not exists
if err := os.MkdirAll(uploadDir, 0o755); err != nil {

View File

@@ -0,0 +1,33 @@
package handler
import (
"path/filepath"
"strings"
"testing"
)
func TestResolveAvatarUploadDir_DefaultRootBecomesAbsolute(t *testing.T) {
dir, err := resolveAvatarUploadDir("")
if err != nil {
t.Fatalf("resolveAvatarUploadDir() error = %v", err)
}
if !filepath.IsAbs(dir) {
t.Fatalf("resolveAvatarUploadDir() = %q, want absolute path", dir)
}
if !strings.HasSuffix(filepath.ToSlash(dir), "/uploads/avatars") {
t.Fatalf("resolveAvatarUploadDir() = %q, want suffix /uploads/avatars", dir)
}
}
func TestResolveAvatarUploadDir_CustomRootPreserved(t *testing.T) {
dir, err := resolveAvatarUploadDir("testdata/uploads-root")
if err != nil {
t.Fatalf("resolveAvatarUploadDir() error = %v", err)
}
if !filepath.IsAbs(dir) {
t.Fatalf("resolveAvatarUploadDir() = %q, want absolute path", dir)
}
if !strings.HasSuffix(filepath.ToSlash(dir), "/testdata/uploads-root/avatars") {
t.Fatalf("resolveAvatarUploadDir() = %q, want custom root suffix", dir)
}
}