A simple application to send CSP violation reports to an email address
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

81 lines
2.4 KiB

  1. /*
  2. * Copyright (C) 2019 bn4t
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. package main
  18. import (
  19. "encoding/json"
  20. "github.com/labstack/echo/v4"
  21. "strings"
  22. )
  23. type CSPJson struct {
  24. CspReport struct {
  25. DocumentURI string `json:"document-uri"`
  26. Referrer string `json:"referrer"`
  27. ViolatedDirective string `json:"violated-directive"`
  28. OriginalPolicy string `json:"original-policy"`
  29. BlockedURI string `json:"blocked-uri"`
  30. } `json:"csp-report"`
  31. }
  32. var RateLimitMap = make(map[string]int)
  33. func handleReport(c echo.Context) error {
  34. domain := c.Param("domain")
  35. var cspReportJson CSPJson
  36. ip := c.RealIP()
  37. // Validate the that requester is still in bounds of ratelimit
  38. // if not deny the request
  39. // This is to prevent spam since every CSP report sends a mail
  40. // check if an entry for the ip already exists and add 1 request for the ip
  41. // if it doesn't exist add it to the map
  42. if _, hasValue := RateLimitMap[ip]; hasValue {
  43. // check if ratelimit is exceeded
  44. if RateLimitMap[ip] < Config.RateLimit {
  45. RateLimitMap[ip]++
  46. } else {
  47. return c.String(492, "Too many requests")
  48. }
  49. } else {
  50. RateLimitMap[ip] = 1
  51. }
  52. // deny request if the content-type header is wrong
  53. if !strings.Contains(c.Request().Header.Get("Content-Type"), "application/csp-report") {
  54. return c.String(400, "wrong content-type header")
  55. }
  56. // unmarshal json
  57. err := json.NewDecoder(c.Request().Body).Decode(&cspReportJson)
  58. if err != nil {
  59. return c.String(500, "unable to decode csp report")
  60. }
  61. err = sendCSPMail(domain, cspReportJson.CspReport.DocumentURI, cspReportJson.CspReport.Referrer, cspReportJson.CspReport.ViolatedDirective,
  62. cspReportJson.CspReport.OriginalPolicy, cspReportJson.CspReport.BlockedURI)
  63. if err != nil {
  64. return c.String(500, "internal server error")
  65. }
  66. return c.NoContent(204)
  67. }