From ab55f908c9a666979b99ba27a5c238e58604c458 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 13 Oct 2020 15:54:37 +0200 Subject: [PATCH] Fix domain endpoint validation --- profile/endpoints/endpoint-domain.go | 111 ++++++++++++++++----------- profile/endpoints/endpoints_test.go | 16 ++++ 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/profile/endpoints/endpoint-domain.go b/profile/endpoints/endpoint-domain.go index 3cc3450f..4ca7f293 100644 --- a/profile/endpoints/endpoint-domain.go +++ b/profile/endpoints/endpoint-domain.go @@ -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\.-]+\*$`) + containsRegex = regexp.MustCompile(`^\*?[a-z0-9\.-]+\*$`) ) // EndpointDomain matches domains. @@ -88,53 +88,72 @@ func (ep *EndpointDomain) String() string { return ep.renderPPP(ep.OriginalValue) } +func isValidEndpointDomain(value string) bool { + // Check for root domain + if value == "." { + return true + } + + // Check for a "contains" value. + if containsRegex.MatchString(value) { + return true + } + + // Remove special leading characters. + value = strings.TrimLeft(value, ".*") + + // Check if value is now a valid domain. + return netutils.IsValidFqdn(value) +} + 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 '.': + case '*': + default: + domain += "." + } + + // Check if domain is valid. + if !isValidEndpointDomain(domain) { + return nil, nil + } + + // 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) + } } diff --git a/profile/endpoints/endpoints_test.go b/profile/endpoints/endpoints_test.go index 85164a4d..2584dfe8 100644 --- a/profile/endpoints/endpoints_test.go +++ b/profile/endpoints/endpoints_test.go @@ -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,20 @@ func testEndpointMatch(t *testing.T, ep Endpoint, entity *intel.Entity, expected } } +func testFormat(t *testing.T, endpoint string) { + _, err := parseEndpoint(endpoint) + assert.NoError(t, err) +} + +func TestEndpointFormat(t *testing.T) { + testFormat(t, "+ .") + testFormat(t, "+ .at") + testFormat(t, "+ .at.") + testFormat(t, "+ 1.at") + testFormat(t, "+ 1.at.") + testFormat(t, "+ 1.f.ix.de.") +} + func TestEndpointMatching(t *testing.T) { // ANY