Files
mats 66f36cfac6
build / build-push (push) Successful in 1m11s
more stuff
2026-10-16 14:49:50 +02:00

173 lines
6.6 KiB
Go

package acl
import (
"strings"
"testing"
"git.k8s-git-k6n.net/mats/fancy-dns/internal/config"
)
func named(name, policy string, m config.Match) config.Group {
return config.Group{Name: name, Policy: policy, Match: m}
}
func analyzeAt(idx int, defRate int, groups ...config.Group) Analysis {
return Analyze(Context{Groups: groups, Index: idx, DefaultRateLimit: defRate, DefaultPolicy: "allow"})
}
func has(fs []Finding, field, contains string) bool {
for _, f := range fs {
if f.Field == field && strings.Contains(f.Message, contains) {
return true
}
}
return false
}
func TestAnalyzeAllowListIsAllowlistMode(t *testing.T) {
g := named("kids", "allow", config.Match{CIDRs: []string{"10.0.5.0/24"}})
g.AllowDomains = []string{"*.school.example"}
a := analyzeAt(0, 0, g)
if !has(a.Notes, "allow_domains", "allowlist-only") {
t.Errorf("notes = %+v, want the allowlist-only explanation", a.Notes)
}
if !strings.Contains(strings.Join(a.Summary, " "), "ONLY") {
t.Errorf("summary = %v, want it to say only the listed names resolve", a.Summary)
}
}
func TestAnalyzeBlockOnlyIsTheSameAsAllow(t *testing.T) {
a := analyzeAt(0, 0, named("g", "block_only", config.Match{CIDRs: []string{"10.0.0.0/24"}}))
if !has(a.Warnings, "policy", "exactly like allow") {
t.Errorf("warnings = %+v, want the block_only warning", a.Warnings)
}
}
func TestAnalyzeRateLimitZeroExemptsFromTheDefault(t *testing.T) {
g := named("g", "allow", config.Match{CIDRs: []string{"10.0.0.0/24"}})
if a := analyzeAt(0, 50, g); !has(a.Warnings, "rate_limit", "exempt") {
t.Errorf("warnings = %+v, want the exemption warning", a.Warnings)
}
if a := analyzeAt(0, 0, g); len(a.Warnings) != 0 {
t.Errorf("no default limit, yet warnings = %+v", a.Warnings)
}
g.RateLimit = 20
if a := analyzeAt(0, 50, g); has(a.Warnings, "rate_limit", "exempt") {
t.Errorf("an explicit limit must not warn about exemption: %+v", a.Warnings)
}
}
func TestAnalyzeDenyIgnoresTheRest(t *testing.T) {
g := named("g", "deny", config.Match{CIDRs: []string{"10.0.0.0/24"}})
g.RateLimit = 5
g.SkipBlocklist = true
a := analyzeAt(0, 50, g)
if !has(a.Notes, "rate_limit", "Ignored") || !has(a.Notes, "skip_blocklist", "Ignored") {
t.Errorf("notes = %+v, want ignored-setting notes", a.Notes)
}
if has(a.Warnings, "rate_limit", "exempt") {
t.Errorf("a denied group has no rate limit to be exempt from: %+v", a.Warnings)
}
}
func TestAnalyzeShadowing(t *testing.T) {
wide := named("lan", "allow", config.Match{CIDRs: []string{"10.0.0.0/8"}})
narrow := named("cam", "deny", config.Match{CIDRs: []string{"10.1.2.0/24"}})
a := analyzeAt(1, 0, wide, narrow)
if !a.Unreachable || !has(a.Warnings, "match", `"lan"`) {
t.Errorf("narrow range after a wider one: unreachable=%v warnings=%+v", a.Unreachable, a.Warnings)
}
// The other order is the useful one and must stay quiet.
if a := analyzeAt(0, 0, narrow, wide); a.Unreachable || len(a.Warnings) > 1 {
t.Errorf("specific-before-general flagged: unreachable=%v warnings=%+v", a.Unreachable, a.Warnings)
}
// Saving the wide group above the narrow one buries the narrow one.
a = analyzeAt(0, 0, wide, narrow)
if !has(a.Warnings, "match", `makes "cam" unreachable`) {
t.Errorf("warnings = %+v, want the newly-unreachable warning", a.Warnings)
}
}
func TestAnalyzePartialOverlapNamesTheClaimedRange(t *testing.T) {
first := named("a", "allow", config.Match{CIDRs: []string{"10.0.0.0/24"}})
second := named("b", "allow", config.Match{CIDRs: []string{"10.0.0.128/25", "192.168.0.0/24"}})
a := analyzeAt(1, 0, first, second)
if a.Unreachable {
t.Fatal("one range is still free; the group is reachable")
}
if !has(a.Warnings, "match", "10.0.0.128/25 is already claimed by \"a\"") {
t.Errorf("warnings = %+v", a.Warnings)
}
}
func TestAnalyzeReverseNameAndTagShadowing(t *testing.T) {
first := named("a", "allow", config.Match{PTRSuffixes: []string{"lan.example."}, Tags: []string{"laptop"}})
second := named("b", "allow", config.Match{PTRSuffixes: []string{".lab.lan.example."}, Tags: []string{"Laptop"}})
if a := analyzeAt(1, 0, first, second); !a.Unreachable {
t.Errorf("a suffix under an earlier suffix plus the same tag is fully claimed: %+v", a)
}
}
func TestAnalyzeGroupAfterACatchAllIsUnreachable(t *testing.T) {
all := named("all", "allow", config.Match{Any: true, CIDRs: []string{"10.0.0.0/8"}})
g := named("g", "allow", config.Match{CIDRs: []string{"192.168.0.0/24"}})
a := analyzeAt(1, 0, all, g)
if !a.Unreachable {
t.Error("nothing after a match-any group can be reached")
}
if b := analyzeAt(0, 0, all, g); !has(b.Warnings, "match.any", "every client") {
t.Errorf("warnings = %+v, want the match-any warning", b.Warnings)
}
}
func TestAnalyzeCIDRHygiene(t *testing.T) {
a := analyzeAt(0, 0, named("g", "allow", config.Match{CIDRs: []string{"192.168.1.5/24", "0.0.0.0/0"}}))
if !has(a.Warnings, "match.cidrs", "host bits") || !has(a.Warnings, "match.cidrs", "very wide") {
t.Errorf("warnings = %+v", a.Warnings)
}
}
func TestAnalyzeDomainPatternMistakes(t *testing.T) {
g := named("g", "allow", config.Match{CIDRs: []string{"10.0.0.0/24"}})
g.AllowDomains = []string{"https://example.com/a", ".example.org", "ads.*.com", "*.fine.example", "*"}
g.DenyDomains = []string{"*.fine.example"}
a := analyzeAt(0, 0, g)
for _, want := range []string{"looks like a URL", "starts with a dot", "not supported", "allows every name", "deny wins"} {
if !has(a.Warnings, "allow_domains", want) {
t.Errorf("missing allow_domains warning containing %q in %+v", want, a.Warnings)
}
}
}
func TestAnalyzeTagWithADotNeverMatches(t *testing.T) {
a := analyzeAt(0, 0, named("g", "allow", config.Match{Tags: []string{"lan.example"}}))
if !has(a.Warnings, "match.tags", "single label") {
t.Errorf("warnings = %+v", a.Warnings)
}
}
func TestAnalyzeUnknownUpstreams(t *testing.T) {
g := named("g", "allow", config.Match{CIDRs: []string{"10.0.0.0/24"}})
g.Upstreams = []string{"9.9.9.9:53"}
a := Analyze(Context{Groups: []config.Group{g}, Upstreams: []string{"1.1.1.1:53"}})
if !has(a.Warnings, "upstreams", "uses ALL upstreams") {
t.Errorf("warnings = %+v", a.Warnings)
}
g.Upstreams = []string{"1.1.1.1:53"}
if a := Analyze(Context{Groups: []config.Group{g}, Upstreams: []string{"1.1.1.1:53"}}); has(a.Warnings, "upstreams", "") {
t.Errorf("a configured upstream warned: %+v", a.Warnings)
}
if a := Analyze(Context{Groups: []config.Group{g}}); has(a.Warnings, "upstreams", "") {
t.Errorf("unknown upstream list must skip the check: %+v", a.Warnings)
}
}
func TestAnalyzeToleratesABadIndex(t *testing.T) {
if a := Analyze(Context{Index: 3}); len(a.Summary) != 0 {
t.Errorf("summary = %v", a.Summary)
}
}