package reportscollector import ( "encoding/json" "net" "net/http" "net/url" "time" "github.com/jhillyerd/enmime" ) // Generic browser report as per https://www.w3.org/TR/reporting/. type report struct { Type string `json:"type"` Age int `json:"age"` URL string `json:"url"` UserAgent string `json:"user_agent"` Body map[string]interface{} `json:"body"` } type ReportHandler struct{} func (h *ReportHandler) Name() string { return "report-api" } func (h *ReportHandler) Parse(contentType string, req *http.Request) ([]Event, error) { if contentType != "application/reports+json" { return nil, ErrNoMatch } var reports []*report if err := json.NewDecoder(req.Body).Decode(&reports); err != nil { return nil, err } var events []Event for _, r := range reports { events = append(events, h.eventFromReport(req, r)) } return events, nil } func (h *ReportHandler) ParseMIME(*enmime.Part) ([]Event, error) { return nil, ErrNoMatch } func (h *ReportHandler) eventFromReport(req *http.Request, report *report) Event { ts := time.Now().Add(time.Duration(-report.Age) * time.Second) e := make(Event) if asn, ok := lookupASN(getRemoteIP(req)); ok { e.Set("asn", asn) } e.Set("type", report.Type) e.Set("event_timestamp", ts) e.Set("url", report.URL) e.Set("domain", domainFromURL(report.URL)) e.Set("user_agent", report.UserAgent) e.Set("body", report.Body) return e } func domainFromURL(u string) string { if uri, err := url.Parse(u); err == nil { if host, _, err := net.SplitHostPort(uri.Host); err == nil { return host } return uri.Host } return "" }