1package tlsrpt
2
3import (
4 "encoding/json"
5 "os"
6 "strings"
7 "testing"
8)
9
10const reportJSON = `{
11 "organization-name": "Company-X",
12 "date-range": {
13 "start-datetime": "2016-04-01T00:00:00Z",
14 "end-datetime": "2016-04-01T23:59:59Z"
15 },
16 "contact-info": "sts-reporting@company-x.example",
17 "report-id": "5065427c-23d3-47ca-b6e0-946ea0e8c4be",
18 "policies": [{
19 "policy": {
20 "policy-type": "sts",
21 "policy-string": ["version: STSv1","mode: testing",
22 "mx: *.mail.company-y.example","max_age: 86400"],
23 "policy-domain": "company-y.example",
24 "mx-host": ["*.mail.company-y.example"]
25 },
26 "summary": {
27 "total-successful-session-count": 5326,
28 "total-failure-session-count": 303
29 },
30 "failure-details": [{
31 "result-type": "certificate-expired",
32 "sending-mta-ip": "2001:db8:abcd:0012::1",
33 "receiving-mx-hostname": "mx1.mail.company-y.example",
34 "failed-session-count": 100
35 }, {
36 "result-type": "starttls-not-supported",
37 "sending-mta-ip": "2001:db8:abcd:0013::1",
38 "receiving-mx-hostname": "mx2.mail.company-y.example",
39 "receiving-ip": "203.0.113.56",
40 "failed-session-count": 200,
41 "additional-information": "https://reports.company-x.example/report_info ? id = 5065427 c - 23 d3# StarttlsNotSupported "
42 }, {
43 "result-type": "validation-failure",
44 "sending-mta-ip": "198.51.100.62",
45 "receiving-ip": "203.0.113.58",
46 "receiving-mx-hostname": "mx-backup.mail.company-y.example",
47 "failed-session-count": 3,
48 "failure-reason-code": "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
49 }]
50 }]
51 }`
52
53// ../rfc/8460:1015
54var tlsrptMessage = strings.ReplaceAll(`From: tlsrpt@mail.sender.example.com
55Date: Fri, May 09 2017 16:54:30 -0800
56To: mts-sts-tlsrpt@example.net
57Subject: Report Domain: example.net
58Submitter: mail.sender.example.com
59Report-ID: <735ff.e317+bf22029@example.net>
60TLS-Report-Domain: example.net
61TLS-Report-Submitter: mail.sender.example.com
62MIME-Version: 1.0
63Content-Type: multipart/report; report-type="tlsrpt";
64 boundary="----=_NextPart_000_024E_01CC9B0A.AFE54C00"
65Content-Language: en-us
66
67This is a multipart message in MIME format.
68
69------=_NextPart_000_024E_01CC9B0A.AFE54C00
70Content-Type: text/plain; charset="us-ascii"
71Content-Transfer-Encoding: 7bit
72
73This is an aggregate TLS report from mail.sender.example.com
74
75------=_NextPart_000_024E_01CC9B0A.AFE54C00
76Content-Type: application/tlsrpt+json
77Content-Transfer-Encoding: 8bit
78Content-Disposition: attachment;
79 filename="mail.sender.example!example.com!1013662812!1013749130.json.gz"
80
81`+reportJSON+`
82
83------=_NextPart_000_024E_01CC9B0A.AFE54C00--
84`, "\n", "\r\n")
85
86// Message without multipart.
87var tlsrptMessage2 = strings.ReplaceAll(`From: tlsrpt@mail.sender.example.com
88To: mts-sts-tlsrpt@example.net
89Subject: Report Domain: example.net
90Report-ID: <735ff.e317+bf22029@example.net>
91TLS-Report-Domain: example.net
92TLS-Report-Submitter: mail.sender.example.com
93MIME-Version: 1.0
94Content-Type: application/tlsrpt+json
95Content-Transfer-Encoding: 8bit
96Content-Disposition: attachment;
97 filename="mail.sender.example!example.com!1013662812!1013749130.json.gz"
98
99`+reportJSON+`
100`, "\n", "\r\n")
101
102func TestReport(t *testing.T) {
103 // ../rfc/8460:1756
104
105 var report Report
106 dec := json.NewDecoder(strings.NewReader(reportJSON))
107 dec.DisallowUnknownFields()
108 if err := dec.Decode(&report); err != nil {
109 t.Fatalf("parsing report: %s", err)
110 }
111
112 if _, err := ParseMessage(xlog, strings.NewReader(tlsrptMessage)); err != nil {
113 t.Fatalf("parsing TLSRPT from message: %s", err)
114 }
115
116 if _, err := ParseMessage(xlog, strings.NewReader(tlsrptMessage2)); err != nil {
117 t.Fatalf("parsing TLSRPT from message: %s", err)
118 }
119
120 if _, err := ParseMessage(xlog, strings.NewReader(strings.ReplaceAll(tlsrptMessage, "multipart/report", "multipart/related"))); err != ErrNoReport {
121 t.Fatalf("got err %v, expected ErrNoReport", err)
122 }
123
124 if _, err := ParseMessage(xlog, strings.NewReader(strings.ReplaceAll(tlsrptMessage, "application/tlsrpt+json", "application/json"))); err != ErrNoReport {
125 t.Fatalf("got err %v, expected ErrNoReport", err)
126 }
127
128 files, err := os.ReadDir("../testdata/tlsreports")
129 if err != nil {
130 t.Fatalf("listing reports: %s", err)
131 }
132 for _, file := range files {
133 f, err := os.Open("../testdata/tlsreports/" + file.Name())
134 if err != nil {
135 t.Fatalf("open %q: %s", file, err)
136 }
137 if _, err := ParseMessage(xlog, f); err != nil {
138 t.Fatalf("parsing TLSRPT from message %q: %s", file.Name(), err)
139 }
140 f.Close()
141 }
142}
143
144func FuzzParseMessage(f *testing.F) {
145 f.Add(tlsrptMessage)
146 f.Fuzz(func(t *testing.T, s string) {
147 ParseMessage(xlog, strings.NewReader(s))
148 })
149}
150