diff --git a/cmd/dmarc-report-converter/consts.go b/cmd/dmarc-report-converter/consts.go index 456e025..bf333f4 100644 --- a/cmd/dmarc-report-converter/consts.go +++ b/cmd/dmarc-report-converter/consts.go @@ -1,39 +1,6 @@ package main -const htmlTmpl = ` - - - - - - - - - - - - - - - - - - +const htmlMain = `

@@ -121,25 +88,40 @@ table.table.bottomless {{.Row.PolicyEvaluated.SPF}} {{- end }} - {{.AuthResults.DKIM.Domain}} - - {{- if eq .AuthResults.DKIM.Result "pass"}} - {{.AuthResults.DKIM.Result}} - {{- else if eq .AuthResults.DKIM.Result "fail"}} - {{.AuthResults.DKIM.Result}} - {{- else}} - {{.AuthResults.DKIM.Result}} - {{- end}} + + {{- $len := len .AuthResults.DKIM }} + {{- range $j, $foo := .AuthResults.DKIM }} + {{- .Domain }} + {{- if lt $j $len }}
{{ end }} + {{- end -}} - {{.AuthResults.SPF.Domain}} - - {{- if eq .AuthResults.SPF.Result "pass"}} - {{.AuthResults.SPF.Result}} - {{- else if eq .AuthResults.SPF.Result "fail"}} - {{.AuthResults.SPF.Result}} - {{- else}} - {{.AuthResults.SPF.Result}} - {{- end}} + + {{- range $j, $d := .AuthResults.DKIM }} + {{.Result}} + {{- if lt $j $len }}
{{ end }} + {{- end -}} + + + {{- $len = len .AuthResults.SPF }} + {{- range $j, $foo := .AuthResults.SPF }} + {{- .Domain }} + {{- if lt $j $len }}
{{ end }} + {{- end -}} + + {{- range $j, $d := .AuthResults.SPF }} + {{.Result}} + {{- if lt $j $len }}
{{ end }} + {{- end -}} {{- end }} @@ -148,6 +130,42 @@ table.table.bottomless
+` + +const htmlTmpl = ` + + + + + + + + + + + + + + + + + +` + htmlMain + ` ` @@ -179,141 +197,42 @@ table.table.bottomless - -

-
-
-
-
-
- DMARC Report, id {{.Report.ReportMetadata.ReportID}} -
-
- - - - - - - - - - - - - - - -
Organization{{.Report.ReportMetadata.OrgName}} ({{.Report.ReportMetadata.Email}})
Date rangesince {{.Report.ReportMetadata.DateRange.Begin.UTC}} until {{.Report.ReportMetadata.DateRange.End.UTC}}
Policy published{{.Report.PolicyPublished.Domain}}: p={{.Report.PolicyPublished.Policy}} sp={{.Report.PolicyPublished.SPolicy}} pct={{.Report.PolicyPublished.Pct}} adkim={{.Report.PolicyPublished.ADKIM}} aspf={{.Report.PolicyPublished.ASPF}}
-
-
-
-
-

-
-
-
-
passed {{ .Report.MessagesStats.PassedPercent }}%
-
-
-
- passed {{ .Report.MessagesStats.Passed }} failed {{ .Report.MessagesStats.Failed }} total {{ .Report.MessagesStats.All }} -
-
-

-
-
- - - - - - - - - - - - - - - - - - - - - - {{- range .Report.Records }} - {{- if .IsPassed }} - - {{- else }} - - {{- end }} - - - - - - - - - - - - {{- end }} - -
policy evaluatedauth results
iphostnamemsgsdispositionDKIMSPFDKIM domainresultSPF domainresult
{{.Row.SourceIP}}{{.Row.SourceHostname}}{{.Row.Count}}{{.Row.PolicyEvaluated.Disposition}} - {{- if eq .Row.PolicyEvaluated.DKIM "fail" }} - {{.Row.PolicyEvaluated.DKIM}} - {{- else }} - {{.Row.PolicyEvaluated.DKIM}} - {{- end}} - - {{- if eq .Row.PolicyEvaluated.SPF "fail" }} - {{.Row.PolicyEvaluated.SPF}} - {{- else }} - {{.Row.PolicyEvaluated.SPF}} - {{- end }} - {{.AuthResults.DKIM.Domain}} - {{- if eq .AuthResults.DKIM.Result "pass"}} - {{.AuthResults.DKIM.Result}} - {{- else if eq .AuthResults.DKIM.Result "fail"}} - {{.AuthResults.DKIM.Result}} - {{- else}} - {{.AuthResults.DKIM.Result}} - {{- end}} - {{.AuthResults.SPF.Domain}} - {{- if eq .AuthResults.SPF.Result "pass"}} - {{.AuthResults.SPF.Result}} - {{- else if eq .AuthResults.SPF.Result "fail"}} - {{.AuthResults.SPF.Result}} - {{- else}} - {{.AuthResults.SPF.Result}} - {{- end}} -
-
-
-
+` + htmlMain + ` ` +// NOTE(moorereason): This template assumes only one SPF result will be present even though the DMARC spec allows for multiple. const txtTmpl = ` DMARC report with id {{.ReportMetadata.ReportID}} Organization: {{.ReportMetadata.ExtraContactInfo}} ({{.ReportMetadata.Email}})) Date range: since {{.ReportMetadata.DateRange.Begin.UTC}} until {{.ReportMetadata.DateRange.End.UTC}} Policy published: {{.PolicyPublished.Domain}}: p={{.PolicyPublished.Policy}} sp={{.PolicyPublished.SPolicy}} pct={{.PolicyPublished.Pct}} adkim={{.PolicyPublished.ADKIM}} aspf={{.PolicyPublished.ASPF}} ------------------------------------------------------------------------------------------------------------------------- -{{printf "%23v | %32v | %57v |" "" "policy evaluated" "auth results" }} ------------------------------------------------------------------------------------------------------------------------- -{{printf "%16v | %4v | %10v | %8v | %8v | %16v | %8v | %16v | %8v | %v" "ip" "msgs" "disp" "dkim" "spf" "dkim domain" "dkim res" "spf domain" "spf res" "hostname" }} ------------------------------------------------------------------------------------------------------------------------- +-------------------------------------------------------------------------------------------------------------------------- +{{printf "%24v | %32v | %58v |" "" "policy evaluated" "auth results" }} +-------------------------------------------------------------------------------------------------------------------------- +{{printf "%17v | %4v | %10v | %8v | %8v | %16v | %9v | %16v | %8v | %v" "ip" "msgs" "disp" "dkim" "spf" "dkim domain" "dkim res" "spf domain" "spf res" "hostname" }} +-------------------------------------------------------------------------------------------------------------------------- {{- range .Records }} -{{- if .IsPassed }} -{{ printf "* %14v | %4v | %10v | %8v | %8v | %16v | %8v | %16v | %8v | %v" .Row.SourceIP .Row.Count .Row.PolicyEvaluated.Disposition .Row.PolicyEvaluated.DKIM .Row.PolicyEvaluated.SPF .AuthResults.DKIM.Domain .AuthResults.DKIM.Result .AuthResults.SPF.Domain .AuthResults.SPF.Result .Row.SourceHostname}} -{{- else }} -{{ printf "%16v | %4v | %10v | %8v | %8v | %16v | %8v | %16v | %8v | %v" .Row.SourceIP .Row.Count .Row.PolicyEvaluated.Disposition .Row.PolicyEvaluated.DKIM .Row.PolicyEvaluated.SPF .AuthResults.DKIM.Domain .AuthResults.DKIM.Result .AuthResults.SPF.Domain .AuthResults.SPF.Result .Row.SourceHostname}} -{{- end}} + {{- $prefix := " " }}{{ if .IsPassed }}{{ $prefix = "* " }}{{ end }} + {{- $dkimLen := len .AuthResults.DKIM }} + {{- if eq $dkimLen 0 }} +{{ printf "%2v%15v | %4v | %10v | %8v | %8v | %16v | %9v | %16v | %8v | %v" $prefix .Row.SourceIP .Row.Count .Row.PolicyEvaluated.Disposition .Row.PolicyEvaluated.DKIM .Row.PolicyEvaluated.SPF "" "" (index .AuthResults.SPF 0).Domain (index .AuthResults.SPF 0).Result .Row.SourceHostname }} + {{- else if eq $dkimLen 1 }} + {{- $dkimDomain := (index .AuthResults.DKIM 0).Domain }} + {{- $dkimResult := (index .AuthResults.DKIM 0).Result }} +{{ printf "%2v%15v | %4v | %10v | %8v | %8v | %16v | %9v | %16v | %8v | %v" $prefix .Row.SourceIP .Row.Count .Row.PolicyEvaluated.Disposition .Row.PolicyEvaluated.DKIM .Row.PolicyEvaluated.SPF $dkimDomain $dkimResult (index .AuthResults.SPF 0).Domain (index .AuthResults.SPF 0).Result .Row.SourceHostname }} + {{- else }} + {{- $rec := . }} + {{- range $i, $res := .AuthResults.DKIM }} + {{- $dkimDomain := $res.Domain }} + {{- $dkimResult := $res.Result }} + {{- if eq $i 0 }} +{{ printf "%2v%15v | %4v | %10v | %8v | %8v | %16v | %9v | %16v | %8v | %v" $prefix $rec.Row.SourceIP $rec.Row.Count $rec.Row.PolicyEvaluated.Disposition $rec.Row.PolicyEvaluated.DKIM $rec.Row.PolicyEvaluated.SPF $dkimDomain $dkimResult (index $rec.AuthResults.SPF 0).Domain (index $rec.AuthResults.SPF 0).Result $rec.Row.SourceHostname }} + {{- else }} +{{ printf "%2v%15v | %4v | %10v | %8v | %8v | %16v | %9v | %16v | %8v | %v" "" "" "" "" "" "" $dkimDomain $dkimResult "" "" "" }} + {{- end }} + {{- end }} + {{- end }} {{- end }} {{ printf "Total: %v, passed %v, failed %v" .MessagesStats.All .MessagesStats.Passed .MessagesStats.Failed }} diff --git a/pkg/dmarc/dmarc.go b/pkg/dmarc/dmarc.go index fe9eb34..cc53f42 100644 --- a/pkg/dmarc/dmarc.go +++ b/pkg/dmarc/dmarc.go @@ -200,8 +200,8 @@ type Identifiers struct { // AuthResults represents feedback>record>auth_results section type AuthResults struct { - DKIM DKIMAuthResult `xml:"dkim" json:"dkim"` - SPF SPFAuthResult `xml:"spf" json:"spf"` + DKIM []DKIMAuthResult `xml:"dkim" json:"dkim"` + SPF []SPFAuthResult `xml:"spf" json:"spf"` } // DKIMAuthResult represnets feedback>record>auth_results>dkim sections diff --git a/pkg/dmarc/dmarc_test.go b/pkg/dmarc/dmarc_test.go index c923321..5fd4edb 100644 --- a/pkg/dmarc/dmarc_test.go +++ b/pkg/dmarc/dmarc_test.go @@ -43,15 +43,24 @@ var xmlRecord1 = Record{ EnvelopeFrom: "", }, AuthResults: AuthResults{ - DKIM: DKIMAuthResult{ - Domain: "test.net", - Result: "pass", - Selector: "selector", + DKIM: []DKIMAuthResult{ + { + Domain: "test1.net", + Result: "fail", + Selector: "selector1", + }, + { + Domain: "test2.net", + Result: "pass", + Selector: "selector2", + }, }, - SPF: SPFAuthResult{ - Domain: "test.net", - Result: "pass", - Scope: "mfrom", + SPF: []SPFAuthResult{ + { + Domain: "test.net", + Result: "pass", + Scope: "mfrom", + }, }, }, } @@ -71,15 +80,19 @@ var xmlRecord2 = Record{ EnvelopeFrom: "", }, AuthResults: AuthResults{ - DKIM: DKIMAuthResult{ - Domain: "test2.net", - Result: "fail", - Selector: "selector", + DKIM: []DKIMAuthResult{ + { + Domain: "test2.net", + Result: "fail", + Selector: "selector", + }, }, - SPF: SPFAuthResult{ - Domain: "test2.net", - Result: "softfail", - Scope: "mfrom", + SPF: []SPFAuthResult{ + { + Domain: "test2.net", + Result: "softfail", + Scope: "mfrom", + }, }, }, } @@ -225,15 +238,13 @@ var xmlEmptyReport = Report{ EnvelopeFrom: "", }, AuthResults: AuthResults{ - DKIM: DKIMAuthResult{ - Domain: "", - Result: "", - Selector: "", - }, - SPF: SPFAuthResult{ - Domain: "", - Result: "", - Scope: "", + DKIM: nil, + SPF: []SPFAuthResult{ + { + Domain: "", + Result: "", + Scope: "", + }, }, }, }, @@ -259,6 +270,6 @@ func TestReadParse_Empty(t *testing.T) { } if !reflect.DeepEqual(out, xmlEmptyReport) { - t.Errorf("ReadParse(%v): parsed structs are invalid: %+v", testFile, out) + t.Errorf("ReadParse(%v): parsed structs are invalid:\nGOT:\n%#v\nWANT:\n%#v", testFile, out, xmlEmptyReport) } } diff --git a/pkg/dmarc/merge_test.go b/pkg/dmarc/merge_test.go index b946c09..88a1a84 100644 --- a/pkg/dmarc/merge_test.go +++ b/pkg/dmarc/merge_test.go @@ -46,15 +46,19 @@ func TestReport_MergeRecord(t *testing.T) { EnvelopeFrom: "", }, AuthResults: AuthResults{ - DKIM: DKIMAuthResult{ - Domain: "test3.net", - Result: "fail", - Selector: "selector", + DKIM: []DKIMAuthResult{ + { + Domain: "test3.net", + Result: "fail", + Selector: "selector", + }, }, - SPF: SPFAuthResult{ - Domain: "test3.net", - Result: "softfail", - Scope: "mfrom", + SPF: []SPFAuthResult{ + { + Domain: "test3.net", + Result: "softfail", + Scope: "mfrom", + }, }, }, } diff --git a/pkg/dmarc/testdata/test.xml b/pkg/dmarc/testdata/test.xml index 3de6798..e590988 100644 --- a/pkg/dmarc/testdata/test.xml +++ b/pkg/dmarc/testdata/test.xml @@ -32,9 +32,14 @@ - test.net + test1.net + fail + selector1 + + + test2.net pass - selector + selector2 test.net diff --git a/pkg/dmarc/testdata/test.xml.gz b/pkg/dmarc/testdata/test.xml.gz index fdd5cb1..8917948 100644 Binary files a/pkg/dmarc/testdata/test.xml.gz and b/pkg/dmarc/testdata/test.xml.gz differ diff --git a/pkg/dmarc/testdata/test.xml.gz.gz b/pkg/dmarc/testdata/test.xml.gz.gz index 2f8ffe8..864c44f 100644 Binary files a/pkg/dmarc/testdata/test.xml.gz.gz and b/pkg/dmarc/testdata/test.xml.gz.gz differ diff --git a/pkg/dmarc/testdata/test.xml.zip b/pkg/dmarc/testdata/test.xml.zip index 4ec40c2..6f4e96a 100644 Binary files a/pkg/dmarc/testdata/test.xml.zip and b/pkg/dmarc/testdata/test.xml.zip differ diff --git a/pkg/dmarc/testdata/test2.xml b/pkg/dmarc/testdata/test2.xml index f69affa..3461177 100644 --- a/pkg/dmarc/testdata/test2.xml +++ b/pkg/dmarc/testdata/test2.xml @@ -32,9 +32,14 @@ - test.net + test1.net + fail + selector1 + + + test2.net pass - selector + selector2 test.net