Merge pull request #176 from safing/fix/domain-endpoint-validation

Fix domain endpoint validation
This commit is contained in:
Daniel 2020-10-14 11:35:29 +02:00 committed by GitHub
commit 3b5b2282c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 47 deletions

View file

@ -5,6 +5,7 @@ import (
"strings"
"github.com/safing/portmaster/intel"
"github.com/safing/portmaster/network/netutils"
)
const (
@ -16,8 +17,7 @@ const (
)
var (
domainRegex = regexp.MustCompile(`^\*?(([a-z0-9][a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z]{2,}\.?$`)
altDomainRegex = regexp.MustCompile(`^\*?[a-z0-9\.-]+\*$`)
allowedDomainChars = regexp.MustCompile(`^[a-z0-9\.-]+$`)
)
// EndpointDomain matches domains.
@ -90,51 +90,63 @@ func (ep *EndpointDomain) String() string {
func parseTypeDomain(fields []string) (Endpoint, error) {
domain := fields[1]
if domainRegex.MatchString(domain) || altDomainRegex.MatchString(domain) {
ep := &EndpointDomain{
OriginalValue: domain,
}
// fix domain ending
switch domain[len(domain)-1] {
case '.':
case '*':
default:
domain += "."
}
// fix domain case
domain = strings.ToLower(domain)
switch {
case strings.HasPrefix(domain, "*") && strings.HasSuffix(domain, "*"):
ep.MatchType = domainMatchTypeContains
ep.Domain = strings.Trim(domain, "*")
return ep.parsePPP(ep, fields)
case strings.HasSuffix(domain, "*"):
ep.MatchType = domainMatchTypePrefix
ep.Domain = strings.Trim(domain, "*")
return ep.parsePPP(ep, fields)
case strings.HasPrefix(domain, "*"):
ep.MatchType = domainMatchTypeSuffix
ep.Domain = strings.Trim(domain, "*")
return ep.parsePPP(ep, fields)
case strings.HasPrefix(domain, "."):
ep.MatchType = domainMatchTypeZone
ep.Domain = strings.TrimLeft(domain, ".")
ep.DomainZone = "." + ep.Domain
return ep.parsePPP(ep, fields)
default:
ep.MatchType = domainMatchTypeExact
ep.Domain = domain
return ep.parsePPP(ep, fields)
}
ep := &EndpointDomain{
OriginalValue: domain,
}
return nil, nil
// Fix domain ending.
switch domain[len(domain)-1] {
case '.', '*':
default:
domain += "."
}
// Fix domain case.
domain = strings.ToLower(domain)
needValidFQDN := true
switch {
case strings.HasPrefix(domain, "*") && strings.HasSuffix(domain, "*"):
ep.MatchType = domainMatchTypeContains
ep.Domain = strings.TrimPrefix(domain, "*")
ep.Domain = strings.TrimSuffix(ep.Domain, "*")
needValidFQDN = false
case strings.HasSuffix(domain, "*"):
ep.MatchType = domainMatchTypePrefix
ep.Domain = strings.TrimSuffix(domain, "*")
needValidFQDN = false
// Prefix matching cannot be combined with zone matching
if strings.HasPrefix(ep.Domain, ".") {
return nil, nil
}
case strings.HasPrefix(domain, "*"):
ep.MatchType = domainMatchTypeSuffix
ep.Domain = strings.TrimPrefix(domain, "*")
needValidFQDN = false
case strings.HasPrefix(domain, "."):
ep.MatchType = domainMatchTypeZone
ep.Domain = strings.TrimPrefix(domain, ".")
ep.DomainZone = "." + ep.Domain
default:
ep.MatchType = domainMatchTypeExact
ep.Domain = domain
}
// 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 ep.parsePPP(ep, fields)
}

View file

@ -5,6 +5,8 @@ import (
"runtime"
"testing"
"github.com/stretchr/testify/assert"
"github.com/safing/portmaster/core/pmtesting"
"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) {
// ANY