mirror of
https://github.com/projectdiscovery/httpx
synced 2026-06-08 16:50:17 +00:00
9330887a58
* Extract body-domains and body-fqdns * remvove port in domains * Add test for domains extraction * misc update * improve domain regex * fix test * extract domain inside quotes * sanitize urls * fix test * minor * do not embed * remove js variables fp + improve regex --------- Co-authored-by: Tarun Koyalwar <tarun@projectdiscovery.io>
114 lines
3.4 KiB
Go
114 lines
3.4 KiB
Go
package httpx
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
|
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
|
"github.com/weppos/publicsuffix-go/publicsuffix"
|
|
)
|
|
|
|
const (
|
|
// group 1 is actual domain regex while group 0 and group 2 are used to filter out invalid matches (by skipping irrelevant contexts)
|
|
potentialDomainRegex = `(?:^|['"/@])` + `([a-z0-9]+[a-z0-9.-]*\.[a-z]{2,})` + `(?:['"/@]|$)`
|
|
)
|
|
|
|
var (
|
|
// potentialDomainsCompiled is a compiled regex for potential domains (aka domain names)
|
|
potentialDomainsCompiled = regexp.MustCompile(potentialDomainRegex)
|
|
defaultDenylist = []string{".3g2", ".3gp", ".7z", ".apk", ".arj", ".avi", ".axd", ".bmp", ".csv", ".deb", ".dll", ".doc", ".drv", ".eot", ".exe", ".flv", ".gif", ".gifv", ".gz", ".h264", ".ico", ".iso", ".jar", ".jpeg", ".jpg", ".lock", ".m4a", ".m4v", ".map", ".mkv", ".mov", ".mp3", ".mp4", ".mpeg", ".mpg", ".msi", ".ogg", ".ogm", ".ogv", ".otf", ".pdf", ".pkg", ".png", ".ppt", ".psd", ".rar", ".rm", ".rpm", ".svg", ".swf", ".sys", ".tar.gz", ".tar", ".tif", ".tiff", ".ttf", ".txt", ".vob", ".wav", ".webm", ".webp", ".wmv", ".woff", ".woff2", ".xcf", ".xls", ".xlsx", ".zip", ".css", ".js", ".map", ".php", ".sheet", ".ms", ".wp", ".html", ".htm", ".md"}
|
|
suffixBlacklist = map[string]struct{}{}
|
|
)
|
|
|
|
type BodyDomain struct {
|
|
Fqdns []string `json:"body_fqdn,omitempty"`
|
|
Domains []string `json:"body_domains,omitempty"`
|
|
}
|
|
|
|
func (h *HTTPX) BodyDomainGrab(r *Response) *BodyDomain {
|
|
domains := make(map[string]struct{})
|
|
fqdns := make(map[string]struct{})
|
|
|
|
for _, tmp := range potentialDomainsCompiled.FindAllStringSubmatch(r.Raw, -1) {
|
|
// only interested in 1st group
|
|
if len(tmp) < 2 {
|
|
continue
|
|
}
|
|
d := tmp[1]
|
|
// minimal + known blacklist
|
|
if !isValidDomain(d) {
|
|
continue
|
|
}
|
|
// try to parse its tld
|
|
if !isValidTLD(d) {
|
|
continue
|
|
}
|
|
// get domain
|
|
val, err := publicsuffix.Domain(d)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if r.Input != val {
|
|
domains[val] = struct{}{}
|
|
}
|
|
if d != val && d != r.Input {
|
|
fqdns[d] = struct{}{}
|
|
}
|
|
}
|
|
|
|
return &BodyDomain{Domains: mapsutil.GetKeys(domains), Fqdns: mapsutil.GetKeys(fqdns)}
|
|
}
|
|
|
|
func isValidDomain(d string) bool {
|
|
parts := strings.Split(d, ".")
|
|
if len(parts) < 2 {
|
|
return false
|
|
}
|
|
// this is try when all parts are numeric
|
|
// in which this is not a valid domain (could be a ip or something else)
|
|
allnumeric := true
|
|
// traverse in reverse
|
|
for i := len(parts) - 1; i >= 0; i-- {
|
|
if _, ok := suffixBlacklist["."+parts[i]]; ok {
|
|
return false
|
|
}
|
|
// check for numeric
|
|
local:
|
|
for _, c := range parts[i] {
|
|
if !unicode.IsDigit(c) {
|
|
allnumeric = false
|
|
break local
|
|
}
|
|
}
|
|
}
|
|
|
|
if allnumeric {
|
|
// not a domain could be ip or something else
|
|
return false
|
|
}
|
|
|
|
// simple hack for android/ios package name
|
|
if stringsutil.HasPrefixAny(d, "com", "net", "io", "org") && !stringsutil.HasSuffixAny(d, "com", "net", "io", "org") {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isValidTLD(domain string) bool {
|
|
rule := publicsuffix.DefaultList.Find(domain, publicsuffix.DefaultFindOptions)
|
|
if rule == nil || rule.Type != publicsuffix.NormalType {
|
|
return false
|
|
}
|
|
|
|
_, err := publicsuffix.ParseFromListWithOptions(publicsuffix.DefaultList, domain, &publicsuffix.FindOptions{DefaultRule: rule})
|
|
return err == nil
|
|
}
|
|
|
|
func init() {
|
|
for _, s := range defaultDenylist {
|
|
suffixBlacklist[s] = struct{}{}
|
|
}
|
|
}
|