go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dnsshake/spf_test.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package dnsshake 5 6 import ( 7 "fmt" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 ) 13 14 func TestSpfAst(t *testing.T) { 15 spf := NewSpf() 16 ast, err := spf.Parse("v=spf1") 17 require.NoError(t, err) 18 fmt.Printf("%v\n", ast) 19 20 ast, err = spf.Parse("v=spf1 mx/30 mx:example.org/30 -all") 21 require.NoError(t, err) 22 fmt.Printf("%v\n", ast) 23 24 ast, err = spf.Parse("v=spf1 mx -all exp=explain._spf.%{d}") 25 require.NoError(t, err) 26 fmt.Printf("%v\n", ast) 27 } 28 29 // TestSpfAstParser tests the examples from https://datatracker.ietf.org/doc/html/rfc7208#appendix-A.1 30 func TestRfcRecords(t *testing.T) { 31 type test struct { 32 Title string 33 DnsTxtRecord string 34 Expected *SpfRecord 35 } 36 37 testCases := []test{ 38 // Simple Examples 39 { 40 Title: "any <ip> passes", 41 DnsTxtRecord: "v=spf1 +all", 42 Expected: &SpfRecord{ 43 Version: "spf1", 44 Directives: []Directive{{ 45 Qualifier: "+", 46 Mechanism: "all", 47 }}, 48 }, 49 }, 50 { 51 Title: "hosts 192.0.2.10 and 192.0.2.11 pass", 52 DnsTxtRecord: "v=spf1 a -all", 53 Expected: &SpfRecord{ 54 Version: "spf1", 55 Directives: []Directive{ 56 { 57 Mechanism: "a", 58 }, 59 { 60 Qualifier: "-", 61 Mechanism: "all", 62 }, 63 }, 64 }, 65 }, 66 { 67 Title: "no sending hosts pass since example.org has no A records", 68 DnsTxtRecord: "v=spf1 a:example.org -all", 69 Expected: &SpfRecord{ 70 Version: "spf1", 71 Directives: []Directive{ 72 { 73 Mechanism: "a", 74 Value: "example.org", 75 }, 76 { 77 Qualifier: "-", 78 Mechanism: "all", 79 }, 80 }, 81 }, 82 }, 83 { 84 Title: "sending hosts 192.0.2.129 and 192.0.2.130 pass", 85 DnsTxtRecord: "v=spf1 mx -all", 86 Expected: &SpfRecord{ 87 Version: "spf1", 88 Directives: []Directive{ 89 { 90 Mechanism: "mx", 91 }, 92 { 93 Qualifier: "-", 94 Mechanism: "all", 95 }, 96 }, 97 }, 98 }, 99 { 100 Title: "sending host 192.0.2.140 passes", 101 DnsTxtRecord: "v=spf1 mx:example.org -all", 102 Expected: &SpfRecord{ 103 Version: "spf1", 104 Directives: []Directive{ 105 { 106 Mechanism: "mx", 107 Value: "example.org", 108 }, 109 { 110 Qualifier: "-", 111 Mechanism: "all", 112 }, 113 }, 114 }, 115 }, 116 { 117 Title: "any sending host in 192.0.2.128/30 or 192.0.2.140/30 passes", 118 DnsTxtRecord: "v=spf1 mx/30 mx:example.org/30 -all", 119 Expected: &SpfRecord{ 120 Version: "spf1", 121 Directives: []Directive{ 122 { 123 Mechanism: "mx", 124 CIDR: "30", 125 }, 126 { 127 Mechanism: "mx", 128 Value: "example.org", 129 CIDR: "30", 130 }, 131 { 132 Qualifier: "-", 133 Mechanism: "all", 134 }, 135 }, 136 }, 137 }, 138 { 139 Title: "sending host 192.0.2.65 passes (reverse DNS is valid and is in example.com)", 140 DnsTxtRecord: "v=spf1 ptr -all", 141 Expected: &SpfRecord{ 142 Version: "spf1", 143 Directives: []Directive{ 144 { 145 Mechanism: "ptr", 146 }, 147 { 148 Qualifier: "-", 149 Mechanism: "all", 150 }, 151 }, 152 }, 153 }, 154 { 155 Title: "ending host 192.0.2.65 fails", 156 DnsTxtRecord: "v=spf1 ip4:192.0.2.128/28 -all", 157 Expected: &SpfRecord{ 158 Version: "spf1", 159 Directives: []Directive{ 160 { 161 Mechanism: "ip4", 162 Value: "192.0.2.128", 163 CIDR: "28", 164 }, 165 { 166 Qualifier: "-", 167 Mechanism: "all", 168 }, 169 }, 170 }, 171 }, 172 // Multiple Domain Example 173 { 174 Title: "Multiple Domain Example", 175 DnsTxtRecord: "v=spf1 include:example.com include:example.net -all", 176 Expected: &SpfRecord{ 177 Version: "spf1", 178 Directives: []Directive{ 179 { 180 Mechanism: "include", 181 Value: "example.com", 182 }, 183 { 184 Mechanism: "include", 185 Value: "example.net", 186 }, 187 { 188 Qualifier: "-", 189 Mechanism: "all", 190 }, 191 }, 192 }, 193 }, 194 { 195 Title: "Redirect", 196 DnsTxtRecord: "v=spf1 +mx redirect=_spf.example.com", 197 Expected: &SpfRecord{ 198 Version: "spf1", 199 Directives: []Directive{ 200 { 201 Qualifier: "+", 202 Mechanism: "mx", 203 }, 204 }, 205 Modifiers: []Modifier{ 206 { 207 Modifier: "redirect", 208 Value: "_spf.example.com", 209 }, 210 }, 211 }, 212 }, 213 { 214 Title: "Exists", 215 DnsTxtRecord: "v=spf1 exists:%{ir}.%{l1r+-}._spf.%{d} -all", 216 Expected: &SpfRecord{ 217 Version: "spf1", 218 Directives: []Directive{ 219 { 220 Mechanism: "exists", 221 Value: "%{ir}.%{l1r+-}._spf.%{d}", 222 }, 223 { 224 Qualifier: "-", 225 Mechanism: "all", 226 }, 227 }, 228 }, 229 }, 230 { 231 Title: "Explain", 232 DnsTxtRecord: "v=spf1 mx -all exp=explain._spf.%{d}", 233 Expected: &SpfRecord{ 234 Version: "spf1", 235 Directives: []Directive{ 236 { 237 Mechanism: "mx", 238 }, 239 { 240 Qualifier: "-", 241 Mechanism: "all", 242 }, 243 }, 244 Modifiers: []Modifier{ 245 { 246 Modifier: "exp", 247 Value: "explain._spf.%{d}", 248 }, 249 }, 250 }, 251 }, 252 } 253 254 spf := NewSpf() 255 for i := range testCases { 256 tc := testCases[i] 257 t.Run(tc.Title, func(t *testing.T) { 258 ast, err := spf.Parse(tc.DnsTxtRecord) 259 assert.NoError(t, err) 260 261 // check that the data was parsed as expected 262 assert.EqualValues(t, tc.Expected, ast) 263 }) 264 } 265 }