FileCodeBox Go 重写版 v2.5.6(安全审计修复版)
Go 1.27.1 (Gin+GORM) + Vue 3 文件快传服务: - 安全审计全部修复(docs/security-audit-2026-09-05.md): bcrypt 密码哈希与自动升级、presign 直传服务端大小/内容校验、 全局请求体上限、依赖升级(govulncheck 0 命中)、janitor 后台清理、 管理端审计动作落库、/admin CORS 收紧、通知内容白名单净化、 会话默认 7 天、限流缓存故障降级、robots.txt 端点等 - 前端:取件链接复制修复(不再重复拼接提取码)、markdown 净化器加固 - Redis 支持库号(FCB_REDIS_DB / redis://…/db URL) - 文档:docs/api/* 与 openapi.yaml 同步最新行为(robots.txt、 提码 5 位起、chunk 32MiB 上限、admin 审计动作等) 验证:gofmt/go vet/go test 全绿;二进制端到端冒烟通过
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"filecodebox/internal/response"
|
||||
)
|
||||
|
||||
const testSecret = "unit-test-secret-0123456789abcdef"
|
||||
|
||||
func init() { gin.SetMode(gin.TestMode) }
|
||||
|
||||
func TestSignAndVerifyAdminToken(t *testing.T) {
|
||||
token, expiresAt, err := SignAdminToken(testSecret, time.Hour)
|
||||
if err != nil {
|
||||
t.Fatalf("签发失败: %v", err)
|
||||
}
|
||||
if expiresAt.Before(time.Now()) {
|
||||
t.Fatal("过期时间不合理")
|
||||
}
|
||||
claims, err := VerifyAdminToken(testSecret, token)
|
||||
if err != nil {
|
||||
t.Fatalf("校验失败: %v", err)
|
||||
}
|
||||
if !claims.IsAdmin {
|
||||
t.Fatal("is_admin 应为 true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyTamperedToken(t *testing.T) {
|
||||
token, _, _ := SignAdminToken(testSecret, time.Hour)
|
||||
claims, err := VerifyAdminToken(testSecret+"-wrong", token)
|
||||
if err == nil || claims != nil {
|
||||
t.Fatal("密钥不匹配应校验失败")
|
||||
}
|
||||
// 篡改 payload
|
||||
tampered := token[:len(token)-3] + "abc"
|
||||
if _, err := VerifyAdminToken(testSecret, tampered); err == nil {
|
||||
t.Fatal("篡改的 token 应校验失败")
|
||||
}
|
||||
// 非 HMAC 算法拒绝
|
||||
algNone := "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc19hZG1pbiI6dHJ1ZX0."
|
||||
if _, err := VerifyAdminToken(testSecret, algNone); err == nil {
|
||||
t.Fatal("none 算法应被拒绝")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminAuthMiddleware(t *testing.T) {
|
||||
r := gin.New()
|
||||
r.GET("/protected", AdminAuth(func() string { return testSecret }), func(c *gin.Context) {
|
||||
response.OK(c, gin.H{"ok": true})
|
||||
})
|
||||
|
||||
// 无 token → 401
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, httptest.NewRequest("GET", "/protected", nil))
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("无 token 应 401: %d", w.Code)
|
||||
}
|
||||
// 有效 token → 200
|
||||
token, _, _ := SignAdminToken(testSecret, time.Hour)
|
||||
w = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/protected", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("有效 token 应 200: %d %s", w.Code, w.Body.String())
|
||||
}
|
||||
// 过期 token → 401
|
||||
expired, _, _ := SignAdminToken(testSecret, -time.Minute)
|
||||
w = httptest.NewRecorder()
|
||||
req = httptest.NewRequest("GET", "/protected", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+expired)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("过期 token 应 401: %d", w.Code)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user