mirror of
https://github.com/safing/portmaster
synced 2025-09-05 03:59:11 +00:00
Merge pull request #176 from safing/fix/domain-endpoint-validation
Fix domain endpoint validation
This commit is contained in:
commit
3b5b2282c4
2 changed files with 86 additions and 47 deletions
|
@ -5,6 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/safing/portmaster/intel"
|
"github.com/safing/portmaster/intel"
|
||||||
|
"github.com/safing/portmaster/network/netutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -16,8 +17,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
domainRegex = regexp.MustCompile(`^\*?(([a-z0-9][a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z]{2,}\.?$`)
|
allowedDomainChars = regexp.MustCompile(`^[a-z0-9\.-]+$`)
|
||||||
altDomainRegex = regexp.MustCompile(`^\*?[a-z0-9\.-]+\*$`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// EndpointDomain matches domains.
|
// EndpointDomain matches domains.
|
||||||
|
@ -90,51 +90,63 @@ func (ep *EndpointDomain) String() string {
|
||||||
|
|
||||||
func parseTypeDomain(fields []string) (Endpoint, error) {
|
func parseTypeDomain(fields []string) (Endpoint, error) {
|
||||||
domain := fields[1]
|
domain := fields[1]
|
||||||
|
|
||||||
if domainRegex.MatchString(domain) || altDomainRegex.MatchString(domain) {
|
|
||||||
ep := &EndpointDomain{
|
ep := &EndpointDomain{
|
||||||
OriginalValue: domain,
|
OriginalValue: domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix domain ending
|
// Fix domain ending.
|
||||||
switch domain[len(domain)-1] {
|
switch domain[len(domain)-1] {
|
||||||
case '.':
|
case '.', '*':
|
||||||
case '*':
|
|
||||||
default:
|
default:
|
||||||
domain += "."
|
domain += "."
|
||||||
}
|
}
|
||||||
|
|
||||||
// fix domain case
|
// Fix domain case.
|
||||||
domain = strings.ToLower(domain)
|
domain = strings.ToLower(domain)
|
||||||
|
needValidFQDN := true
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(domain, "*") && strings.HasSuffix(domain, "*"):
|
case strings.HasPrefix(domain, "*") && strings.HasSuffix(domain, "*"):
|
||||||
ep.MatchType = domainMatchTypeContains
|
ep.MatchType = domainMatchTypeContains
|
||||||
ep.Domain = strings.Trim(domain, "*")
|
ep.Domain = strings.TrimPrefix(domain, "*")
|
||||||
return ep.parsePPP(ep, fields)
|
ep.Domain = strings.TrimSuffix(ep.Domain, "*")
|
||||||
|
needValidFQDN = false
|
||||||
|
|
||||||
case strings.HasSuffix(domain, "*"):
|
case strings.HasSuffix(domain, "*"):
|
||||||
ep.MatchType = domainMatchTypePrefix
|
ep.MatchType = domainMatchTypePrefix
|
||||||
ep.Domain = strings.Trim(domain, "*")
|
ep.Domain = strings.TrimSuffix(domain, "*")
|
||||||
return ep.parsePPP(ep, fields)
|
needValidFQDN = false
|
||||||
|
|
||||||
|
// Prefix matching cannot be combined with zone matching
|
||||||
|
if strings.HasPrefix(ep.Domain, ".") {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
case strings.HasPrefix(domain, "*"):
|
case strings.HasPrefix(domain, "*"):
|
||||||
ep.MatchType = domainMatchTypeSuffix
|
ep.MatchType = domainMatchTypeSuffix
|
||||||
ep.Domain = strings.Trim(domain, "*")
|
ep.Domain = strings.TrimPrefix(domain, "*")
|
||||||
return ep.parsePPP(ep, fields)
|
needValidFQDN = false
|
||||||
|
|
||||||
case strings.HasPrefix(domain, "."):
|
case strings.HasPrefix(domain, "."):
|
||||||
ep.MatchType = domainMatchTypeZone
|
ep.MatchType = domainMatchTypeZone
|
||||||
ep.Domain = strings.TrimLeft(domain, ".")
|
ep.Domain = strings.TrimPrefix(domain, ".")
|
||||||
ep.DomainZone = "." + ep.Domain
|
ep.DomainZone = "." + ep.Domain
|
||||||
return ep.parsePPP(ep, fields)
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ep.MatchType = domainMatchTypeExact
|
ep.MatchType = domainMatchTypeExact
|
||||||
ep.Domain = domain
|
ep.Domain = domain
|
||||||
return ep.parsePPP(ep, fields)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate domain "content".
|
||||||
|
switch {
|
||||||
|
case needValidFQDN && !netutils.IsValidFqdn(ep.Domain):
|
||||||
|
return nil, nil
|
||||||
|
case !needValidFQDN && !allowedDomainChars.MatchString(ep.Domain):
|
||||||
|
return nil, nil
|
||||||
|
case strings.Contains(ep.Domain, ".."):
|
||||||
|
// The above regex does not catch double dots.
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ep.parsePPP(ep, fields)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/safing/portmaster/core/pmtesting"
|
"github.com/safing/portmaster/core/pmtesting"
|
||||||
"github.com/safing/portmaster/intel"
|
"github.com/safing/portmaster/intel"
|
||||||
)
|
)
|
||||||
|
@ -27,6 +29,31 @@ func testEndpointMatch(t *testing.T, ep Endpoint, entity *intel.Entity, expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testFormat(t *testing.T, endpoint string, shouldSucceed bool) {
|
||||||
|
_, err := parseEndpoint(endpoint)
|
||||||
|
if shouldSucceed {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
} else {
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEndpointFormat(t *testing.T) {
|
||||||
|
testFormat(t, "+ .", false)
|
||||||
|
testFormat(t, "+ .at", true)
|
||||||
|
testFormat(t, "+ .at.", true)
|
||||||
|
testFormat(t, "+ 1.at", true)
|
||||||
|
testFormat(t, "+ 1.at.", true)
|
||||||
|
testFormat(t, "+ 1.f.ix.de.", true)
|
||||||
|
testFormat(t, "+ *contains*", true)
|
||||||
|
testFormat(t, "+ *has.suffix", true)
|
||||||
|
testFormat(t, "+ *.has.suffix", true)
|
||||||
|
testFormat(t, "+ *has.prefix*", true)
|
||||||
|
testFormat(t, "+ *has.prefix.*", true)
|
||||||
|
testFormat(t, "+ .sub.and.prefix.*", false)
|
||||||
|
testFormat(t, "+ *.sub..and.prefix.*", false)
|
||||||
|
}
|
||||||
|
|
||||||
func TestEndpointMatching(t *testing.T) {
|
func TestEndpointMatching(t *testing.T) {
|
||||||
// ANY
|
// ANY
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue