go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/network/resources/dnsshake/spf.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package dnsshake
     5  
     6  import (
     7  	"strings"
     8  
     9  	"github.com/alecthomas/participle/v2"
    10  	"github.com/alecthomas/participle/v2/lexer"
    11  )
    12  
    13  // Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1
    14  // https://datatracker.ietf.org/doc/html/rfc7208
    15  //
    16  // 1. Parse SPF records
    17  // 2. Validate SPF record
    18  // 3. Validate EMAIL + DOMAIN
    19  
    20  // Model after https://datatracker.ietf.org/doc/html/rfc7208#section-12
    21  var spfLexer = lexer.MustSimple([]lexer.SimpleRule{
    22  	{`Header`, `v=`},
    23  	{`Version`, `spf\d`},
    24  	{"whitespace", `\s+`},
    25  	{`Colon`, `[:]`},
    26  	{`Slash`, `[/]`},
    27  	{`Equal`, `[=]`},
    28  	{`Mechanism`, `\b(all|include|a|mx|ptr|ip4|ip6|exists)\b`},
    29  	{`Modifier`, `\b(redirect|exp)\b`},
    30  	{`String`, `[^+\-:\s=\/][\w.%\-+{}]+`},
    31  	{`Qualifier`, `[\+\-~?]`},
    32  	{`Number`, `\d+`},
    33  })
    34  
    35  // nolint: govet
    36  type SpfRecord struct {
    37  	Version    string      `"v=" @Version`
    38  	Directives []Directive `@@*`
    39  	Modifiers  []Modifier  `@@*`
    40  }
    41  
    42  // nolint: govet
    43  type Directive struct {
    44  	Qualifier string `(@Qualifier)?`
    45  	Mechanism string `@Mechanism`
    46  	Value     string `(":" @String)?`
    47  	CIDR      string `("/" @String)?`
    48  }
    49  
    50  // nolint: govet
    51  type Modifier struct {
    52  	Modifier string `@Modifier "="`
    53  	Value    string `@String`
    54  }
    55  
    56  var spfParser = participle.MustBuild[SpfRecord](
    57  	participle.Lexer(spfLexer),
    58  )
    59  
    60  func NewSpf() *spf {
    61  	return &spf{}
    62  }
    63  
    64  type spf struct{}
    65  
    66  func (s *spf) Parse(txt string) (*SpfRecord, error) {
    67  	lines := strings.Split(txt, " ")
    68  
    69  	return spfParser.Parse("", strings.NewReader(strings.Join(lines, "\n")))
    70  }