Skip to content

Commit f96ee4f

Browse files
authored
fix: wildcard list domains are now validated (#6)
uncommented lines in wildcard lists may compile to a valid regular expression, but may not be valid domains which can cause unintended filter actions. - wildcard parsing function now includes domain name validation via regular expression - domain name validation regular expression sourced from github.com/asaskevich/govalidator; MIT license is compatible
1 parent 4868341 commit f96ee4f

File tree

1 file changed

+39
-14
lines changed

1 file changed

+39
-14
lines changed

action.go

+39-14
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ type ActionConfig struct {
5151
hostsRegexp *regexp.Regexp
5252
}
5353

54+
// DNSNameRegexp matches valid domain names.
55+
// Sourced from https://github.com/asaskevich/govalidator; const DNSName
56+
//
57+
// MIT Licensed, Copyright (c) 2014-2020 Alex Saskevich
58+
var DNSNameRegexp = regexp.MustCompile(
59+
`^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`,
60+
)
61+
5462
// NewActionConfig returns an action ready to accept configurations
5563
func NewActionConfig(action ActionType) ActionConfig {
5664
return ActionConfig{
@@ -143,15 +151,20 @@ func (a ActionConfig) AddWildcardList(url string) error {
143151
return nil
144152
}
145153

154+
func (ActionConfig) cleanListLine(line string) string {
155+
out := strings.TrimPrefix(line, ".") // remove pcre-style wildcard prefix
156+
out = strings.TrimPrefix(out, "*.") // remove generic wildcard
157+
out = strings.TrimPrefix(out, "||") // remove adblock plus prefix
158+
out = strings.TrimSuffix(out, "^") // remove adblock plus suffix
159+
out = strings.TrimPrefix(out, "address=/") // remove dnsmasq declaration
160+
out = strings.Split(out, "/")[0] // remove dnsmasq ip address, if any
161+
return out
162+
}
163+
146164
func (a ActionConfig) makeWildcard(expr string) string {
147-
wc := strings.TrimPrefix(expr, ".")
148-
wc = strings.TrimPrefix(wc, "*.")
149-
wc = strings.TrimPrefix(wc, "||")
150-
wc = strings.TrimSuffix(wc, "^")
151-
wc = strings.TrimPrefix(wc, "address=/")
152-
wc = strings.Split(wc, "/")[0]
153-
wc = strings.ReplaceAll(wc, ".", "\\.")
154-
return fmt.Sprintf("^.*\\.%s|^%s", wc, wc)
165+
out := strings.ReplaceAll(expr, ".", "\\.") // escape periods in domain name
166+
out = fmt.Sprintf("^.*\\.%s|^%s", out, out) // format to match root and sub domains
167+
return out
155168
}
156169

157170
// BuildDomains creates a map of unique domains from explicit declarations and
@@ -314,11 +327,11 @@ func (a ActionConfig) buildRegExpsWildcard(r map[string]*regexp.Regexp) error {
314327
line := scanner.Bytes()
315328
line = bytes.TrimSpace(line)
316329

317-
if a.shouldSkip(line) {
330+
var ok bool
331+
var expString string
332+
if ok, expString = a.getWildcardString(line); !ok {
318333
continue
319334
}
320-
lineStr := string(line)
321-
expString := a.makeWildcard(lineStr)
322335
if _, ok := r[expString]; ok {
323336
continue
324337
}
@@ -328,19 +341,31 @@ func (a ActionConfig) buildRegExpsWildcard(r map[string]*regexp.Regexp) error {
328341
log.Errorf(
329342
"error compiling %s wildcard %q { %s } from list %s; %s",
330343
a.configType,
331-
lineStr,
344+
a.cleanListLine(string(line)),
332345
expString,
333346
dom,
334347
err,
335348
)
336-
} else {
337-
r[expString] = exp
349+
continue
338350
}
351+
r[expString] = exp
339352
}
340353
}
341354
return nil
342355
}
343356

357+
func (a ActionConfig) getWildcardString(line []byte) (bool, string) {
358+
// strip comments and platform-specific formatting
359+
if a.shouldSkip(line) {
360+
return false, ""
361+
}
362+
clean := a.cleanListLine(string(line))
363+
if !DNSNameRegexp.MatchString(clean) {
364+
return false, ""
365+
}
366+
return true, a.makeWildcard(clean)
367+
}
368+
344369
func (a ActionConfig) shouldSkip(line []byte) bool {
345370
if len(line) == 0 {
346371
// skip empty lines

0 commit comments

Comments
 (0)