test: add comprehensive test coverage and improve code quality

- Add new test files for auth, service, and handler modules
- Improve test organization and coverage
- Refactor code for better maintainability
- Add captcha, settings, stats, and theme handler tests
- Add auth module tests (CAS, OAuth, password, SSO, state)
- Add service layer tests for auth, export, permissions, roles
- All Go tests pass (exit code 0)
- All frontend tests pass (325 tests in 59 files)
This commit is contained in:
2026-04-17 20:43:50 +08:00
parent 0d66aa0423
commit 582ad7a069
136 changed files with 19010 additions and 8544 deletions

View File

@@ -80,7 +80,7 @@ func MaskEmail(email string) string {
if email == "" {
return ""
}
prefix := email[:3]
suffix := email[strings.Index(email, "@"):]
return prefix + "***" + suffix

View File

@@ -185,22 +185,22 @@ func validateIPOrCIDR(s string) error {
type AnomalyEvent string
const (
AnomalyBruteForce AnomalyEvent = "brute_force" // 暴力破解(短时间大量失败)
AnomalyNewLocation AnomalyEvent = "new_location" // 新地区登录
AnomalyMultipleIP AnomalyEvent = "multiple_ip" // 短时间内多个 IP 登录
AnomalyOffHours AnomalyEvent = "off_hours" // 非工作时间登录(可配置)
AnomalyNewDevice AnomalyEvent = "new_device" // 新设备登录
AnomalySuspicious AnomalyEvent = "suspicious" // 可疑活动(综合判断)
AnomalyBruteForce AnomalyEvent = "brute_force" // 暴力破解(短时间大量失败)
AnomalyNewLocation AnomalyEvent = "new_location" // 新地区登录
AnomalyMultipleIP AnomalyEvent = "multiple_ip" // 短时间内多个 IP 登录
AnomalyOffHours AnomalyEvent = "off_hours" // 非工作时间登录(可配置)
AnomalyNewDevice AnomalyEvent = "new_device" // 新设备登录
AnomalySuspicious AnomalyEvent = "suspicious" // 可疑活动(综合判断)
)
// LoginRecord 登录记录
type LoginRecord struct {
UserID int64
IP string
Location string // 登录地区
UserID int64
IP string
Location string // 登录地区
DeviceFingerprint string // 设备指纹
Success bool
Timestamp time.Time
Success bool
Timestamp time.Time
}
// AnomalyDetector 异常登录检测器
@@ -232,11 +232,11 @@ type AnomalyDetectorConfig struct {
// DefaultAnomalyConfig 默认配置
var DefaultAnomalyConfig = AnomalyDetectorConfig{
MaxRecordsPerUser: 100,
Window: 15 * time.Minute,
MaxFailures: 10,
MaxDistinctIPs: 5,
AutoBlockDuration: 30 * time.Minute,
MaxRecordsPerUser: 100,
Window: 15 * time.Minute,
MaxFailures: 10,
MaxDistinctIPs: 5,
AutoBlockDuration: 30 * time.Minute,
KnownLocationsLimit: 5,
KnownDevicesLimit: 10,
}
@@ -271,12 +271,12 @@ func (d *AnomalyDetector) RecordLogin(_ context.Context, userID int64, ip, locat
now := time.Now()
record := LoginRecord{
UserID: userID,
IP: ip,
Location: location,
UserID: userID,
IP: ip,
Location: location,
DeviceFingerprint: deviceFingerprint,
Success: success,
Timestamp: now,
Success: success,
Timestamp: now,
}
// 追加记录,保留最新的 maxRecords 条

View File

@@ -79,17 +79,17 @@ func (v *Validator) SanitizeSQL(input string) string {
// Remove common SQL injection patterns that could bypass quoting
dangerousPatterns := []string{
`;[\s]*--`, // SQL comment
`/\*.*?\*/`, // Block comment (non-greedy)
`\bxp_\w+`, // Extended stored procedures
`\bexec[\s\(]`, // EXEC statements
`\bsp_\w+`, // System stored procedures
`\bwaitfor[\s]+delay`, // Time-based blind SQL injection
`\bunion[\s]+select`, // UNION injection
`\bdrop[\s]+table`, // DROP TABLE
`\binsert[\s]+into`, // INSERT
`;[\s]*--`, // SQL comment
`/\*.*?\*/`, // Block comment (non-greedy)
`\bxp_\w+`, // Extended stored procedures
`\bexec[\s\(]`, // EXEC statements
`\bsp_\w+`, // System stored procedures
`\bwaitfor[\s]+delay`, // Time-based blind SQL injection
`\bunion[\s]+select`, // UNION injection
`\bdrop[\s]+table`, // DROP TABLE
`\binsert[\s]+into`, // INSERT
`\bupdate[\s]+\w+[\s]+set`, // UPDATE
`\bdelete[\s]+from`, // DELETE
`\bdelete[\s]+from`, // DELETE
}
result := replacer.Replace(input)
@@ -108,20 +108,20 @@ func (v *Validator) SanitizeSQL(input string) string {
func (v *Validator) SanitizeXSS(input string) string {
// Remove dangerous tags and attributes using pattern matching
dangerousPatterns := []struct {
pattern string
replaceAll bool
pattern string
replaceAll bool
}{
{`(?i)<script[^>]*>.*?</script>`, true}, // Script tags
{`(?i)</script>`, false}, // Closing script
{`(?i)<iframe[^>]*>.*?</iframe>`, true}, // Iframe injection
{`(?i)<object[^>]*>.*?</object>`, true}, // Object injection
{`(?i)<embed[^>]*>.*?</embed>`, true}, // Embed injection
{`(?i)<applet[^>]*>.*?</applet>`, true}, // Applet injection
{`(?i)javascript\s*:`, false}, // JavaScript protocol
{`(?i)vbscript\s*:`, false}, // VBScript protocol
{`(?i)data\s*:`, false}, // Data URL protocol
{`(?i)on\w+\s*=`, false}, // Event handlers
{`(?i)<style[^>]*>.*?</style>`, true}, // Style injection
{`(?i)<script[^>]*>.*?</script>`, true}, // Script tags
{`(?i)</script>`, false}, // Closing script
{`(?i)<iframe[^>]*>.*?</iframe>`, true}, // Iframe injection
{`(?i)<object[^>]*>.*?</object>`, true}, // Object injection
{`(?i)<embed[^>]*>.*?</embed>`, true}, // Embed injection
{`(?i)<applet[^>]*>.*?</applet>`, true}, // Applet injection
{`(?i)javascript\s*:`, false}, // JavaScript protocol
{`(?i)vbscript\s*:`, false}, // VBScript protocol
{`(?i)data\s*:`, false}, // Data URL protocol
{`(?i)on\w+\s*=`, false}, // Event handlers
{`(?i)<style[^>]*>.*?</style>`, true}, // Style injection
}
result := input