From 4df1a917af7ef14f0894b6cd568e4eebcdbe34b2 Mon Sep 17 00:00:00 2001 From: Timothy Farrell Date: Sat, 21 Jul 2018 21:44:03 -0500 Subject: [PATCH] Standardize on security headers to keep better track of what we need. Also move the server to it's own directory for better organization. --- packages/gallery/server/headers.js | 36 +++++++++++++++++++ .../{src/server.js => server/index.js} | 11 ++++-- packages/gallery/webpack.config.js | 8 ++--- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 packages/gallery/server/headers.js rename packages/gallery/{src/server.js => server/index.js} (78%) diff --git a/packages/gallery/server/headers.js b/packages/gallery/server/headers.js new file mode 100644 index 0000000..42f6a36 --- /dev/null +++ b/packages/gallery/server/headers.js @@ -0,0 +1,36 @@ +const STANDARD_HEADERS = { + 'Service-Worker-Allowed': '/', // Allow a service worker to intercept requests + 'Content-Security-Policy': { + 'default-src': "'self'", // FF has a bug with SVGs: https://bugzilla.mozilla.org/show_bug.cgi?id=1262842 + 'script-src': "'self'", // TODO: Use "strict-dynamic for production" + 'media-src': "'self'", + 'object-src': "'self'", + 'img-src': "'self' blob:", + 'connect-src': '*', + 'style-src': "'self' 'unsafe-inline'", + 'worker-src': "'self'", + 'frame-ancestors': "'none'" // No other sight may include this in a frame + }, + 'X-Content-Type-Options': 'nosniff', // http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx + 'X-Frame-Options': 'DENY', // No other sight may include this in a frame + 'X-XSS-Protection': '1; mode=block', + 'Referrer-Policy': 'same-origin' // Don't send a referrer except back to this server + // 'Strict-Transport-Security': 'max-age=63,13904; includeSubDomains; preload', +}; + +function formatHeaders(headers = STANDARD_HEADERS) { + const _headers = Object.assign({}, headers); + _headers['Content-Security-Policy'] = Object.entries(_headers['Content-Security-Policy']) + .map(e => e.join(' ')) + .join('; '); + return _headers; +} + +function expressSetHeaders(res, path, stat) { + res.set(formatHeaders()); +} + +module.exports = { + formatHeaders, + expressSetHeaders +}; diff --git a/packages/gallery/src/server.js b/packages/gallery/server/index.js similarity index 78% rename from packages/gallery/src/server.js rename to packages/gallery/server/index.js index e9129cc..88f2303 100644 --- a/packages/gallery/src/server.js +++ b/packages/gallery/server/index.js @@ -3,6 +3,7 @@ const express = require('express'); const request = require('request'); const bodyParser = require('body-parser'); +const { expressSetHeaders } = require('./headers.js'); // Constants const B2_BASE_URL = 'https://api.backblazeb2.com/b2api/v1/'; @@ -12,10 +13,11 @@ const app = express(); app.use(bodyParser.text()); app.use( - express.static('.', { + express.static('./dist/', { dotfiles: 'ignore', etag: false, - index: ['src/index.html'] + index: ['index.html'], + setHeaders: expressSetHeaders }) ); @@ -52,3 +54,8 @@ app.post('/api/v1/remove_file', POSTRedirect('/b2api/v1/b2_delete_file_version') module.exports = { app: app }; + +if (require.main === module) { + // While this can work in Chrome, it is not ready for prime-time without a security certificate. + app.listen(8090, '127.0.0.1'); +} diff --git a/packages/gallery/webpack.config.js b/packages/gallery/webpack.config.js index d7d75c4..d9af3f1 100644 --- a/packages/gallery/webpack.config.js +++ b/packages/gallery/webpack.config.js @@ -2,7 +2,8 @@ const path = require('path'); const webpack = require('webpack'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const server = require('./src/server.js'); +const server = require('./server/index.js'); +const { formatHeaders } = require('./server/headers.js'); const API_PORT = 8888; const API_HOST = '127.0.0.1'; @@ -27,10 +28,7 @@ module.exports = { contentBase: path.join(__dirname, 'dist'), host: '0.0.0.0', https: true, - headers: { - 'Service-Worker-Allowed': '/', - 'Access-Control-Allow-Origin': '*' - }, + headers: formatHeaders(), proxy: { '/api': `http://${API_HOST}:${API_PORT}` }