From cb6d5b4ca6c6f9af597368fec56cfd3fb6db9cbf Mon Sep 17 00:00:00 2001 From: Gus Naughton Date: Tue, 19 Apr 2022 17:13:50 -0700 Subject: [PATCH] feature: introduces CSP support --- packages/sirv-cli/bin.js | 4 +++- packages/sirv-cli/index.js | 6 ++++++ packages/sirv-cli/readme.md | 3 ++- tests/sirv-cli.js | 23 ++++++++++++++++++++++- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/packages/sirv-cli/bin.js b/packages/sirv-cli/bin.js index 9a7d054..fb165ab 100644 --- a/packages/sirv-cli/bin.js +++ b/packages/sirv-cli/bin.js @@ -6,7 +6,7 @@ const pkg = require('./package'); sade('sirv [dir]') .version(pkg.version) .describe('Run a static file server') - .example('build --cors --port 8888') + .example('build --cors --csp "default-src example.com" --port 8888') .example('public --quiet --etag --maxage 31536000 --immutable') .example('public --http2 --key priv.pem --cert cert.pem') .example('public -qeim 31536000') @@ -15,6 +15,7 @@ sade('sirv [dir]') .option('-D, --dev', 'Enable "dev" mode') .option('-e, --etag', 'Enable "ETag" header') .option('-d, --dotfiles', 'Enable dotfile asset requests') + .option('-C, --csp', 'Set Content-Security-Policy headers') .option('-c, --cors', 'Enable "CORS" headers to allow any origin requestor') .option('-G, --gzip', 'Send precompiled "*.gz" files when "gzip" is supported', true) .option('-B, --brotli', 'Send precompiled "*.br" files when "brotli" is supported', true) @@ -39,6 +40,7 @@ sade('sirv [dir]') immutable: false, http2: false, cors: false, + csp: false, logs: true, } }); diff --git a/packages/sirv-cli/index.js b/packages/sirv-cli/index.js index 7810262..2497ae4 100644 --- a/packages/sirv-cli/index.js +++ b/packages/sirv-cli/index.js @@ -41,6 +41,12 @@ module.exports = function (dir, opts) { } } + if (opts.csp) { + opts.setHeaders = res => { + res.setHeader('Content-Security-Policy', opts.csp) + } + } + let server; let fn = sirv(dir, opts); let { hrtime, stdout } = process; diff --git a/packages/sirv-cli/readme.md b/packages/sirv-cli/readme.md index 5ac82e6..be025d4 100644 --- a/packages/sirv-cli/readme.md +++ b/packages/sirv-cli/readme.md @@ -48,6 +48,7 @@ $ sirv --help -D, --dev Enable "dev" mode -e, --etag Enable "ETag" header -d, --dotfiles Enable dotfile asset requests + -C, --csp Enable Content-Security-Policy headers -c, --cors Enable "CORS" headers to allow any origin requestor -G, --gzip Send precompiled "*.gz" files when "gzip" is supported (default true) -B, --brotli Send precompiled "*.br" files when "brotli" is supported (default true) @@ -66,7 +67,7 @@ $ sirv --help -h, --help Displays this message Examples - $ sirv build --cors --port 8888 + $ sirv build --cors --csp "default-src example.com" --port 8888 $ sirv public --quiet --etag --maxage 31536000 --immutable $ sirv public --http2 --key priv.pem --cert cert.pem $ sirv public -qeim 31536000 diff --git a/tests/sirv-cli.js b/tests/sirv-cli.js index d731218..d455076 100644 --- a/tests/sirv-cli.js +++ b/tests/sirv-cli.js @@ -22,6 +22,7 @@ help('--help', () => { -D, --dev Enable "dev" mode -e, --etag Enable "ETag" header -d, --dotfiles Enable dotfile asset requests + -C, --csp Enable Content-Security-Policy headers -c, --cors Enable "CORS" headers to allow any origin requestor -G, --gzip Send precompiled "*.gz" files when "gzip" is supported (default true) -B, --brotli Send precompiled "*.br" files when "brotli" is supported (default true) @@ -40,7 +41,7 @@ help('--help', () => { -h, --help Displays this message Examples - $ sirv build --cors --port 8888 + $ sirv build --cors --csp "default-src example.com" --port 8888 $ sirv public --quiet --etag --maxage 31536000 --immutable $ sirv public --http2 --key priv.pem --cert cert.pem $ sirv public -qeim 31536000 @@ -71,6 +72,26 @@ basic.run(); // --- +const csp = suite('csp'); + +csp('should attach CSP headers to response', async () => { + let server = await utils.spawn('--csp "default-src example.com"'); + + try { + let res = await server.send('GET', '/blog'); + await utils.matches(res, 200, 'blog.html', 'utf8'); + assert.is(res.headers['Content-Security-Policy'], 'default-src example.com'); + } finally { + await server.close(); + } +}); + +csp.run(); + +// --- + +// --- + const cors = suite('cors'); cors('should attach CORS headers to response', async () => {