package kubesource import ( "strings" "testing" "git.k8s-git-k6n.net/mats/fancy-dns/internal/config" ) func TestOverridesAndRulesReachTheSnapshot(t *testing.T) { first := obj("AddressOverride", "tornberg", map[string]any{"domain": "Tornberg.ME.", "addresses": []any{"10.10.3.10", "fd00::1"}}) dup := obj("AddressOverride", "a-dup", map[string]any{"domain": "tornberg.me", "addresses": []any{"10.0.0.9"}}) // The oldest claim wins, whatever the names sort like. first.SetCreationTimestamp(metav1Time(1000)) dup.SetCreationTimestamp(metav1Time(2000)) _, _, ch := start(t, first, dup, obj("AddressOverride", "bad", map[string]any{"domain": "x.test", "addresses": []any{"not-an-ip"}}), obj("DomainRule", "blocks", map[string]any{"action": "block", "names": []any{"Ads.Example.com", "*.tracker.example"}}), obj("DomainRule", "allows", map[string]any{"action": "allow", "names": []any{"**.good.example"}}), obj("DomainRule", "bogus", map[string]any{"action": "drop", "names": []any{"x.test"}}), ) sn := next(t, ch) if len(sn.Overrides) != 1 || sn.Overrides[0].Domain != "tornberg.me" || len(sn.Overrides[0].Addresses) != 2 { t.Fatalf("overrides = %+v", sn.Overrides) } if got := strings.Join(sn.BlockNames, ","); got != "ads.example.com,*.tracker.example" { t.Fatalf("block names = %s", got) } if got := strings.Join(sn.AllowNames, ","); got != "**.good.example" { t.Fatalf("allow names = %s", got) } if len(sn.Problems) != 3 { t.Fatalf("problems = %+v, want the duplicate, the bad address and the bad action", sn.Problems) } rpz := RenderRPZ(sn.Overrides) for _, want := range []string{"$TTL 60", "tornberg.me A 10.10.3.10", "tornberg.me AAAA fd00::1"} { if !strings.Contains(rpz, want) { t.Errorf("RPZ is missing %q:\n%s", want, rpz) } } } // TestRuleNamesCannotSmuggleRules: names are joined into line-oriented parser // input, so anything that is not a DNS name must be refused before it gets there. func TestRuleNamesCannotSmuggleRules(t *testing.T) { for _, bad := range []string{ "a.test\nb.test", "a.test # comment", "a.test A 10.0.0.1", "a b", "", ".", "a..test", "-.test"[:0] + "$ORIGIN x.", "*.", "*", } { if _, err := CheckRules(DomainRuleSpec{Action: "block", Names: []string{bad}}); err == nil { t.Errorf("name %q accepted", bad) } if _, err := CheckOverride(AddressOverrideSpec{Domain: bad, Addresses: []string{"10.0.0.1"}}); err == nil { t.Errorf("override domain %q accepted", bad) } } if _, err := CheckRules(DomainRuleSpec{Action: "block", Names: []string{"*.ok.example", "**.ok2.example", "plain.example."}}); err != nil { t.Errorf("valid names rejected: %v", err) } // Surrounding whitespace is trimmed, never passed on. if r, err := CheckRules(DomainRuleSpec{Action: "block", Names: []string{" evil.test\r\n"}}); err != nil || r.Names[0] != "evil.test" { t.Errorf("trailing control characters survived: %q %v", r.Names, err) } if _, err := CheckOverride(AddressOverrideSpec{Domain: "x.test", Addresses: []string{"::ffff:10.0.0.1"}}); err != nil { t.Errorf("mapped IPv4 rejected: %v", err) } } func TestMergeAddsInlineRulesWithoutTouchingBase(t *testing.T) { base := config.Default() base.Block.BlockEntries = []string{"yaml.example"} merged, _ := Merge(base, &Snapshot{BlockNames: []string{"crd.example"}, AllowNames: []string{"ok.example"}}) if got := strings.Join(merged.Block.BlockEntries, ","); got != "yaml.example,crd.example" { t.Fatalf("block entries = %s", got) } if len(merged.Block.AllowEntries) != 1 || len(base.Block.BlockEntries) != 1 || len(base.Block.AllowEntries) != 0 { t.Fatal("Merge modified its input or lost an entry") } }