From d73b73ac892b6e576b2d7f9106eeb85c38c1e847 Mon Sep 17 00:00:00 2001 From: Patrick Britton Date: Fri, 6 Feb 2026 17:26:02 -0600 Subject: [PATCH] Remove hardcoded reserved names - all reserved usernames now DB-only via admin console --- .../services/username_validation_service.go | 171 ++---------------- 1 file changed, 11 insertions(+), 160 deletions(-) diff --git a/go-backend/internal/services/username_validation_service.go b/go-backend/internal/services/username_validation_service.go index 7a623b6..d9a60f1 100644 --- a/go-backend/internal/services/username_validation_service.go +++ b/go-backend/internal/services/username_validation_service.go @@ -22,32 +22,9 @@ type UsernameCheckResult struct { Message string } -// ValidateUsernameWithDB checks a handle against reserved names (hardcoded + DB), -// inappropriate words, and format rules. +// ValidateUsernameWithDB checks a handle against the reserved_usernames DB table, +// inappropriate words, and format rules. All reserved names are managed via admin console. func ValidateUsernameWithDB(ctx context.Context, pool *pgxpool.Pool, handle string) UsernameCheckResult { - result := ValidateUsername(handle) - if result.Violation != UsernameOK { - return result - } - - // Also check DB reserved_usernames table - if pool != nil { - var count int - err := pool.QueryRow(ctx, `SELECT COUNT(*) FROM reserved_usernames WHERE username = $1`, strings.ToLower(strings.TrimSpace(handle))).Scan(&count) - if err == nil && count > 0 { - return UsernameCheckResult{ - UsernameReserved, - "This username is reserved. If you officially represent this brand, company, or public figure, you can submit a verification request at support@sojorn.net to claim it.", - } - } - } - - return UsernameCheckResult{UsernameOK, ""} -} - -// ValidateUsername checks a handle against reserved names, inappropriate words, -// and format rules. Returns a result with a user-facing message. -func ValidateUsername(handle string) UsernameCheckResult { h := strings.ToLower(strings.TrimSpace(handle)) // Format check @@ -58,11 +35,15 @@ func ValidateUsername(handle string) UsernameCheckResult { return UsernameCheckResult{UsernameInvalidFormat, "Username can only contain letters, numbers, underscores, and periods."} } - // Reserved check - if isReserved(h) { - return UsernameCheckResult{ - UsernameReserved, - "This username is reserved. If you officially represent this brand, company, or public figure, you can submit a verification request at support@sojorn.net to claim it.", + // Reserved check — DB only + if pool != nil { + var count int + err := pool.QueryRow(ctx, `SELECT COUNT(*) FROM reserved_usernames WHERE username = $1`, h).Scan(&count) + if err == nil && count > 0 { + return UsernameCheckResult{ + UsernameReserved, + "This username is reserved. If you officially represent this brand, company, or public figure, you can submit a verification request at support@sojorn.net to claim it.", + } } } @@ -88,136 +69,6 @@ func ValidateDisplayName(name string) UsernameCheckResult { var validHandleRegex = regexp.MustCompile(`^[a-z0-9_.]+$`) -// ------------------------------------------------------------------- -// Reserved usernames -// ------------------------------------------------------------------- - -func isReserved(h string) bool { - // Exact match - if reservedSet[h] { - return true - } - // Prefix match (e.g. "sojorn_anything", "admin_anything") - for _, prefix := range reservedPrefixes { - if strings.HasPrefix(h, prefix) { - return true - } - } - // Contains match for brand names that shouldn't appear even as substrings - for _, substr := range reservedSubstrings { - if strings.Contains(h, substr) { - return true - } - } - return false -} - -// Platform terms, system accounts, and roles -var platformReserved = []string{ - "sojorn", "admin", "administrator", "moderator", "mod", - "support", "help", "helpdesk", "system", "official", - "root", "superuser", "staff", "team", "security", - "abuse", "postmaster", "webmaster", "info", "contact", - "noreply", "no_reply", "mailer", "daemon", "bot", - "api", "dev", "developer", "ceo", "cto", "cfo", "coo", - "founder", "cofounder", "intern", "hr", - "legal", "compliance", "privacy", "terms", - "news", "press", "media", "blog", "status", - "feedback", "report", "bug", "feature", - "billing", "payment", "sales", "marketing", - "everyone", "all", "here", "channel", - "null", "undefined", "anonymous", "unknown", - "test", "testing", "demo", "example", - "signup", "signin", "login", "logout", "register", - "settings", "account", "profile", "dashboard", - "home", "feed", "explore", "discover", "search", - "notification", "notifications", "message", "messages", - "chat", "dm", "dms", "inbox", "outbox", - "verified", "verification", "verify", - "beacon", "beacons", "quip", "quips", -} - -// Major tech companies and social platforms -var techCompanyReserved = []string{ - "google", "apple", "microsoft", "amazon", "meta", - "facebook", "instagram", "twitter", "tiktok", "snapchat", - "linkedin", "reddit", "pinterest", "youtube", "twitch", - "discord", "telegram", "whatsapp", "signal", - "netflix", "spotify", "hulu", "disney", "disneyplus", - "openai", "chatgpt", "anthropic", "claude", - "nvidia", "amd", "intel", "samsung", "sony", - "tesla", "spacex", "nasa", "boeing", "airbus", - "uber", "lyft", "airbnb", "doordash", "grubhub", - "paypal", "stripe", "venmo", "cashapp", "zelle", - "coinbase", "binance", "robinhood", "fidelity", - "github", "gitlab", "stackoverflow", "atlassian", - "slack", "zoom", "teams", "webex", - "shopify", "squarespace", "wordpress", "wix", - "dropbox", "icloud", "onedrive", - "oracle", "ibm", "salesforce", "adobe", "canva", -} - -// Major brands and corporations -var brandReserved = []string{ - "nike", "adidas", "puma", "reebok", "underarmour", - "cocacola", "coca_cola", "pepsi", "starbucks", "mcdonalds", - "walmart", "target", "costco", "kroger", "wholefoods", - "bmw", "mercedes", "audi", "porsche", "ferrari", - "lamborghini", "ford", "chevrolet", "toyota", "honda", - "gucci", "louisvuitton", "chanel", "prada", "hermes", - "rolex", "cartier", "tiffany", "burberry", - "nfl", "nba", "mlb", "nhl", "mls", "fifa", "ufc", - "espn", "cnn", "bbc", "foxnews", "msnbc", "nytimes", - "washingtonpost", "wsj", "reuters", "apnews", - "marvel", "dccomics", "nintendo", "playstation", "xbox", - "paramount", "warner", "universal", -} - -// Public figures, politicians, and notable people -var publicFigureReserved = []string{ - "elonmusk", "elon_musk", "jeffbezos", "jeff_bezos", - "markzuckerberg", "mark_zuckerberg", "zuckerberg", - "timcook", "tim_cook", "billgates", "bill_gates", - "satyanadella", "sundarpichai", "samaltman", - "joebiden", "joe_biden", "donaldtrump", "donald_trump", - "barackobama", "barack_obama", "kamalaharris", - "taylorswift", "taylor_swift", "beyonce", "rihanna", - "drake", "kanyewest", "kanye_west", "ye", - "kimkardashian", "kim_kardashian", "kyliejenner", - "cristiano", "ronaldo", "messi", "lebronjames", "lebron_james", - "therock", "the_rock", "dwaynejohnson", - "mrbeaast", "mrbeast", "pewdiepie", "ninja", - "joerogan", "joe_rogan", "oprah", - "pope", "popefrancis", "dalailama", -} - -var reservedPrefixes = []string{ - "sojorn_", "sojorn.", "official_", "official.", - "admin_", "admin.", "mod_", "mod.", - "support_", "support.", "team_", "team.", - "staff_", "staff.", "system_", "system.", -} - -var reservedSubstrings = []string{ - "sojorn", -} - -var reservedSet map[string]bool - -func init() { - reservedSet = make(map[string]bool) - for _, lists := range [][]string{ - platformReserved, - techCompanyReserved, - brandReserved, - publicFigureReserved, - } { - for _, name := range lists { - reservedSet[name] = true - } - } -} - // ------------------------------------------------------------------- // Inappropriate content filter // -------------------------------------------------------------------