From d7742ac670ee813ae74c617150281ad9565d49d6 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Fri, 16 Mar 2018 15:58:20 +0200 Subject: [PATCH] Refactor CSP module --- server/csp.js | 157 +++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 77 deletions(-) diff --git a/server/csp.js b/server/csp.js index e5d172ca..1f1cd7ae 100644 --- a/server/csp.js +++ b/server/csp.js @@ -1,8 +1,65 @@ const helmet = require('helmet'); const dev = process.env.REACT_APP_ENV === 'development'; -const googleAnalyticsEnabled = !!process.env.REACT_APP_GOOGLE_ANALYTICS_ID; -const sentryClientEnabled = !!process.env.REACT_APP_PUBLIC_SENTRY_DSN; +const self = "'self'"; +const unsafeInline = "'unsafe-inline'"; +const unsafeEval = "'unsafe-eval'"; +const data = 'data:'; +const devImagesMaybe = dev ? ['*.localhost:8000'] : []; + +// Default CSP whitelist. +// +// NOTE: Do not change these in the customisations, make custom +// additions within the exported function in the bottom of this file. +const defaultDirectives = { + baseUri: [self], + defaultSrc: [self], + connectSrc: [ + self, + process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL, + 'maps.googleapis.com', + + // Google Analytics + 'www.google-analytics.com', + 'stats.g.doubleclick.net', + + 'sentry.io', + '*.stripe.com', + ], + fontSrc: [self, data, 'assets-sharetribecom.sharetribe.com', 'fonts.gstatic.com'], + frameSrc: [self, '*.stripe.com'], + imgSrc: [ + self, + data, + ...devImagesMaybe, + '*.imgix.net', + 'sharetribe.imgix.net', // Safari 9.1 didn't recognize asterisk rule. + + // Styleguide placeholder images + 'lorempixel.com', + 'via.placeholder.com', + + 'maps.googleapis.com', + '*.gstatic.com', + + // Google Analytics + 'www.google.com', + 'www.google-analytics.com', + 'stats.g.doubleclick.net', + + '*.stripe.com', + ], + scriptSrc: [ + self, + unsafeInline, + unsafeEval, + data, + 'maps.googleapis.com', + '*.google-analytics.com', + 'js.stripe.com', + ], + styleSrc: [self, unsafeInline, 'fonts.googleapis.com'], +}; /** * Middleware for creating a Content Security Policy @@ -17,87 +74,33 @@ const sentryClientEnabled = !!process.env.REACT_APP_PUBLIC_SENTRY_DSN; * reported to the report URL instead of blocked */ module.exports = (reportUri, enforceSsl, reportOnly) => { - const self = "'self'"; - const unsafeInline = "'unsafe-inline'"; - const unsafeEval = "'unsafe-eval'"; - const data = 'data:'; - const sharetribeApi = process.env.REACT_APP_SHARETRIBE_SDK_BASE_URL; - const sharetribeAssets = 'assets-sharetribecom.sharetribe.com'; - const imgix = '*.imgix.net'; - // Safari 9.1 didn't recognize asterisk rule. - const sharetribeImgix = 'sharetribe.imgix.net'; - const loremPixel = 'lorempixel.com'; - const placeholder = 'via.placeholder.com'; - const google = 'www.google.com'; - const googleAnalytics = 'www.google-analytics.com'; - const googleAnalyticsDoubleClick = 'stats.g.doubleclick.net'; - const googleMaps = 'maps.googleapis.com'; - const googleStaticFonts = 'fonts.gstatic.com'; - const googleStatic = '*.gstatic.com'; - const googleFonts = 'fonts.googleapis.com'; - const stripeJs = 'js.stripe.com'; - const stripeQ = 'q.stripe.com'; - const stripeApi = 'api.stripe.com'; - const sentryApi = 'sentry.io'; + // ================ START CUSTOM CSP URLs ================ // - const scriptSrc = [self, unsafeInline, unsafeEval, data, googleMaps, stripeJs]; + // Add custom CSP whitelisted URLs here. See commented example + // below. For format specs and examples, see: + // https://content-security-policy.com/ - const styleSrc = [self, unsafeInline, googleFonts]; + // Example: extend default img directive with custom domain + // const { imgSrc = [] } = defaultDirectives; + // const exampleImgSrc = imgSrc.concat('my-custom-domain.example.com'); - const imgSrc = [ - self, - data, - imgix, - sharetribeImgix, - loremPixel, - placeholder, - stripeQ, - googleMaps, - googleStatic, - ]; - - const connectSrc = [self, sharetribeApi, googleMaps, stripeApi]; - - const fontSrc = [self, data, sharetribeAssets, googleStaticFonts]; - - const frameSrc = [self, stripeJs]; - - if (googleAnalyticsEnabled) { - // Only whitelist Google Analytics URLs when the analytics is - // enabled. - scriptSrc.push(googleAnalytics); - imgSrc.push(google); - imgSrc.push(googleAnalytics); - imgSrc.push(googleAnalyticsDoubleClick); - connectSrc.push(googleAnalytics); - connectSrc.push(googleAnalyticsDoubleClick); - } - if (sentryClientEnabled) { - connectSrc.push(sentryApi); - } - if (dev) { - // The default Core docker-compose setup server images from this - // URL locally. - imgSrc.push('*.localhost:8000'); - } - - const directives = { - scriptSrc, - styleSrc, - imgSrc, - connectSrc, - fontSrc, - frameSrc, - formAction: [self], - defaultSrc: [self], - baseUri: [self], - reportUri, + const customDirectives = { + // Example: Add custom directive override + // imgSrc: exampleImgSrc, }; - if (enforceSsl) { - directives.blockAllMixedContent = true; - } + // ================ END CUSTOM CSP URLs ================ // + const directives = Object.assign( + { + reportUri, + blockAllMixedContent: enforceSsl, + }, + defaultDirectives, + customDirectives + ); + + // See: https://helmetjs.github.io/docs/csp/ return helmet.contentSecurityPolicy({ directives, reportOnly,