package dmarcrpt import ( "encoding/xml" "os" "path/filepath" "reflect" "strings" "testing" "github.com/mjl-/mox/mlog" ) var xlog = mlog.New("dmarcrpt") const reportExample = ` google.com noreply-dmarc-support@google.com https://support.google.com/a/answer/2466580 10051505501689795560 1596412800 1596499199 example.org r r

reject

reject 100
127.0.0.1 1 none pass pass example.org example.org pass example example.org pass
` func TestParseReport(t *testing.T) { var expect = &Feedback{ XMLName: xml.Name{Local: "feedback"}, ReportMetadata: ReportMetadata{ OrgName: "google.com", Email: "noreply-dmarc-support@google.com", ExtraContactInfo: "https://support.google.com/a/answer/2466580", ReportID: "10051505501689795560", DateRange: DateRange{ Begin: 1596412800, End: 1596499199, }, }, PolicyPublished: PolicyPublished{ Domain: "example.org", ADKIM: "r", ASPF: "r", Policy: "reject", SubdomainPolicy: "reject", Percentage: 100, }, Records: []ReportRecord{ { Row: Row{ SourceIP: "127.0.0.1", Count: 1, PolicyEvaluated: PolicyEvaluated{ Disposition: DispositionNone, DKIM: DMARCPass, SPF: DMARCPass, }, }, Identifiers: Identifiers{ HeaderFrom: "example.org", }, AuthResults: AuthResults{ DKIM: []DKIMAuthResult{ { Domain: "example.org", Result: DKIMPass, Selector: "example", }, }, SPF: []SPFAuthResult{ { Domain: "example.org", Result: SPFPass, }, }, }, }, }, } feedback, err := ParseReport(strings.NewReader(reportExample)) if err != nil { t.Fatalf("parsing report: %s", err) } if !reflect.DeepEqual(expect, feedback) { t.Fatalf("expected:\n%#v\ngot:\n%#v", expect, feedback) } } func TestParseMessageReport(t *testing.T) { dir := filepath.FromSlash("../testdata/dmarc-reports") files, err := os.ReadDir(dir) if err != nil { t.Fatalf("listing dmarc aggregate report emails: %s", err) } for _, file := range files { p := filepath.Join(dir, file.Name()) f, err := os.Open(p) if err != nil { t.Fatalf("open %q: %s", p, err) } _, err = ParseMessageReport(xlog, f) if err != nil { t.Fatalf("ParseMessageReport: %q: %s", p, err) } f.Close() } // No report in a non-multipart message. _, err = ParseMessageReport(xlog, strings.NewReader("From: \r\n\r\nNo report.\r\n")) if err != ErrNoReport { t.Fatalf("message without report, got err %#v, expected ErrNoreport", err) } // No report in a multipart message. var multipartNoreport = strings.ReplaceAll(`From: To: Subject: Report Domain: mox.example Submitter: mail.mox.example MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="===============5735553800636657282==" --===============5735553800636657282== Content-Type: text/plain MIME-Version: 1.0 test --===============5735553800636657282== Content-Type: text/html MIME-Version: 1.0 --===============5735553800636657282==-- `, "\n", "\r\n") _, err = ParseMessageReport(xlog, strings.NewReader(multipartNoreport)) if err != ErrNoReport { t.Fatalf("message without report, got err %#v, expected ErrNoreport", err) } } func FuzzParseReport(f *testing.F) { f.Add("") f.Add(reportExample) f.Fuzz(func(t *testing.T, s string) { ParseReport(strings.NewReader(s)) }) }