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  }