Middleware for monitoring CSP Violations
jrson83 opened this issue · comments
With the CSP middleware it is possible to enable reporting of CSP violations to an API endpoint using the legacy v0 Report-To
, or v1 Reporting-Endpoints
directives (more info). Using the Reporting API, the browser sends a violation report as an HTTP POST request with content type: ['application/json', 'application/csp-report', 'application/reports+json']
to the endpoint.
Would it be possible to create a middleware to setup an API endpoint and listen for incoming reports, like described here in the example for node js, to listen for incoming reports?
// node js example
app.use(
bodyParser.json({
type: [
'application/json',
'application/csp-report',
'application/reports+json',
],
})
);
app.post('/__cspreport__', (req, res) => {
console.log(req.body);
});
Another great example how this could be done found in this blog post Monitoring Content Security.
Lume is not intended to be a server-side framework that can handle routers and http apis. The http middleware system is designed to server static files, so maybe the monitoring system should be implemented externally, so the logs can be stored in a database or the filesystem.
If the purpose of this middleware is only to console.log()
the incoming reports (so they can be seen in the Logs section of Deno deploy), maybe this functionality could be implemented in the CSP middleware (btw, I've moved this issue to the experimental plugins repo).
To slim down a bit the middleware, I propose the following:
- Move the csp builder to a different module, so it can be reused by other projects, not only Lume.
- Allow to customize the report URI to be handled by the own middleware.
Imagine something like this (pseudocode):
import { CspOptions, builder } from "https://deno.land/x/csp_builder/mod.ts";
interface Options {
/** Options to build the CSP headers */
csp: CspOptions;
/** To log the incoming reports */
logReports: boolean;
}
export default function csp(userOptions?: Partial<Options>): Middleware {
const options = merge(defaults, userOptions);
return async (request: Request, next: RequestHandler) => {
if (options.logReports && request.method === "POST" && request.url === options.csp.reportUri) {
cons json = await request.json();
console.log(json);
return new Response();
}
const response = await next(request);
const { headers } = response;
builder(headers, options.options);
return response;
};
}