1 parent
cc7466823b
commit
e8c5b414d9
44 files changed
+7649
-344
No files matched your search
@@ -0,0 +1,143 @@
|
||||
package blocklist
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestExampleRPZZoneBlocksWhatItSays runs the shipped policy zone through the
|
||||
// exact path Manager.RefreshAll uses (parseRPZFiles, which prepends a per-file
|
||||
// $ORIGIN derived from the file name) and pins the rules it produces.
|
||||
//
|
||||
// The origin is the reason this is a test and not a doc: a file called
|
||||
// local.rpz.zone.example is read with the origin "local.", so the relative owner
|
||||
// `*.doubleclick.net` became `*.doubleclick.net.local.` and the example blocked
|
||||
// nothing. The example now spells every owner absolutely; if someone "tidies it
|
||||
// up" back to relative names, the rules below stop matching and this fails.
|
||||
func TestExampleRPZZoneBlocksWhatItSays(t *testing.T) {
|
||||
path := filepath.Join("..", "..", "deploy", "zones", "local.rpz.zone.example")
|
||||
|
||||
rules, stats, err := parseRPZFiles([]string{path})
|
||||
if err != nil {
|
||||
t.Fatalf("parseRPZFiles(%s): %v", path, err)
|
||||
}
|
||||
|
||||
type ruleWant struct {
|
||||
name string
|
||||
action Action
|
||||
response string
|
||||
}
|
||||
want := []ruleWant{
|
||||
{"*.doubleclick.net.", ActionNXDomain, ""},
|
||||
{"*.ads.example.com.", ActionNXDomain, ""},
|
||||
{"*.tracker.example.net.", ActionNXDomain, ""},
|
||||
// A bare owner name in a policy zone is given the subdomain form,
|
||||
// which covers the name's children but not the name itself; see
|
||||
// TestExampleRPZZoneApexIsNotCovered.
|
||||
{"*.analytics.example.org.", ActionNXDomain, ""},
|
||||
{"*.noisy-cdn.example.", ActionDrop, ""},
|
||||
{"*.metrics.internal.", ActionPassthrough, ""},
|
||||
{"*.blocked.example.", ActionRedirect, "192.0.2.10"},
|
||||
}
|
||||
got := make([]ruleWant, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
got = append(got, ruleWant{r.Name, r.Action, r.Response})
|
||||
if r.TTL != 60 {
|
||||
t.Errorf("rule %q has TTL %d, want the zone's $TTL 60", r.Name, r.TTL)
|
||||
}
|
||||
}
|
||||
if !slices.Equal(got, want) {
|
||||
t.Fatalf("rules mismatch\n got: %+v\nwant: %+v", got, want)
|
||||
}
|
||||
if stats.Rules != len(want) {
|
||||
t.Errorf("stats.Rules = %d, want %d", stats.Rules, len(want))
|
||||
}
|
||||
// The A and the AAAA share an owner, so one of them is deduplicated
|
||||
// rather than compiled twice.
|
||||
if stats.Duplicates != 1 {
|
||||
t.Errorf("stats.Duplicates = %d, want 1 (the second blocked.example. record)", stats.Duplicates)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExampleRPZZoneMatchesTheNamesItNames builds the Set an instance would
|
||||
// actually consult, so the pin above is about lookups and not only about the
|
||||
// parser's output. A rule that exists but does not match is still a blocklist
|
||||
// that does nothing.
|
||||
func TestExampleRPZZoneMatchesTheNamesItNames(t *testing.T) {
|
||||
path := filepath.Join("..", "..", "deploy", "zones", "local.rpz.zone.example")
|
||||
rules, _, err := parseRPZFiles([]string{path})
|
||||
if err != nil {
|
||||
t.Fatalf("parseRPZFiles(%s): %v", path, err)
|
||||
}
|
||||
b := NewBuilder()
|
||||
for _, r := range rules {
|
||||
b.Add(r, "local-rpz", nil)
|
||||
}
|
||||
set := b.Build()
|
||||
|
||||
blocked := []string{
|
||||
"www.doubleclick.net.",
|
||||
"a.b.doubleclick.net.",
|
||||
// These three carry an explicit "*." owner in the example, so only
|
||||
// names below them are covered; the apex gap is pinned separately.
|
||||
"cdn.ads.example.com.",
|
||||
"pixel.tracker.example.net.",
|
||||
"cdn.noisy-cdn.example.",
|
||||
}
|
||||
for _, name := range blocked {
|
||||
e := set.Match(name)
|
||||
if e == nil {
|
||||
t.Errorf("%s is not matched by the example policy zone", name)
|
||||
continue
|
||||
}
|
||||
if e.IsAllow() {
|
||||
t.Errorf("%s matched an allow rule, want a block", name)
|
||||
}
|
||||
}
|
||||
if e := set.Match("x.metrics.internal."); e == nil || !e.IsAllow() {
|
||||
t.Errorf("x.metrics.internal. = %v, want the rpz-passthru allow rule", e)
|
||||
}
|
||||
if e := set.Match("unrelated.example."); e != nil {
|
||||
t.Errorf("unrelated.example. matched %v, want no rule", e)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExampleRPZZoneApexIsNotCovered pins a real gap rather than an intention.
|
||||
//
|
||||
// An RPZ record names the domain it applies to, and in RPZ semantics that means
|
||||
// the domain and everything below it. rpzRule gives a bare owner name the
|
||||
// subdomain form ("*.name"), and a subdomain-form rule does not match the name
|
||||
// itself — so `analytics.example.org.` in a policy zone blocks
|
||||
// sub.analytics.example.org. and leaves analytics.example.org. resolving
|
||||
// normally. The apex form ("**.name") is what covers both.
|
||||
//
|
||||
// Not changed here: it alters which queries get blocked for anyone already
|
||||
// running a policy zone, and the previous round's decision was to pin the
|
||||
// open questions rather than change behaviour. If this is fixed, the fix is one
|
||||
// character in rpzRule ("*." -> "**.") plus this test and the STATUS.md entry.
|
||||
func TestExampleRPZZoneApexIsNotCovered(t *testing.T) {
|
||||
path := filepath.Join("..", "..", "deploy", "zones", "local.rpz.zone.example")
|
||||
rules, _, err := parseRPZFiles([]string{path})
|
||||
if err != nil {
|
||||
t.Fatalf("parseRPZFiles(%s): %v", path, err)
|
||||
}
|
||||
b := NewBuilder()
|
||||
for _, r := range rules {
|
||||
b.Add(r, "local-rpz", nil)
|
||||
}
|
||||
set := b.Build()
|
||||
|
||||
for _, name := range []string{"doubleclick.net.", "ads.example.com.", "analytics.example.org."} {
|
||||
if e := set.Match(name); e != nil {
|
||||
t.Errorf("%s matched %v; a policy record for a domain is expected to cover "+
|
||||
"the domain itself as well as its subdomains (see STATUS.md)", name, e)
|
||||
}
|
||||
}
|
||||
// The same gap on the other side: an rpz-passthru. for the apex does not
|
||||
// allowlist the apex, so a name on the blocklist stays blocked.
|
||||
if e := set.Match("metrics.internal."); e != nil && e.IsAllow() {
|
||||
t.Errorf("metrics.internal. matched the allow rule %v; the apex of an "+
|
||||
"rpz-passthru. record is not covered", e)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user