DEV Community

Cover image for Add CSP to XMCloud
Jay Bonning
Jay Bonning

Posted on

Add CSP to XMCloud

It is common that you are going to need to pull in external content and use or display it with your website. So, we need to tell the browser where those assets can come from by setting a few values in the header.

The Plugin

This new file will be based on the cors-header.js that is part of the base packages for XMCloud. So we need to create a new plug-in file. I named mine 'csp-header.js'.

src\<project name>\src\lib\next-config\plugins\csp-header.js

const config = require('../../../temp/config');

/**
 * @param {import('next').NextConfig} nextConfig
 */
const cspHeader = (nextConfig = {}) => {
  if (!config.sitecoreApiHost) {
    return nextConfig;
  }
  return Object.assign({}, nextConfig, {
    async headers() {
      const extendHeaders = typeof nextConfig.headers === 'function' ? await nextConfig.headers() : [];

      var fullCSP = '';

      fullCSP = fullCSP.concat(concatAttributes('default-src', config.customCSPDefault));
      fullCSP = fullCSP.concat(concatAttributes('img-src', config.customCSPImg));


      if(fullCSP.length > 0) {
        return [
          ...(await extendHeaders),
          {
            source: '/:path*',
            headers: [
              {
                key: 'Content-Security-Policy',
                value: fullCSP,
              },
            ],
          }
        ];
      }
      else { return [ ...(await extendHeaders) ];}
    },
  });
};

function concatAttributes(cspType, cspValue) {
  var returnAttribute = '';
  if(cspValue !== 'undefined' && cspValue.length > 0) {
    returnAttribute = cspType.concat(' ', cspValue.replace(/\\/g, ''), ';');
  }
  return returnAttribute;
}

module.exports = cspHeader;

Enter fullscreen mode Exit fullscreen mode

There are a couple of things to point out here. I chose to pull this information in from a config so it could be changed. You could easily just put your CSP values in place of these lines

fullCSP = fullCSP.concat(concatAttributes('default-src', config.customCSPDefault));
fullCSP = fullCSP.concat(concatAttributes('img-src', config.customCSPImg));
Enter fullscreen mode Exit fullscreen mode

It is also worth noting that you can also customize which pages this applies by adjusting the source.

source: '/:path*',

The last thing that you will need to know is you will need top stop and start your instance before this plugin is picked up by the application. Expect a decent amount of up.ps1 and down.ps1 while you are doing your testing.

Adding Config Entries

Working through this on a docker instance. I wanted to be able to place a value directly in the .env similar to how it would get set when you are setting up a hosting environment.

CUSTOM_CSP_DEFAULT="\'self\' \'unsafe-inline\' \'unsafe-eval\' data:"
CUSTOM_CSP_IMG="\'self\' data:"
Enter fullscreen mode Exit fullscreen mode

I added these to my .env, but they need to wired into the config to be used by the plugin. To do this you will need to edit the file:

src\<project name>\scripts\generate-config.ts

I removed some lines and replaced them with ... below, but you will want to add an entry for each config line you want incorporated. Note that you can use the parameter directly, or use the constantCase function to pull it in. It will make the translation between the value CUSTOM_CSP_DEFAULT to customCspDefault when it is called.

const defaultConfig: JssConfig = {
...
  customCSPDefault: process.env[`${constantCase(`customCspDefault`)}`],
  customCSPImg: process.env[`${constantCase(`customCspImg`)}`],
...
};
Enter fullscreen mode Exit fullscreen mode

Thanks

A lot of this came from help I received in the Sitecore Slack community. If you are not already there, I suggest you sign up.

Also the people on my team who put up with me on a daily basis.

Top comments (0)