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:
@@ -80,7 +80,7 @@ func MaskEmail(email string) string {
|
||||
if email == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
prefix := email[:3]
|
||||
suffix := email[strings.Index(email, "@"):]
|
||||
return prefix + "***" + suffix
|
||||
|
||||
@@ -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 条
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user