diff --git a/middleware/contentSecurityPolicy.js b/middleware/contentSecurityPolicy.js new file mode 100644 index 000000000..992e41e36 --- /dev/null +++ b/middleware/contentSecurityPolicy.js @@ -0,0 +1,49 @@ +const helmet = require('helmet'); +const { WEBSOCKET_LIVE_URI } = require('../config'); +const { BASE_PATH, BASE_URL, STATIC_URL } = require('../url'); +const { URL } = require('url'); + +// websocketSrc represents the host where we can connect for websocket requests. +const websocketSrc = new URL(WEBSOCKET_LIVE_URI || BASE_URL).host; + +// staticSrc represents any static asset hosted on the static host. +const staticSrc = new URL(STATIC_URL).host; + +// nonceSrc represents the nonce source that is used to indicate a safe resource +// to load. +const nonceSrc = (req, res) => `'nonce-${res.locals.nonce}'`; + +module.exports = helmet.contentSecurityPolicy({ + directives: { + reportUri: `${BASE_PATH}api/v1/csp`, // report all policy violations to our reporting uri + defaultSrc: ["'none'"], // by default, do not allow anything at all + scriptSrc: [ + "'self'", + 'https://ajax.googleapis.com', // for jquery + staticSrc, // for any static files loaded from a cdn + nonceSrc, + ], + styleSrc: [ + "'self'", + 'https://maxcdn.bootstrapcdn.com', // for bootstrap css + 'https://fonts.googleapis.com', // for google fonts + 'https://code.getmdl.io', // for mdl css + staticSrc, // for any static files loaded from a cdn + nonceSrc, + ], + connectSrc: ["'self'", websocketSrc], + fontSrc: [ + "'self'", + 'https://maxcdn.bootstrapcdn.com', // for font-awesome + 'https://fonts.gstatic.com', // for google fonts + staticSrc, // for any static files loaded from a cdn + nonceSrc, + ], + imgSrc: [ + "'self'", + staticSrc, // for any static files loaded from a cdn + nonceSrc, + ], + }, + browserSniff: false, +}); diff --git a/middleware/nonce.js b/middleware/nonce.js new file mode 100644 index 000000000..1181fac85 --- /dev/null +++ b/middleware/nonce.js @@ -0,0 +1,9 @@ +const uuid = require('uuid/v4'); + +// nonce is designed to create a random value that can be used in conjunction +// with the csp middleware. +module.exports = (req, res, next) => { + res.locals.nonce = uuid(); + + next(); +}; diff --git a/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk b/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk index f8191d525..5080277f0 100644 --- a/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk +++ b/plugins/talk-plugin-notifications/server/views/unsubscribe-notifications.njk @@ -2,10 +2,17 @@ {% block title %}{{ t('talk-plugin-notifications.unsubscribe_page.unsubscribe') }}{% endblock %} +{% block css %} +{{ super() }} + +{% endblock %} + {% block html %}