mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 10:42:57 +00:00
168 lines
4.0 KiB
Go
168 lines
4.0 KiB
Go
package validation
|
|
|
|
import (
|
|
"errors"
|
|
"regexp"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// ValidateCommentContent checks if comment content meets requirements
|
|
func ValidateCommentContent(content string) error {
|
|
c := strings.TrimSpace(content)
|
|
if c == "" {
|
|
return errors.New("comment cannot be empty")
|
|
}
|
|
length := utf8.RuneCountInString(c)
|
|
if length < 6 {
|
|
return errors.New("comment too short (minimum 6 characters)")
|
|
}
|
|
if length > 2000 {
|
|
return errors.New("comment too long (maximum 2000 characters)")
|
|
}
|
|
// Check for excessive caps (anti-spam)
|
|
if isExcessiveCaps(c) {
|
|
return errors.New("too many capital letters detected")
|
|
}
|
|
// Check for excessive repetition
|
|
if hasExcessiveRepetition(c) {
|
|
return errors.New("excessive character repetition detected")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateCommentTargetType checks if target type is allowed
|
|
func ValidateCommentTargetType(targetType string) error {
|
|
allowed := map[string]bool{
|
|
"article": true,
|
|
"event": true,
|
|
"gallery_album": true,
|
|
"youtube_video": true,
|
|
}
|
|
if !allowed[targetType] {
|
|
return errors.New("invalid target type")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateCommentStatus checks if status is valid
|
|
func ValidateCommentStatus(status string) error {
|
|
if status != "visible" && status != "hidden" {
|
|
return errors.New("status must be 'visible' or 'hidden'")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateBanReason checks ban reason
|
|
func ValidateBanReason(reason string) error {
|
|
r := strings.TrimSpace(reason)
|
|
if r == "" {
|
|
return errors.New("ban reason cannot be empty")
|
|
}
|
|
if len(r) > 500 {
|
|
return errors.New("ban reason too long (max 500 characters)")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateBanDuration checks ban duration in hours
|
|
func ValidateBanDuration(hours int) error {
|
|
if hours < 0 {
|
|
return errors.New("ban duration cannot be negative (use 0 for permanent)")
|
|
}
|
|
if hours > 8760 { // More than 1 year
|
|
return errors.New("ban duration too long (max 1 year = 8760 hours)")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Helper: check if more than 50% of letters are uppercase
|
|
func isExcessiveCaps(text string) bool {
|
|
if len(text) < 20 {
|
|
return false // Allow caps in short messages
|
|
}
|
|
var upper, lower int
|
|
for _, r := range text {
|
|
if r >= 'A' && r <= 'Z' {
|
|
upper++
|
|
} else if r >= 'a' && r <= 'z' {
|
|
lower++
|
|
}
|
|
}
|
|
total := upper + lower
|
|
if total == 0 {
|
|
return false
|
|
}
|
|
return float64(upper)/float64(total) > 0.5
|
|
}
|
|
|
|
// Helper: detect excessive character repetition (e.g., "aaaaaa", "!!!!!!")
|
|
func hasExcessiveRepetition(text string) bool {
|
|
// Check for 5+ consecutive identical characters without using backreferences (unsupported in Go regex)
|
|
run := 1
|
|
var prev rune
|
|
first := true
|
|
for _, r := range text {
|
|
if first {
|
|
prev = r
|
|
first = false
|
|
continue
|
|
}
|
|
if r == prev {
|
|
run++
|
|
if run >= 5 {
|
|
return true
|
|
}
|
|
} else {
|
|
prev = r
|
|
run = 1
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SanitizeCommentContent removes dangerous HTML but preserves basic formatting
|
|
func SanitizeCommentContent(content string) string {
|
|
// Remove script tags and their content
|
|
re := regexp.MustCompile(`(?i)<script[^>]*>.*?</script>`)
|
|
content = re.ReplaceAllString(content, "")
|
|
|
|
// Remove iframe tags
|
|
re = regexp.MustCompile(`(?i)<iframe[^>]*>.*?</iframe>`)
|
|
content = re.ReplaceAllString(content, "")
|
|
|
|
// Remove on* event handlers
|
|
re = regexp.MustCompile(`(?i)\s*on\w+\s*=\s*["'][^"']*["']`)
|
|
content = re.ReplaceAllString(content, "")
|
|
|
|
return strings.TrimSpace(content)
|
|
}
|
|
|
|
// ValidateReportReason checks report reason
|
|
func ValidateReportReason(reason string) error {
|
|
r := strings.TrimSpace(reason)
|
|
if len(r) > 255 {
|
|
return errors.New("report reason too long (max 255 characters)")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateReactionType checks if reaction type is valid
|
|
func ValidateReactionType(reactionType string) error {
|
|
allowed := map[string]bool{
|
|
"like": true,
|
|
"heart": true,
|
|
"smile": true,
|
|
"laugh": true,
|
|
"surprised": true,
|
|
"thumbs_up": true,
|
|
"thumbs_down": true,
|
|
"sad": true,
|
|
"angry": true,
|
|
}
|
|
if !allowed[reactionType] {
|
|
return errors.New("invalid reaction type")
|
|
}
|
|
return nil
|
|
}
|