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"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
var profanityDetector = goaway.NewProfanityDetector()
|
var (
|
||||||
|
profanityDetector = goaway.NewProfanityDetector()
|
||||||
|
customFilter = NewCustomFilter()
|
||||||
|
)
|
||||||
|
|
||||||
type WebHandler struct {
|
type WebHandler struct {
|
||||||
db *database.DB
|
db *database.DB
|
||||||
@@ -57,6 +60,11 @@ func (h *WebHandler) ClaimSpace(c *gin.Context) {
|
|||||||
return
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -103,6 +111,15 @@ func (h *WebHandler) CheckSubdomain(c *gin.Context) {
|
|||||||
return
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
availabilityStatus.textContent = '✗ Contains inappropriate content';
|
availabilityStatus.textContent = '✗ Contains inappropriate content';
|
||||||
availabilityStatus.className = 'status taken';
|
availabilityStatus.className = 'status taken';
|
||||||
claimBtn.disabled = true;
|
claimBtn.disabled = true;
|
||||||
|
} else if (data.reason === 'reserved') {
|
||||||
|
availabilityStatus.textContent = '✗ This subdomain is reserved';
|
||||||
|
availabilityStatus.className = 'status taken';
|
||||||
|
claimBtn.disabled = true;
|
||||||
} else {
|
} else {
|
||||||
availabilityStatus.textContent = '✗ Already taken';
|
availabilityStatus.textContent = '✗ Already taken';
|
||||||
availabilityStatus.className = 'status taken';
|
availabilityStatus.className = 'status taken';
|
||||||
|
|||||||
Reference in New Issue
Block a user