fix: audit fixes for websearch, notifications, and channel pricing
P0: fix wildcard matching test assertion (config order, not longest prefix) P0: add TotalRecharged to auth cache snapshot (v5) for percentage threshold P1: move pricing rules into per-platform sections in ChannelsView P1: populate account name cache when editing existing channel rules P1: sanitize email subject headers to prevent SMTP injection P1: make Redis INCR+EXPIRE idempotent for rate limiting P1: deep copy FeaturesConfig in Channel.Clone() P2: clean up stale email="" placeholder comments P2: replace log.Printf with slog in email_service.go
This commit is contained in:
@@ -7,7 +7,7 @@ import (
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math/big"
|
||||
"net/smtp"
|
||||
"net/url"
|
||||
@@ -292,7 +292,7 @@ func (s *EmailService) VerifyCode(ctx context.Context, email, code string) error
|
||||
if subtle.ConstantTimeCompare([]byte(data.Code), []byte(code)) != 1 {
|
||||
data.Attempts++
|
||||
if err := s.cache.SetVerificationCode(ctx, email, data, verifyCodeTTL); err != nil {
|
||||
log.Printf("[Email] Failed to update verification attempt count: %v", err)
|
||||
slog.Error("failed to update verification attempt count", "email", email, "error", err)
|
||||
}
|
||||
if data.Attempts >= maxVerifyCodeAttempts {
|
||||
return ErrVerifyCodeMaxAttempts
|
||||
@@ -302,7 +302,7 @@ func (s *EmailService) VerifyCode(ctx context.Context, email, code string) error
|
||||
|
||||
// 验证成功,删除验证码
|
||||
if err := s.cache.DeleteVerificationCode(ctx, email); err != nil {
|
||||
log.Printf("[Email] Failed to delete verification code after success: %v", err)
|
||||
slog.Error("failed to delete verification code after success", "email", email, "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -452,7 +452,7 @@ func (s *EmailService) SendPasswordResetEmail(ctx context.Context, email, siteNa
|
||||
func (s *EmailService) SendPasswordResetEmailWithCooldown(ctx context.Context, email, siteName, resetURL string) error {
|
||||
// Check email cooldown to prevent email bombing
|
||||
if s.cache.IsPasswordResetEmailInCooldown(ctx, email) {
|
||||
log.Printf("[Email] Password reset email skipped (cooldown): %s", email)
|
||||
slog.Info("password reset email skipped due to cooldown", "email", email)
|
||||
return nil // Silent success to prevent revealing cooldown to attackers
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ func (s *EmailService) SendPasswordResetEmailWithCooldown(ctx context.Context, e
|
||||
|
||||
// Set cooldown marker (Redis TTL handles expiration)
|
||||
if err := s.cache.SetPasswordResetEmailCooldown(ctx, email, passwordResetEmailCooldown); err != nil {
|
||||
log.Printf("[Email] Failed to set password reset cooldown for %s: %v", email, err)
|
||||
slog.Error("failed to set password reset cooldown", "email", email, "error", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -493,7 +493,7 @@ func (s *EmailService) ConsumePasswordResetToken(ctx context.Context, email, tok
|
||||
|
||||
// Delete after verification (one-time use)
|
||||
if err := s.cache.DeletePasswordResetToken(ctx, email); err != nil {
|
||||
log.Printf("[Email] Failed to delete password reset token after consumption: %v", err)
|
||||
slog.Error("failed to delete password reset token after consumption", "email", email, "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user