-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
set CSP header in FastBoot #113
Changes from 1 commit
e5fe84a
1fe117f
42b1e83
b118a5d
8913cfa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { assert } from '@ember/debug'; | ||
|
||
// reads addon config stored in meta element | ||
function readAddonConfig(appInstance) { | ||
let config = appInstance.resolveRegistration('config:environment'); | ||
let addonConfig = config['ember-cli-content-security-policy']; | ||
|
||
// TODO: do not require policy to be stored in config object | ||
// if already available through CSP meta element | ||
assert( | ||
'Required configuration is available at run-time', | ||
addonConfig.hasOwnProperty('reportOnly') && addonConfig.hasOwnProperty('policy') | ||
); | ||
|
||
return config['ember-cli-content-security-policy']; | ||
} | ||
|
||
export function initialize(appInstance) { | ||
let fastboot = appInstance.lookup('service:fastboot'); | ||
rwjblue marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (!fastboot || !fastboot.get('isFastBoot')) { | ||
// nothing to do if application does not run in FastBoot or | ||
// does not even have a FastBoot service | ||
return; | ||
} | ||
|
||
let { policy, reportOnly } = readAddonConfig(appInstance); | ||
let header = reportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy'; | ||
fastboot.get('response.headers').set(header, policy); | ||
} | ||
|
||
export default { | ||
initialize | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default, initialize } from 'ember-cli-content-security-policy/instance-initializers/content-security-policy'; | ||
rwjblue marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
const expect = require('chai').expect; | ||
const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; | ||
const fs = require('fs-extra'); | ||
const denodeify = require('denodeify'); | ||
const request = denodeify(require('request')); | ||
const { | ||
removeConfig, | ||
setConfig | ||
} = require('../utils'); | ||
|
||
describe('e2e: fastboot integration', function() { | ||
this.timeout(300000); | ||
|
||
let app; | ||
let serverProcess; | ||
let serverPromise; | ||
|
||
// code to start and stop fastboot app server is highly inspired by ember-cli-addon-tests | ||
// https://github.com/tomdale/ember-cli-addon-tests/blob/master/lib/commands/start-server.js | ||
function startServer() { | ||
return new Promise((resolve, reject) => { | ||
serverPromise = app.run('node', 'server.js', { | ||
onOutput(output, child) { | ||
// detect start of fastboot app server | ||
if (output.includes('HTTP server started')) { | ||
serverProcess = child; | ||
resolve(); | ||
} | ||
}, | ||
}).catch(reject); | ||
}); | ||
} | ||
|
||
before(async function() { | ||
app = new AddonTestApp(); | ||
|
||
await app.create('default', { | ||
noFixtures: true, | ||
skipNpm: true, | ||
}); | ||
|
||
await app.editPackageJSON(pkg => { | ||
pkg.devDependencies['ember-cli-fastboot'] = "*"; | ||
pkg.devDependencies['fastboot-app-server'] = "*"; | ||
}); | ||
|
||
await app.run('npm', 'install'); | ||
|
||
// Quick Start instructions of FastBoot App Server | ||
// https://github.com/ember-fastboot/fastboot-app-server | ||
await fs.writeFile(app.filePath('server.js'), | ||
` | ||
const FastBootAppServer = require('fastboot-app-server'); | ||
|
||
let server = new FastBootAppServer({ | ||
distPath: 'dist', | ||
port: 49742, | ||
}); | ||
|
||
server.start(); | ||
` | ||
); | ||
}); | ||
|
||
afterEach(async function() { | ||
// stop fastboot app server | ||
if (process.platform === 'win32') { | ||
serverProcess.send({ kill: true }); | ||
} else { | ||
serverProcess.kill('SIGINT'); | ||
} | ||
|
||
// wait until sever terminated | ||
await serverPromise; | ||
|
||
await removeConfig(app); | ||
}); | ||
|
||
it('sets CSP header if served via FastBoot', async function() { | ||
await app.runEmberCommand('build'); | ||
await startServer(); | ||
|
||
let response = await request({ | ||
url: 'http://localhost:49742', | ||
headers: { | ||
'Accept': 'text/html' | ||
}, | ||
}); | ||
|
||
expect(response.headers).to.include.key('content-security-policy-report-only'); | ||
}); | ||
|
||
it('does not set CSP header if disabled', async function() { | ||
await setConfig(app, { enabled: false }); | ||
await app.runEmberCommand('build'); | ||
await startServer(); | ||
|
||
let response = await request({ | ||
url: 'http://localhost:49742', | ||
headers: { | ||
'Accept': 'text/html' | ||
}, | ||
}); | ||
|
||
expect(response.headers).to.not.include.key('content-security-policy-report-only'); | ||
}); | ||
|
||
it('does not set CSP header if delivery does not include header', async function() { | ||
await setConfig(app, { delivery: ['meta'] }); | ||
await app.runEmberCommand('build'); | ||
await startServer(); | ||
|
||
let response = await request({ | ||
url: 'http://localhost:49742', | ||
headers: { | ||
'Accept': 'text/html' | ||
}, | ||
}); | ||
|
||
expect(response.headers).to.not.include.key('content-security-policy-report-only'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,11 +19,12 @@ | |
"start": "ember serve", | ||
"test": "ember test", | ||
"test:all": "ember try:each", | ||
"test:node": "mocha node-tests/**" | ||
"test:node": "for i in node-tests/*/*; do mocha $i; done" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There seems to be strange issues with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reported this upstream: tomdale/ember-cli-addon-tests#215 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eeck, seems fine but that sounds like it was pretty gnarly to track down. |
||
}, | ||
"dependencies": { | ||
"body-parser": "^1.17.0", | ||
"chalk": "^2.0.0", | ||
"ember-cli-babel": "^7.1.2", | ||
"ember-cli-version-checker": "^3.1.3" | ||
}, | ||
"devDependencies": { | ||
|
@@ -33,9 +34,9 @@ | |
"denodeify": "^1.2.1", | ||
"ember-cli": "~3.7.1", | ||
"ember-cli-addon-tests": "^0.11.1", | ||
"ember-cli-babel": "^7.1.2", | ||
"ember-cli-dependency-checker": "^3.0.0", | ||
"ember-cli-eslint": "^4.2.3", | ||
"ember-cli-fastboot": "^2.2.1", | ||
"ember-cli-htmlbars": "^3.0.0", | ||
"ember-cli-htmlbars-inline-precompile": "^1.0.3", | ||
"ember-cli-inject-live-reload": "^1.8.2", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this read the config from the "legacy" location (in
config/environment.js
)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right that it reads the run-time configuration, which is normally provided by consumer via
config/environment.js
. But in this case the configuration is meant to be provided by addon'sconfig
hook only. It's a subset of addons configuration. It only includesreportOnly
option and the build policy string. Both are required at run-time for FastBoot support.