- #9 CDN 本地化:marked/DOMPurify/highlight.js/cropperjs/easymde 入 static/vendor(go:embed),Tailwind 改静态构建(scripts/build_tailwind.sh),CSP 收紧为 default-src 'self' - #10 登录限速:IP+用户名维度 5 次失败锁 15 分钟,内存实现有界(handlers/login_ratelimit.go) - #25 计时侧信道:用户不存在时执行 dummy bcrypt 抹平时间差(随 #10 实施) - #11 配置文件权限 0640 - #12 首启随机一次性密码(弃用 admin/admin) - #13 unix socket 660 + 代理用户加组提示 - #22 storage_dir 路径穿越校验(单安全路径段) - #23 密码最小长度统一(改密/建号/重置),#24 邮箱格式统一校验 - 新增 13 个单元测试;go test ./... 含 -race 全绿
25 lines
616 B
Go
25 lines
616 B
Go
package models
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestRandomAdminPassword covers SECURITY_TODO #12: the first-run admin
|
|
// password comes from the ambiguous-safe alphabet, has fixed length, and
|
|
// differs between generations.
|
|
func TestRandomAdminPassword(t *testing.T) {
|
|
pw := randomAdminPassword()
|
|
if len(pw) != 16 {
|
|
t.Fatalf("password length = %d, want 16", len(pw))
|
|
}
|
|
for _, c := range pw {
|
|
if !strings.ContainsRune(adminPasswordAlphabet, c) {
|
|
t.Fatalf("password contains rune %q outside alphabet", c)
|
|
}
|
|
}
|
|
if pw == randomAdminPassword() {
|
|
t.Fatal("two generated passwords are identical")
|
|
}
|
|
}
|