11package parser
22
33import (
4+ "bytes"
45 "encoding/json"
56 "io"
67
@@ -18,11 +19,22 @@ func NewJSONBatchParser() *JSONBatchParser {
1819
1920func (p * JSONBatchParser ) ParseBatch (r io.Reader ) ([]domain.IncomingReport , error ) {
2021 decoder := json .NewDecoder (r )
21- var reports []domain.IncomingReport
22- if err := decoder .Decode (& reports ); err != nil {
22+
23+ var payload json.RawMessage
24+ if err := decoder .Decode (& payload ); err != nil {
2325 return nil , errors .Wrap (err , "decode reports payload" )
2426 }
2527
28+ payload = bytes .TrimSpace (payload )
29+ if len (payload ) == 0 {
30+ return nil , errors .New ("reports payload must include at least one report" )
31+ }
32+
33+ reports , err := parseReportsPayload (payload )
34+ if err != nil {
35+ return nil , err
36+ }
37+
2638 if len (reports ) == 0 {
2739 return nil , errors .New ("reports payload must include at least one report" )
2840 }
@@ -34,3 +46,50 @@ func (p *JSONBatchParser) ParseBatch(r io.Reader) ([]domain.IncomingReport, erro
3446
3547 return reports , nil
3648}
49+
50+ func parseReportsPayload (payload json.RawMessage ) ([]domain.IncomingReport , error ) {
51+ if payload [0 ] == '[' {
52+ var reports []domain.IncomingReport
53+ if err := json .Unmarshal (payload , & reports ); err != nil {
54+ return nil , errors .Wrap (err , "decode reports payload" )
55+ }
56+ return reports , nil
57+ }
58+
59+ if payload [0 ] == '{' {
60+ return parseLegacyCSPReportPayload (payload )
61+ }
62+
63+ return nil , errors .New ("reports payload must be a JSON array or object" )
64+ }
65+
66+ func parseLegacyCSPReportPayload (payload json.RawMessage ) ([]domain.IncomingReport , error ) {
67+ var legacy struct {
68+ CSPReport json.RawMessage `json:"csp-report"`
69+ }
70+
71+ if err := json .Unmarshal (payload , & legacy ); err != nil {
72+ return nil , errors .Wrap (err , "decode reports payload" )
73+ }
74+
75+ trimmed := bytes .TrimSpace (legacy .CSPReport )
76+ if len (trimmed ) == 0 {
77+ return nil , errors .New ("legacy csp payload must include csp-report" )
78+ }
79+ if trimmed [0 ] != '{' {
80+ return nil , errors .New ("legacy csp payload must include csp-report object" )
81+ }
82+
83+ var body struct {
84+ DocumentURI string `json:"document-uri"`
85+ }
86+ if err := json .Unmarshal (trimmed , & body ); err != nil {
87+ return nil , errors .Wrap (err , "decode legacy csp-report body" )
88+ }
89+
90+ return []domain.IncomingReport {{
91+ Type : "csp-violation" ,
92+ URL : body .DocumentURI ,
93+ Body : trimmed ,
94+ }}, nil
95+ }
0 commit comments