src/server/utilities/cspHeader/index.test.js

Summary

Maintainability
D
1 day
Test Coverage
import injectCspHeader from '.';

import {
  generateChildSrc,
  generateConnectSrc,
  generateDefaultSrc,
  generateFontSrc,
  generateFrameSrc,
  generateImgSrc,
  generateScriptSrc,
  generateStyleSrc,
  generateMediaSrc,
  generateWorkerSrc,
} from './directives';

import { bbcDomains, advertisingServiceCountryDomains } from './domainLists';

// Express Fixtures
const req = ({ urlExample = '', originExample = '' } = {}) => ({
  url: urlExample,
  headers: {
    'user-agent': 'local-agent',
    'bbc-origin': originExample,
  },
});

let headers = {};

const res = {
  setHeader: (key, value) => {
    headers[key] = value;
  },
};

const next = jest.fn();

describe('cspHeader', () => {
  afterEach(() => {
    jest.resetAllMocks();
    headers = {};
  });

  afterAll(() => {
    delete process.env.SIMORGH_APP_ENV;
  });

  [
    {
      isAmp: true,
      isLive: true,
      originExample: 'https://www.bbc.com',
      urlExample: 'https://www.bbc.com/pidgin.amp',
      childSrcExpectation: ['blob:'],
      connectSrcExpectation: ["'self' https:"],
      defaultSrcExpectation: [
        ...bbcDomains,
        'https://*.googlesyndication.com',
        "'self'",
      ].sort(),
      fontSrcExpectation: [...bbcDomains].sort(),
      frameSrcExpectation: [
        ...bbcDomains,
        'https://*.ampproject.net',
        'https://*.doubleclick.net',
        'https://edigitalsurvey.com',
        'https://*.googlesyndication.com',
        'https://www.instagram.com',
        'https://www.riddle.com',
        'https://www.tiktok.com',
        'https://www.youtube.com',
        'https://www.youtube-nocookie.com',
        'https://www.facebook.com',
        'https://*.google.com',
        'https://cdn.privacy-mgmt.com',
        'https://*.googleadservices.com',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
      ].sort(),
      imgSrcExpectation: [
        ...bbcDomains,
        'https://*.adsafeprotected.com',
        'https://*.cdninstagram.com',
        'https://ping.chartbeat.net',
        'https://*.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://*.googleusercontent.com',
        'https://*.gstatic.com',
        'https://*.imrworldwide.com',
        'https://*.tiktokcdn.com',
        'https://*.xx.fbcdn.net',
        'https://www.instagram.com',
        'https://www.tiktok.com',
        'https://*.facebook.com',
        'https://sb.scorecardresearch.com',
        'https://i.ytimg.com',
        'https://*.amazon-adsystem.com',
        'https://www.googleadservices.com',
        'https://*.teads.tv',
        "data: 'self'",
      ].sort(),
      scriptSrcExpectation: [
        ...bbcDomains,
        'https://cdn.ampproject.org',
        'https://*.chartbeat.com',
        'https://*.twitter.com',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
        "'unsafe-inline'",
      ].sort(),
      styleSrcExpectation: [...bbcDomains, "'unsafe-inline'"].sort(),
      mediaSrcExpectation: ["'self' blob: https:"],
      workerSrcExpectation: ['blob:', '*.bbc.co.uk', '*.bbc.com'],
    },
    {
      isAmp: false,
      isLive: true,
      originExample: 'https://www.bbc.com',
      urlExample: 'https://www.bbc.com/pidgin',
      childSrcExpectation: ["'self'"],
      connectSrcExpectation: ["'self' https:"],
      defaultSrcExpectation: [
        ...bbcDomains,
        'https://*.googlesyndication.com',
        "'self'",
      ].sort(),
      fontSrcExpectation: [
        ...bbcDomains,
        'data:',
        'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/',
        'https://fonts.gstatic.com',
      ].sort(),
      frameSrcExpectation: [
        ...bbcDomains,
        'https://chartbeat.com',
        'https://*.chartbeat.com',
        'https://www.youtube.com',
        'https://www.youtube-nocookie.com',
        'https://*.twitter.com',
        'https://www.instagram.com',
        'https://bbc.com',
        'https://bbc-maps.carto.com',
        'https://flo.uri.sh',
        'https://www.riddle.com',
        'https://*.doubleclick.net',
        'https://*.googlesyndication.com',
        'https://edigitalsurvey.com',
        'https://www.tiktok.com',
        'https://*.facebook.com',
        'https://*.google.com',
        'https://cdn.privacy-mgmt.com',
        'https://public.flourish.studio',
        'https://*.googleadservices.com',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
      ].sort(),
      imgSrcExpectation: [
        ...bbcDomains,
        'https://*.adsafeprotected.com',
        'https://*.cdninstagram.com',
        'https://ping.chartbeat.net',
        'https://*.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://*.googleusercontent.com',
        'https://*.gstatic.com',
        'https://*.imrworldwide.com',
        'https://sb.scorecardresearch.com',
        'https://*.twimg.com',
        'https://*.twitter.com',
        'https://i.ytimg.com',
        'https://*.tiktokcdn.com',
        'https://*.xx.fbcdn.net',
        'https://*.amazon-adsystem.com',
        'https://www.googleadservices.com',
        'https://*.teads.tv',
        "data: 'self'",
      ].sort(),
      scriptSrcExpectation: [
        ...bbcDomains,
        'https://*.adsafeprotected.com',
        'https://cdn.ampproject.org',
        'https://*.chartbeat.com',
        'https://*.g.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://public.flourish.studio',
        'https://www.riddle.com',
        'https://adservice.google.co.uk',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://www.googletagservices.com',
        'https://bbc.gscontxt.net',
        'https://*.imrworldwide.com',
        'https://*.permutive.com',
        'https://cdn.privacy-mgmt.com',
        'https://www.instagram.com',
        'https://sb.scorecardresearch.com',
        'https://*.twimg.com',
        'https://*.twitter.com',
        'https://*.wearehearken.eu',
        'https://*.webcontentassessor.com',
        'https://www.tiktok.com',
        'https://lf16-tiktok-web.ttwstatic.com',
        'https://*.facebook.com',
        'https://connect.facebook.net',
        'https://*.xx.fbcdn.net',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        ...advertisingServiceCountryDomains,
        "'self'",
        "'unsafe-inline'",
      ].sort(),
      styleSrcExpectation: [
        ...bbcDomains,
        'https://*.twitter.com',
        'https://*.twimg.com',
        'https://fonts.googleapis.com',
        'https://lf16-tiktok-web.ttwstatic.com',
        'https://*.xx.fbcdn.net',
        "'unsafe-inline'",
      ].sort(),
      mediaSrcExpectation: ["'self' blob: https:"],
      workerSrcExpectation: ['blob:', "'self'", '*.bbc.co.uk', '*.bbc.com'],
    },
    {
      isAmp: true,
      isLive: false,
      originExample: 'https://www.test.bbc.com',
      urlExample: 'https://www.test.bbc.com/pidgin.amp',
      childSrcExpectation: ['blob:'],
      connectSrcExpectation: ["'self' https:"],
      defaultSrcExpectation: [
        ...bbcDomains,
        'https://*.googlesyndication.com',
        "'self'",
      ].sort(),
      fontSrcExpectation: [...bbcDomains],
      frameSrcExpectation: [
        ...bbcDomains,
        'https://*.ampproject.net',
        'https://*.doubleclick.net',
        'https://edigitalsurvey.com',
        'https://*.googlesyndication.com',
        'https://www.instagram.com',
        'https://www.riddle.com',
        'https://www.youtube.com',
        'https://www.youtube-nocookie.com',
        'https://www.tiktok.com',
        'https://www.facebook.com',
        'https://*.google.com',
        'https://cdn.privacy-mgmt.com',
        'https://*.googleadservices.com',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
      ].sort(),
      imgSrcExpectation: [
        ...bbcDomains,
        'https://*.adsafeprotected.com',
        'https://logws1363.ati-host.net',
        'https://*.cdninstagram.com',
        'https://ping.chartbeat.net',
        'http://ping.chartbeat.net',
        'https://*.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://*.googleusercontent.com',
        'https://*.gstatic.com',
        'https://*.imrworldwide.com',
        'https://www.instagram.com',
        'https://sb.scorecardresearch.com',
        'https://i.ytimg.com',
        'https://www.tiktok.com',
        'https://*.tiktokcdn.com',
        'https://*.facebook.com',
        'https://*.xx.fbcdn.net',
        'https://*.amazon-adsystem.com',
        'https://www.googleadservices.com',
        'https://*.teads.tv',
        "data: 'self'",
      ].sort(),
      scriptSrcExpectation: [
        ...bbcDomains,
        'https://cdn.ampproject.org',
        'https://*.chartbeat.com',
        'https://*.twitter.com',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
        "'unsafe-inline'",
      ].sort(),
      styleSrcExpectation: [...bbcDomains, "'unsafe-inline'"].sort(),
      mediaSrcExpectation: ["'self' blob: https:"],
      workerSrcExpectation: ['blob:', '*.bbc.co.uk', '*.bbc.com'],
    },
    {
      isAmp: false,
      isLive: false,
      originExample: 'https://www.test.bbc.com',
      urlExample: 'https://www.test.bbc.com/pidgin',
      childSrcExpectation: ["'self'"],
      connectSrcExpectation: ["'self' https:"],
      defaultSrcExpectation: [
        ...bbcDomains,
        'https://*.googlesyndication.com',
        "'self'",
      ].sort(),
      fontSrcExpectation: [
        ...bbcDomains,
        'data:',
        'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/',
        'https://fonts.gstatic.com',
      ].sort(),
      frameSrcExpectation: [
        ...bbcDomains,
        'https://bbc-maps.carto.com',
        'https://chartbeat.com',
        'https://*.chartbeat.com',
        'https://*.doubleclick.net',
        'https://edigitalsurvey.com',
        'https://bbc.com',
        'https://flo.uri.sh',
        'https://*.googlesyndication.com',
        'https://www.instagram.com',
        'https://www.riddle.com',
        'https://*.twitter.com',
        'https://www.youtube.com',
        'https://www.youtube-nocookie.com',
        'https://www.tiktok.com',
        'https://*.facebook.com',
        'https://*.google.com',
        'https://cdn.privacy-mgmt.com',
        'https://public.flourish.studio',
        'https://*.googleadservices.com',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        "'self'",
      ].sort(),
      imgSrcExpectation: [
        ...bbcDomains,
        'https://ping.chartbeat.net',
        'https://logws1363.ati-host.net',
        'http://ping.chartbeat.net',
        'https://*.twitter.com',
        'https://*.cdninstagram.com',
        'https://*.adsafeprotected.com',
        'https://i.ytimg.com',
        'https://*.twimg.com',
        'https://*.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://*.googleusercontent.com',
        'https://*.gstatic.com',
        'https://*.imrworldwide.com',
        'https://sb.scorecardresearch.com',
        'https://*.tiktokcdn.com',
        'https://*.xx.fbcdn.net',
        'https://*.amazon-adsystem.com',
        'https://www.googleadservices.com',
        'https://*.teads.tv',
        "data: 'self'",
      ].sort(),
      scriptSrcExpectation: [
        ...bbcDomains,
        'https://*.wearehearken.eu',
        'https://*.chartbeat.com',
        'http://*.chartbeat.com',
        'http://localhost:1124',
        'https://*.twitter.com',
        'https://www.instagram.com',
        'https://*.twimg.com',
        'https://public.flourish.studio',
        'https://www.riddle.com',
        'https://*.adsafeprotected.com',
        'https://cdn.ampproject.org',
        'https://*.g.doubleclick.net',
        'https://*.effectivemeasure.net',
        'https://adservice.google.co.uk',
        'https://*.google.com',
        'https://*.googlesyndication.com',
        'https://www.googletagservices.com',
        'https://bbc.gscontxt.net',
        'https://sb.scorecardresearch.com',
        'https://*.imrworldwide.com',
        'https://*.permutive.com',
        'https://cdn.privacy-mgmt.com',
        'https://www.tiktok.com',
        'https://lf16-tiktok-web.ttwstatic.com',
        'https://*.facebook.com',
        'https://connect.facebook.net',
        'https://*.xx.fbcdn.net',
        'https://*.webcontentassessor.com',
        'https://*.amazon-adsystem.com',
        'https://*.teads.tv',
        'https://*.mapcreator.io',
        'https://*.thomsonreuters.com',
        ...advertisingServiceCountryDomains,
        "'self'",
        "'unsafe-inline'",
      ].sort(),
      styleSrcExpectation: [
        ...bbcDomains,
        'https://*.twitter.com',
        'https://*.twimg.com',
        'https://fonts.googleapis.com',
        'https://lf16-tiktok-web.ttwstatic.com',
        'https://*.xx.fbcdn.net',
        "'unsafe-inline'",
      ].sort(),
      mediaSrcExpectation: ["'self' blob: https:"],
      workerSrcExpectation: ['blob:', "'self'", '*.bbc.co.uk', '*.bbc.com'],
    },
  ].forEach(
    ({
      isAmp,
      isLive,
      originExample,
      urlExample,
      childSrcExpectation,
      connectSrcExpectation,
      defaultSrcExpectation,
      fontSrcExpectation,
      frameSrcExpectation,
      imgSrcExpectation,
      scriptSrcExpectation,
      styleSrcExpectation,
      mediaSrcExpectation,
      workerSrcExpectation,
    }) => {
      describe(`Given isAmp ${isAmp} & isLive ${isLive}`, () => {
        it(`Then it has this childSrc`, () => {
          expect(generateChildSrc({ isAmp, isLive })).toEqual(
            childSrcExpectation,
          );
        });

        it(`Then it has this connectSrc`, () => {
          expect(generateConnectSrc()).toEqual(connectSrcExpectation);
        });

        it(`Then it has this defaultSrc`, () => {
          expect(generateDefaultSrc({ isAmp, isLive })).toEqual(
            defaultSrcExpectation,
          );
        });

        it(`Then it has this fontSrc`, () => {
          expect(generateFontSrc({ isAmp, isLive })).toEqual(
            fontSrcExpectation,
          );
        });

        it(`Then it has this frameSrc`, () => {
          expect(generateFrameSrc({ isAmp, isLive })).toEqual(
            frameSrcExpectation,
          );
        });

        it(`Then it has this imgSrc`, () => {
          expect(generateImgSrc({ isAmp, isLive })).toEqual(imgSrcExpectation);
        });

        it(`Then it has this scriptSrc`, () => {
          expect(generateScriptSrc({ isAmp, isLive })).toEqual(
            scriptSrcExpectation,
          );
        });

        it(`Then it has this styleSrc`, () => {
          expect(generateStyleSrc({ isAmp, isLive })).toEqual(
            styleSrcExpectation,
          );
        });

        it(`Then it has this mediaSrc`, () => {
          expect(generateMediaSrc()).toEqual(mediaSrcExpectation);
        });

        it(`Then it has this workerSrc`, () => {
          expect(generateWorkerSrc({ isAmp })).toEqual(workerSrcExpectation);
        });

        it(`Then injectCspHeader middleware applies the correct Content-Security-Policy header`, () => {
          process.env.SIMORGH_APP_ENV = isLive ? 'live' : 'test';

          injectCspHeader(req({ urlExample, originExample }), res, next);

          expect(next).toHaveBeenCalled();

          const expectedCSPHeaderString =
            `default-src ${defaultSrcExpectation.join(' ')};` +
            `child-src ${childSrcExpectation.join(' ')};` +
            `connect-src ${connectSrcExpectation.join(' ')};` +
            `font-src ${fontSrcExpectation.join(' ')};` +
            `frame-src ${frameSrcExpectation.join(' ')};` +
            `img-src ${imgSrcExpectation.join(' ')};` +
            `script-src ${scriptSrcExpectation.join(' ')};` +
            `style-src ${styleSrcExpectation.join(' ')};` +
            `media-src ${mediaSrcExpectation.join(' ')};` +
            `worker-src ${workerSrcExpectation.join(' ')};` +
            `report-to worldsvc;` +
            `upgrade-insecure-requests`;

          expect(headers['Content-Security-Policy']).toEqual(
            expectedCSPHeaderString,
          );
        });

        it(`applies the correct report-to header`, () => {
          process.env.SIMORGH_APP_ENV = isLive ? 'live' : 'test';
          process.env.SIMORGH_CSP_REPORTING_ENDPOINT = 'mocked-value';

          injectCspHeader(req({ urlExample, originExample }), res, next);

          expect(headers['report-to']).toEqual(
            JSON.stringify({
              group: 'worldsvc',
              max_age: 2592000,
              endpoints: [
                {
                  url: 'mocked-value',
                  priority: 1,
                },
              ],
              include_subdomains: true,
            }),
          );
        });
      });
    },
  );
});