mirror of
				https://github.com/coredns/coredns.git
				synced 2025-10-31 02:03:20 -04:00 
			
		
		
		
	Previously OriginsFromArgsOrServerBlock accessed the output of NormalizeExact() by index 0, which could panic when normalization returned an empty slice on error. This happens with malformed input surfaced by fuzzing, for example "unix://<non‑UTF8>". This change hardens normalization in the server block path. If normalization yields no entries, the original value is preserved. The function still returns a newly copied slice. This preserves legacy semantics for valid inputs while eliminating the crash on malformed ones. Added tests to validate. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
		
			
				
	
	
		
			203 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			203 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package plugin
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 	"sort"
 | |
| 	"testing"
 | |
| )
 | |
| 
 | |
| func TestZoneMatches(t *testing.T) {
 | |
| 	child := "example.org."
 | |
| 	zones := Zones([]string{"org.", "."})
 | |
| 	actual := zones.Matches(child)
 | |
| 	if actual != "org." {
 | |
| 		t.Errorf("Expected %v, got %v", "org.", actual)
 | |
| 	}
 | |
| 
 | |
| 	child = "bla.example.org."
 | |
| 	zones = Zones([]string{"bla.example.org.", "org.", "."})
 | |
| 	actual = zones.Matches(child)
 | |
| 
 | |
| 	if actual != "bla.example.org." {
 | |
| 		t.Errorf("Expected %v, got %v", "org.", actual)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestZoneNormalize(t *testing.T) {
 | |
| 	zones := Zones([]string{"example.org", "Example.ORG.", "example.org."})
 | |
| 	expected := "example.org."
 | |
| 	zones.Normalize()
 | |
| 
 | |
| 	for _, actual := range zones {
 | |
| 		if actual != expected {
 | |
| 			t.Errorf("Expected %v, got %v", expected, actual)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNameMatches(t *testing.T) {
 | |
| 	matches := []struct {
 | |
| 		child    string
 | |
| 		parent   string
 | |
| 		expected bool
 | |
| 	}{
 | |
| 		{".", ".", true},
 | |
| 		{"example.org.", ".", true},
 | |
| 		{"example.org.", "example.org.", true},
 | |
| 		{"example.org.", "org.", true},
 | |
| 		{"org.", "example.org.", false},
 | |
| 	}
 | |
| 
 | |
| 	for _, m := range matches {
 | |
| 		actual := Name(m.parent).Matches(m.child)
 | |
| 		if actual != m.expected {
 | |
| 			t.Errorf("Expected %v for %s/%s, got %v", m.expected, m.parent, m.child, actual)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestNameNormalize(t *testing.T) {
 | |
| 	names := []string{
 | |
| 		"example.org", "example.org.",
 | |
| 		"Example.ORG.", "example.org."}
 | |
| 
 | |
| 	for i := 0; i < len(names); i += 2 {
 | |
| 		ts := names[i]
 | |
| 		expected := names[i+1]
 | |
| 		actual := Name(ts).Normalize()
 | |
| 		if expected != actual {
 | |
| 			t.Errorf("Expected %v, got %v", expected, actual)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestHostNormalizeExact(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		in  string
 | |
| 		out []string
 | |
| 	}{
 | |
| 		{".:53", []string{"."}},
 | |
| 		{"example.org:53", []string{"example.org."}},
 | |
| 		{"example.org.:53", []string{"example.org."}},
 | |
| 		{"10.0.0.0/8:53", []string{"10.in-addr.arpa."}},
 | |
| 		{"10.0.0.0/15", []string{"0.10.in-addr.arpa.", "1.10.in-addr.arpa."}},
 | |
| 		{"10.9.3.0/18", []string{"0.9.10.in-addr.arpa.", "1.9.10.in-addr.arpa.", "2.9.10.in-addr.arpa."}},
 | |
| 		{"2001:db8::/29", []string{
 | |
| 			"8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"9.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"a.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"b.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"c.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"d.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"e.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"f.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/30", []string{
 | |
| 			"8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"9.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"a.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"b.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/115", []string{
 | |
| 			"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/114", []string{
 | |
| 			"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/113", []string{
 | |
| 			"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"3.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"6.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 			"7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/112", []string{
 | |
| 			"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"2001:db8::/108", []string{
 | |
| 			"0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.",
 | |
| 		}},
 | |
| 		{"::fFFF:B:F/115", nil},
 | |
| 		{"dns://example.org", []string{"example.org."}},
 | |
| 	}
 | |
| 
 | |
| 	for i := range tests {
 | |
| 		actual := Host(tests[i].in).NormalizeExact()
 | |
| 		expected := tests[i].out
 | |
| 		sort.Strings(expected)
 | |
| 		for j := range expected {
 | |
| 			if expected[j] != actual[j] {
 | |
| 				t.Errorf("Test %d, expected %v, got %v", i, expected[j], actual[j])
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestOriginsFromArgsOrServerBlock(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		args        []string
 | |
| 		serverblock []string
 | |
| 		expected    []string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:        "args",
 | |
| 			args:        []string{"example.org"},
 | |
| 			serverblock: []string{"ignored.local"},
 | |
| 			expected:    []string{"example.org."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "args with cidr expands",
 | |
| 			args:        []string{"10.0.0.0/15"},
 | |
| 			serverblock: nil,
 | |
| 			expected:    []string{"0.10.in-addr.arpa.", "1.10.in-addr.arpa."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "serverblock first normalized",
 | |
| 			args:        nil,
 | |
| 			serverblock: []string{"example.org"},
 | |
| 			expected:    []string{"example.org."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "serverblock cidr first only",
 | |
| 			args:        nil,
 | |
| 			serverblock: []string{"10.0.0.0/15"},
 | |
| 			expected:    []string{"0.10.in-addr.arpa."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "serverblock invalid utf-8 preserved",
 | |
| 			args:        nil,
 | |
| 			serverblock: []string{"\xFF\n:", "example.org"},
 | |
| 			expected:    []string{"\xFF\n:", "example.org."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "args invalid utf-8 dropped",
 | |
| 			args:        []string{"\xFF\n:", "example.org"},
 | |
| 			serverblock: nil,
 | |
| 			expected:    []string{"example.org."},
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "serverblock invalid utf-8 with prefix",
 | |
| 			args:        nil,
 | |
| 			serverblock: []string{"unix://\xff\netcd", "example.org"},
 | |
| 			expected:    []string{"\uFFFD\netcd.", "example.org."}, // \uFFFD is the replacement character
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			got := OriginsFromArgsOrServerBlock(tt.args, tt.serverblock)
 | |
| 			if !reflect.DeepEqual(got, tt.expected) {
 | |
| 				t.Fatalf("%s: expected %q, got %q", tt.name, tt.expected, got)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |