diff --git a/internal/logic/auth/trialEmailWhitelist.go b/internal/logic/auth/trialEmailWhitelist.go index ae15855..de7b0c1 100644 --- a/internal/logic/auth/trialEmailWhitelist.go +++ b/internal/logic/auth/trialEmailWhitelist.go @@ -38,7 +38,35 @@ func ShouldGrantTrialForEmail(register config.RegisterConfig, email string) bool if register.TrialEmailDomainWhitelist == "" { return false } - return IsEmailDomainWhitelisted(email, register.TrialEmailDomainWhitelist) + if !IsEmailDomainWhitelisted(email, register.TrialEmailDomainWhitelist) { + return false + } + // Gmail 系域名:local part 含点号或 + 别名的视为泛域名,直接拒绝赠送 + if IsDisposableAlias(email) { + return false + } + return true +} + +// IsDisposableAlias detects Gmail dot trick and + alias abuse. +// For Gmail-like domains, local part containing "." or "+" is rejected. +// For all other domains, only "+" alias is rejected. +func IsDisposableAlias(email string) bool { + parts := strings.SplitN(strings.ToLower(strings.TrimSpace(email)), "@", 2) + if len(parts) != 2 { + return false + } + local, domain := parts[0], parts[1] + + // All domains: reject + alias + if strings.ContainsRune(local, '+') { + return true + } + // Gmail-like domains: reject dots in local part + if isGmailLikeDomain(domain) && strings.ContainsRune(local, '.') { + return true + } + return false } // NormalizeEmail returns a canonical form of the email for trial deduplication.