Add custom filter for DWS and Tanishq Dubey trademarks
- Create custom_filter.go with DWS/Tanishq Dubey term detection - Block variations including: - DWS, Dubey Web Services, DWS Engineering LLC - Tanishq Dubey, tdubey - Leet speak variations (dub3y, t4nishq, dw5, etc.) - Combined terms (dubeydns, dws-ddns, etc.) - Update frontend to show 'reserved' message for blocked terms - Filters are case-insensitive and handle separators Protects brand identity and personal name from being used in user subdomains
This commit is contained in:
147
internal/handlers/custom_filter.go
Normal file
147
internal/handlers/custom_filter.go
Normal file
@@ -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
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user