Last active
November 4, 2022 19:04
-
-
Save keeth/d73b6b6491b9dc0339b3865de6addfbd to your computer and use it in GitHub Desktop.
CSP report rate-limiting proxy (cloud function)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { createClient } = require("redis"); | |
const fetch = require("node-fetch"); | |
const REQUESTS_LIMIT = 1; | |
const TTL = 60; // seconds | |
const client = createClient({ | |
url: process.env.REDIS_URL, | |
}); | |
client.on("error", (err) => console.error("Redis error", err)); | |
const HEADERS = ["content-type", "user-agent"]; | |
const CONTENT_TYPES = new Set(["application/csp-report", "application/json"]); | |
async function sendReportToServer(report, opts) { | |
return await fetch( | |
process.env.UPSTREAM_SERVER_URL, | |
{ | |
method: "POST", | |
body: JSON.stringify({ "csp-report": report }), | |
...opts, | |
} | |
); | |
} | |
exports.cspRateLimiter = async (req, res) => { | |
if (!client.isReady) { | |
await client.connect(); | |
} | |
if (!CONTENT_TYPES.has(req.get("content-type"))) { | |
return res.status(400).send("Invalid content type"); | |
} | |
const report = | |
req.body["csp-report"] || JSON.parse(req.body.toString())["csp-report"]; | |
if (!report) { | |
return res.status(400).send("Missing csp-report"); | |
} | |
const key = report["violated-directive"] + " " + report["blocked-uri"]; | |
const count = await client.incr(key); | |
if (count === 1) { | |
await client.expire(key, TTL); | |
} | |
if (count > REQUESTS_LIMIT) { | |
return res.status(429).send("Too many requests!"); | |
} | |
const response = await sendReportToServer(report, { | |
headers: HEADERS.reduce((acc, cur) => { | |
acc[cur] = req.get(cur); | |
return acc; | |
}, {}), | |
}); | |
if (!response.ok) { | |
console.error("Upstream API error", response.status, response.statusText); | |
return res.status(500).send("Sentry error"); | |
} | |
return res.status(204).send(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Google cloud function that proxies CSP reports to some other server, rate-limiting to 1 per (directive, blocked-uri) per minute.
Helps avoid flooding your CSP reporting tool with repetitive reports.