11 "github.com/mjl-/mox/mlog"
14var pkglog = mlog.New("dmarcrpt", nil)
16const reportExample = `<?xml version="1.0" encoding="UTF-8" ?>
19 <org_name>google.com</org_name>
20 <email>noreply-dmarc-support@google.com</email>
21 <extra_contact_info>https://support.google.com/a/answer/2466580</extra_contact_info>
22 <report_id>10051505501689795560</report_id>
24 <begin>1596412800</begin>
29 <domain>example.org</domain>
38 <source_ip>127.0.0.1</source_ip>
41 <disposition>none</disposition>
47 <header_from>example.org</header_from>
51 <domain>example.org</domain>
53 <selector>example</selector>
56 <domain>example.org</domain>
64func TestParseReport(t *testing.T) {
65 var expect = &Feedback{
66 XMLName: xml.Name{Local: "feedback"},
67 ReportMetadata: ReportMetadata{
68 OrgName: "google.com",
69 Email: "noreply-dmarc-support@google.com",
70 ExtraContactInfo: "https://support.google.com/a/answer/2466580",
71 ReportID: "10051505501689795560",
77 PolicyPublished: PolicyPublished{
78 Domain: "example.org",
82 SubdomainPolicy: "reject",
85 Records: []ReportRecord{
88 SourceIP: "127.0.0.1",
90 PolicyEvaluated: PolicyEvaluated{
91 Disposition: DispositionNone,
96 Identifiers: Identifiers{
97 HeaderFrom: "example.org",
99 AuthResults: AuthResults{
100 DKIM: []DKIMAuthResult{
102 Domain: "example.org",
107 SPF: []SPFAuthResult{
109 Domain: "example.org",
118 feedback, err := ParseReport(strings.NewReader(reportExample))
120 t.Fatalf("parsing report: %s", err)
122 if !reflect.DeepEqual(expect, feedback) {
123 t.Fatalf("expected:\n%#v\ngot:\n%#v", expect, feedback)
127func TestParseMessageReport(t *testing.T) {
128 dir := filepath.FromSlash("../testdata/dmarc-reports")
129 files, err := os.ReadDir(dir)
131 t.Fatalf("listing dmarc aggregate report emails: %s", err)
134 for _, file := range files {
135 p := filepath.Join(dir, file.Name())
138 t.Fatalf("open %q: %s", p, err)
140 _, err = ParseMessageReport(pkglog.Logger, f)
142 t.Fatalf("ParseMessageReport: %q: %s", p, err)
147 // No report in a non-multipart message.
148 _, err = ParseMessageReport(pkglog.Logger, strings.NewReader("From: <mjl@mox.example>\r\n\r\nNo report.\r\n"))
149 if err != ErrNoReport {
150 t.Fatalf("message without report, got err %#v, expected ErrNoreport", err)
153 // No report in a multipart message.
154 var multipartNoreport = strings.ReplaceAll(`From: <mjl@mox.example>
156Subject: Report Domain: mox.example Submitter: mail.mox.example
158Content-Type: multipart/alternative; boundary="===============5735553800636657282=="
160--===============5735553800636657282==
161Content-Type: text/plain
166--===============5735553800636657282==
167Content-Type: text/html
172--===============5735553800636657282==--
174 _, err = ParseMessageReport(pkglog.Logger, strings.NewReader(multipartNoreport))
175 if err != ErrNoReport {
176 t.Fatalf("message without report, got err %#v, expected ErrNoreport", err)
180func FuzzParseReport(f *testing.F) {
183 f.Fuzz(func(t *testing.T, s string) {
184 ParseReport(strings.NewReader(s))