Files
xingrin/server/internal/pkg/validator/target.go
yyhuni b540f69152 feat(worker): implement subdomain discovery workflow and enhance validation
- Rename IsSubdomainMatchTarget to IsSubdomainOfTarget for clarity
- Add subdomain discovery workflow with template loader and helpers
- Implement workflow registry for managing scan workflows
- Add domain validator package for input validation
- Create wordlist server component for managing DNS resolver lists
- Add template loader activity for dynamic template management
- Implement worker configuration module with environment setup
- Update worker dependencies to include projectdiscovery/utils and govalidator
- Consolidate workspace directory configuration (WORKSPACE_DIR replaces RESULTS_BASE_PATH)
- Update seed generator to use standardized bulk-create API endpoint
- Update all service layer calls to use renamed validation function
2026-01-17 21:15:02 +08:00

152 lines
3.4 KiB
Go

package validator
import (
"net"
"net/url"
"strings"
"github.com/asaskevich/govalidator"
)
// Target type constants
const (
TargetTypeDomain = "domain"
TargetTypeIP = "ip"
TargetTypeCIDR = "cidr"
)
// IsURLMatchTarget checks if URL hostname matches target
// Returns true if the URL's hostname belongs to the target
//
// Matching rules by target type:
// - domain: hostname equals target or ends with .target
// - ip: hostname must exactly equal target
// - cidr: hostname must be an IP within the CIDR range
func IsURLMatchTarget(urlStr, targetName, targetType string) bool {
if urlStr == "" || targetName == "" {
return false
}
parsed, err := url.Parse(urlStr)
if err != nil {
return false
}
hostname := strings.ToLower(parsed.Hostname())
if hostname == "" {
return false
}
targetName = strings.ToLower(targetName)
switch targetType {
case TargetTypeDomain:
// hostname equals target or ends with .target
return hostname == targetName || strings.HasSuffix(hostname, "."+targetName)
case TargetTypeIP:
// hostname must exactly equal target
return hostname == targetName
case TargetTypeCIDR:
// hostname must be an IP within the CIDR range
ip := net.ParseIP(hostname)
if ip == nil {
return false
}
_, network, err := net.ParseCIDR(targetName)
if err != nil {
return false
}
return network.Contains(ip)
default:
return false
}
}
// IsSubdomainOfTarget checks if subdomain belongs to target domain
// Returns true if subdomain is a valid DNS name and equals target or ends with .target
func IsSubdomainOfTarget(subdomain, targetDomain string) bool {
subdomain = strings.ToLower(strings.TrimSpace(subdomain))
targetDomain = strings.ToLower(strings.TrimSpace(targetDomain))
if subdomain == "" || targetDomain == "" {
return false
}
// Validate DNS name format
if !govalidator.IsDNSName(subdomain) {
return false
}
return subdomain == targetDomain || strings.HasSuffix(subdomain, "."+targetDomain)
}
// DetectTargetType auto-detects target type from input string.
// Returns empty string if the input is not a valid target format.
func DetectTargetType(name string) string {
name = strings.TrimSpace(name)
if name == "" {
return ""
}
// Check CIDR first (must be before IP check since "10.0.0.0" is valid for both)
if _, _, err := net.ParseCIDR(name); err == nil {
return TargetTypeCIDR
}
// Check IP
if net.ParseIP(name) != nil {
return TargetTypeIP
}
// Check if it looks like an IP but is invalid (e.g., 999.999.999.999)
// This prevents invalid IPs from being classified as domains
if looksLikeIP(name) {
return "" // Invalid IP format
}
// Check domain
if govalidator.IsDNSName(name) {
return TargetTypeDomain
}
return ""
}
// looksLikeIP checks if a string looks like an IP address format
// (e.g., "999.999.999.999" or "::gggg")
func looksLikeIP(s string) bool {
// Check for IPv4-like format (digits and dots only)
if strings.Count(s, ".") == 3 {
parts := strings.Split(s, ".")
allNumeric := true
for _, part := range parts {
if part == "" {
allNumeric = false
break
}
for _, c := range part {
if c < '0' || c > '9' {
allNumeric = false
break
}
}
if !allNumeric {
break
}
}
if allNumeric {
return true
}
}
// Check for IPv6-like format (contains colons)
if strings.Contains(s, ":") && !strings.Contains(s, "://") {
return true
}
return false
}