fix: round-2 audit fixes — security, code quality, and UI improvements
Security (HIGH): - Normalize all Redis cache keys to lowercase (verifyCode, passwordReset) - Fix verify code TTL renewal on failed attempts: use remaining TTL via ExpiresAt field instead of resetting to full 15-minute window - Add 3 missing fields to diffSettings audit log (promo_code, invitation_code, custom_endpoints) Code quality (MEDIUM): - Extract filterVerifiedEmails shared helper (balance_notify_service.go) - Add Pricing array non-empty validation for channel pricing rules - Add platform token semantics comment in gateway_service.go - Complete validatePlanPatch test coverage (+10 test cases) - Replace string types with QuotaThresholdType/QuotaResetMode across frontend - Remove duplicate getPlatformTextColor/getRateBadgeClass in ChannelsView - Return EMAIL_NOT_FOUND error on RemoveNotifyEmail miss UI improvements: - Reorder cost tooltip: user billing above separator, account billing below - Add NaN guard to accountBilled function - Move timezone selector inline into reset-mode row (no longer standalone)
This commit is contained in:
@@ -330,6 +330,7 @@ func saveNotifyVerifyCode(ctx context.Context, cache EmailCache, email, code str
|
||||
Code: code,
|
||||
Attempts: 0,
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(verifyCodeTTL),
|
||||
}
|
||||
if err := cache.SetNotifyVerifyCode(ctx, email, data, verifyCodeTTL); err != nil {
|
||||
return fmt.Errorf("save verify code: %w", err)
|
||||
@@ -370,7 +371,11 @@ func verifyNotifyCode(ctx context.Context, cache EmailCache, email, code string)
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(data.Code), []byte(code)) != 1 {
|
||||
data.Attempts++
|
||||
if err := cache.SetNotifyVerifyCode(ctx, email, data, verifyCodeTTL); err != nil {
|
||||
remaining := time.Until(data.ExpiresAt)
|
||||
if remaining <= 0 {
|
||||
return ErrInvalidVerifyCode
|
||||
}
|
||||
if err := cache.SetNotifyVerifyCode(ctx, email, data, remaining); err != nil {
|
||||
slog.Error("failed to update notify verify code attempts", "email", email, "error", err)
|
||||
}
|
||||
if data.Attempts >= maxVerifyCodeAttempts {
|
||||
@@ -418,11 +423,17 @@ func (s *UserService) RemoveNotifyEmail(ctx context.Context, userID int64, email
|
||||
}
|
||||
|
||||
filtered := make([]NotifyEmailEntry, 0, len(user.BalanceNotifyExtraEmails))
|
||||
found := false
|
||||
for _, e := range user.BalanceNotifyExtraEmails {
|
||||
if !strings.EqualFold(e.Email, email) {
|
||||
if strings.EqualFold(e.Email, email) {
|
||||
found = true
|
||||
} else {
|
||||
filtered = append(filtered, e)
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return infraerrors.BadRequest("EMAIL_NOT_FOUND", "notification email not found")
|
||||
}
|
||||
user.BalanceNotifyExtraEmails = filtered
|
||||
return s.userRepo.Update(ctx, user)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user