fix: harden avatar upload path and sync review truth
This commit is contained in:
@@ -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 {
|
||||
|
||||
33
internal/api/handler/avatar_handler_path_test.go
Normal file
33
internal/api/handler/avatar_handler_path_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user