diff --git a/router/ruleset.go b/router/ruleset.go index 216d30e..caa45ca 100644 --- a/router/ruleset.go +++ b/router/ruleset.go @@ -28,7 +28,9 @@ func NewRuleSet(rules ...string) *RuleSet { } // Add appends rules that are not already present, using the raw rule string -// as the membership key. Duplicates are ignored. +// as the membership key. Duplicates are ignored. Empty rules are dropped: +// aconfig parses an empty TOML array as [""], which would otherwise surface +// as a rule that can never match a real domain. func (rs *RuleSet) Add(rules ...string) { if len(rules) == 0 { return @@ -37,6 +39,9 @@ func (rs *RuleSet) Add(rules ...string) { rs.mu.Lock() defer rs.mu.Unlock() for _, rule := range rules { + if rule == "" { + continue + } if _, ok := rs.set[rule]; ok { continue } @@ -59,6 +64,9 @@ func (rs *RuleSet) Replace(rules ...string) { nextSet := make(map[string]struct{}, len(rules)) nextTree := suffixtree.NewNodeFromRules() for _, rule := range rules { + if rule == "" { + continue + } if _, ok := nextSet[rule]; ok { continue } diff --git a/router/ruleset_test.go b/router/ruleset_test.go index 9dc6ab0..229651d 100644 --- a/router/ruleset_test.go +++ b/router/ruleset_test.go @@ -210,3 +210,23 @@ func TestListFilteredPaginationAndSearch(t *testing.T) { t.Fatalf("expected empty page with total 5, got %v (total %d)", rules, total) } } + +func TestRuleSetDropsEmptyRules(t *testing.T) { + rs := NewRuleSet("a.com", "", "b.com") + list := rs.List() + if len(list) != 2 || list[0] != "a.com" || list[1] != "b.com" { + t.Fatalf("Add should drop empty rules: %q", list) + } + + rs.Add("", "c.com") + list = rs.List() + if len(list) != 3 || list[2] != "c.com" { + t.Fatalf("Add should drop empty rules: %q", list) + } + + rs.Replace("", "d.com") + list = rs.List() + if len(list) != 1 || list[0] != "d.com" { + t.Fatalf("Replace should drop empty rules: %q", list) + } +}