mirror of
https://github.com/warmbly/warmbly.git
synced 2026-09-05 16:02:48 +00:00
57 lines
1.6 KiB
Go
57 lines
1.6 KiB
Go
package validate
|
|
|
|
import (
|
|
"net"
|
|
"strings"
|
|
)
|
|
|
|
// TrackingHostname reports whether host is usable as a tracking domain: a bare
|
|
// hostname with no scheme, no port, no path, no raw IP literal and no
|
|
// internal/metadata name, mirroring the webhook-SSRF posture. Mailbox and
|
|
// campaign tracking domains share one definition so a host that is valid on one
|
|
// cannot be invalid on the other.
|
|
func TrackingHostname(host string) bool {
|
|
if host == "" || len(host) > 253 {
|
|
return false
|
|
}
|
|
if strings.Contains(host, "://") || strings.ContainsAny(host, " \t\r\n/\\?#@:") {
|
|
return false
|
|
}
|
|
if net.ParseIP(host) != nil {
|
|
return false
|
|
}
|
|
lower := strings.ToLower(host)
|
|
if lower == "localhost" || strings.HasSuffix(lower, ".localhost") || lower == "metadata.google.internal" {
|
|
return false
|
|
}
|
|
// A single label has no public zone to own, and a trailing dot is a
|
|
// fully-qualified form nobody types into this field.
|
|
if !strings.Contains(lower, ".") || strings.HasPrefix(lower, ".") || strings.HasSuffix(lower, ".") {
|
|
return false
|
|
}
|
|
for _, label := range strings.Split(lower, ".") {
|
|
if label == "" || len(label) > 63 {
|
|
return false
|
|
}
|
|
if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") {
|
|
return false
|
|
}
|
|
for _, c := range label {
|
|
if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-') {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
// The last label is the TLD: letters only, at least two of them.
|
|
tld := lower[strings.LastIndex(lower, ".")+1:]
|
|
if len(tld) < 2 {
|
|
return false
|
|
}
|
|
for _, c := range tld {
|
|
if c < 'a' || c > 'z' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|