Search code examples
node.jsmeteorcontent-security-policyhelmet.js

Setup for Helmet and ReportURi?


I'm using Helmet and getting one of these console.log messages:

:3000/:48 Refused to execute inline script because it violates the following Content Security Policy directive: "script-src ..."

I'm trying to find out what script is being blocked.

I have Helmet initialized with these options:

const helmetOptions = {
    crossOriginEmbedderPolicy: false,
    contentSecurityPolicy: {
        blockAllMixedContent: true,
        directives: {
            reportUri: '/report-violation',
            defaultSrc: [self],
            scriptSrc: [.....

On my server, I have this set up to capture messages sent to reportUri:

WebApp.connectHandlers.use('/report-violation', function (req, res, next) {
    debugger;
    const report = req.body;
    console.log('cp #2. Helmet CSP Violation:', report);
    next();
});

The server function is being hit, but req.body is coming in undefined - here's the server console.log:

cp #2. Helmet CSP Violation: undefined

cp #2. Helmet CSP Violation: undefined

What am I missing?

UPDATE

I checked to see if I need to json-parse the req body, but req is arriving already json-parsed into fields with keys and values.

Note: the server function may look a little funky but that's how you access Express in Meteor, which is my build tool. :)


Solution

  • This seems to be working:

    WebApp.connectHandlers.use('/report-violation', (req, res, next) => {
        // Check if the request method is POST
        if (req.method === 'POST') {
            let report = '';
            req.on('data', chunk => {
                report += chunk;
            });
            req.on('end', () => {
                try {
                    // Attempt to parse the report as JSON
                    const parsedReport = JSON.parse(report);
                    try{
                        let violatedDirective = parsedReport["csp-report"]["violated-directive"]
                        let effectiveDirective = parsedReport["csp-report"]["effective-directive"]
                        let blockedUri = parsedReport["csp-report"]["blocked-uri"]
                        console.log('Helmet CSP Violation: ', blockedUri);
                        console.log('violated-directive: ', violatedDirective);
                        console.log('effective-directive: ', effectiveDirective);
                        console.log('-----');
                    }
                    catch (Exception){
                        console.log('No blockedUri was found.')
                    }
    
                    // Send a response indicating the method is allowed
                    res.writeHead(200, { 'Content-Type': 'text/plain' });
                    res.end('POST method is allowed for this route.');
                } catch (error) {
                    // Handle the case where the report is not valid JSON
                    console.error('Error parsing report as JSON:', error);
                    res.writeHead(400, { 'Content-Type': 'text/plain' });
                    res.end('Bad Request: Invalid JSON');
                }
            });
        } else {
            // If not POST, respond with a 405 status code and include the Allow header
            res.writeHead(405, { 'Content-Type': 'text/plain', 'Allow': 'POST' });
            res.end('Method Not Allowed');
        }
        // No need to call next() here since we're sending a response
    });
    

    Note: Meteor's WebApp.connectHandlers have some differences from express. This code would need revisions to work with express.