diff --git a/internal/handlers/custom_filter.go b/internal/handlers/custom_filter.go new file mode 100644 index 0000000..99a353a --- /dev/null +++ b/internal/handlers/custom_filter.go @@ -0,0 +1,147 @@ +package handlers + +import ( + "strings" + "unicode" +) + +// CustomFilter contains terms related to DWS and Tanishq Dubey that should be blocked +type CustomFilter struct { + blockedTerms []string +} + +// NewCustomFilter creates a new custom filter with DWS and personal name variations +func NewCustomFilter() *CustomFilter { + return &CustomFilter{ + blockedTerms: []string{ + // DWS variations + "dws", + "dubey", + "dubeyweb", + "dubeywebservices", + "dubeyweb services", + "dubey-engineering", + "dubeyengineering", + "dwsengineering", + "dws-engineering", + "dws-engineering-llc", + "dwsengineeringllc", + "dwsllc", + "dws-llc", + "webservices", + "web-services", + "dubeycorp", + "dubey-corp", + "dubeyinc", + "dubey-inc", + + // Tanishq Dubey variations + "tanishq", + "tanishqdubey", + "tanishq-dubey", + "tdubey", + "t-dubey", + "tanishq-d", + "tdub", + "tanish", + "dubey-t", + "dubeytanishq", + "dubey-tanishq", + + // Leet speak variations + "dub3y", + "dub3yweb", + "t4nishq", + "t4n1shq", + "tan1shq", + "dub3y3ng1n33r1ng", + "dw5", + "dw$", + "dub3yc0rp", + + // Common combinations + "dubeydns", + "dubey-ddns", + "dwsdns", + "dws-ddns", + "tanishqdns", + "tanishq-ddns", + "tdubeydns", + "tdubey-ddns", + }, + } +} + +// IsBlocked checks if the given text contains any blocked terms +func (cf *CustomFilter) IsBlocked(text string) bool { + normalized := cf.normalize(text) + + for _, term := range cf.blockedTerms { + if strings.Contains(normalized, term) { + return true + } + } + + return false +} + +// normalize prepares text for comparison by: +// - Converting to lowercase +// - Removing common separators +// - Converting leet speak to normal text +func (cf *CustomFilter) normalize(text string) string { + // Convert to lowercase + text = strings.ToLower(text) + + // Remove separators + replacer := strings.NewReplacer( + "-", "", + "_", "", + ".", "", + " ", "", + ) + text = replacer.Replace(text) + + // Convert leet speak + text = cf.leetToNormal(text) + + return text +} + +// leetToNormal converts common leet speak characters to normal letters +func (cf *CustomFilter) leetToNormal(text string) string { + replacements := map[rune]rune{ + '0': 'o', + '1': 'i', + '3': 'e', + '4': 'a', + '5': 's', + '6': 'g', + '7': 't', + '8': 'b', + '9': 'g', + '@': 'a', + '$': 's', + '!': 'i', + '|': 'i', + '+': 't', + } + + result := make([]rune, len(text)) + for i, char := range text { + if replacement, ok := replacements[char]; ok { + result[i] = replacement + } else { + result[i] = unicode.ToLower(char) + } + } + + return string(result) +} + +// GetBlockedTerms returns a copy of the blocked terms list (for testing/debugging) +func (cf *CustomFilter) GetBlockedTerms() []string { + terms := make([]string, len(cf.blockedTerms)) + copy(terms, cf.blockedTerms) + return terms +} diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 91dbc32..7360311 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -16,7 +16,10 @@ import ( "github.com/gin-gonic/gin" ) -var profanityDetector = goaway.NewProfanityDetector() +var ( + profanityDetector = goaway.NewProfanityDetector() + customFilter = NewCustomFilter() +) type WebHandler struct { db *database.DB @@ -57,6 +60,11 @@ func (h *WebHandler) ClaimSpace(c *gin.Context) { return } + if customFilter.IsBlocked(subdomain) { + c.JSON(http.StatusBadRequest, gin.H{"error": "Subdomain is reserved"}) + return + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() @@ -103,6 +111,15 @@ func (h *WebHandler) CheckSubdomain(c *gin.Context) { return } + if customFilter.IsBlocked(subdomain) { + c.JSON(http.StatusOK, gin.H{ + "available": false, + "subdomain": subdomain, + "reason": "reserved", + }) + return + } + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() diff --git a/web/static/js/app.js b/web/static/js/app.js index e019346..ec331d5 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -37,6 +37,10 @@ document.addEventListener('DOMContentLoaded', function() { availabilityStatus.textContent = '✗ Contains inappropriate content'; availabilityStatus.className = 'status taken'; claimBtn.disabled = true; + } else if (data.reason === 'reserved') { + availabilityStatus.textContent = '✗ This subdomain is reserved'; + availabilityStatus.className = 'status taken'; + claimBtn.disabled = true; } else { availabilityStatus.textContent = '✗ Already taken'; availabilityStatus.className = 'status taken';