express-csp-header
Advanced tools
Comparing version 1.2.1 to 2.0.0
64
index.js
@@ -7,64 +7,32 @@ var cspHeader = require('csp-header'); | ||
function expressCsp(params){ | ||
var policies, | ||
extend, | ||
reportUri, | ||
reportOnly; | ||
function expressCsp(params) { | ||
params = params || {}; | ||
policies = params.policies; | ||
extend = params.extend; | ||
reportUri = params.reportUri; | ||
reportOnly = Boolean(params.reportOnly); | ||
if(typeof extend === 'object'){ | ||
policies = extendPolicies(policies, extend); | ||
} | ||
return function (req, res, next) { | ||
const cspHeaderParams = { | ||
policies: params.policies, | ||
extend: params.extend, | ||
presets: params.presets, | ||
'report-uri': (typeof params.reportUri === 'function' ? params.reportUri(req, res) : params.reportUri) | ||
}; | ||
let cspString = cspHeader(cspHeaderParams); | ||
return function(req, res, next){ | ||
var cspString = cspHeader({ | ||
policies: policies, | ||
'report-uri': (typeof reportUri === 'function' ? reportUri(req, res) : reportUri) | ||
}); | ||
if(cspString){ | ||
if(cspString.indexOf(expressCsp.NONCE) > -1){ | ||
if (cspString) { | ||
if (cspString.indexOf(expressCsp.NONCE) > -1) { | ||
req.nonce = crypto.randomBytes(16).toString('base64'); | ||
cspString = cspString.replace(new RegExp(expressCsp.NONCE, 'g'), cspHeader.nonce(req.nonce)); | ||
} | ||
if(cspString.indexOf(expressCsp.TLD) > -1){ | ||
if (cspString.indexOf(expressCsp.TLD) > -1) { | ||
var domain = parseDomain(req.hostname || req.host); | ||
var tld = domain && domain.tld; | ||
if(tld){ | ||
if (tld) { | ||
cspString = cspString.replace(new RegExp(expressCsp.TLD, 'g'), tld); | ||
} | ||
} | ||
res.set(CSP_HEADER_NAME + (reportOnly ? CSP_REPORT_ONLY : ''), cspString); | ||
res.set(CSP_HEADER_NAME + (params.reportOnly ? CSP_REPORT_ONLY : ''), cspString); | ||
next(); | ||
} | ||
} | ||
}; | ||
} | ||
function extendPolicies(original, extension){ | ||
var extended = Object.assign(original); | ||
Object.keys(extension).forEach(function(policyName){ | ||
var extPolicy = extension[policyName], | ||
origPolicy = original[policyName]; | ||
if(!(extPolicy instanceof Array)){ | ||
throw new Error(''); | ||
} | ||
if(typeof origPolicy !== 'undefined'){ | ||
extPolicy.forEach(function(rule){ | ||
if(typeof rule === 'string' && origPolicy.indexOf(rule) === -1){ | ||
extended[policyName].push(rule); | ||
} | ||
}); | ||
}else{ | ||
extended[policyName] = extPolicy; | ||
} | ||
}); | ||
return extended; | ||
} | ||
expressCsp.SELF = cspHeader.SELF; | ||
@@ -77,2 +45,2 @@ expressCsp.INLINE = cspHeader.INLINE; | ||
module.exports = expressCsp; | ||
module.exports = expressCsp; |
{ | ||
"name": "express-csp-header", | ||
"version": "1.2.1", | ||
"version": "2.0.0", | ||
"description": "Content-Security-Policy middleware for Express", | ||
@@ -10,3 +10,3 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "mocha test --check-leaks" | ||
"test": "ava" | ||
}, | ||
@@ -24,2 +24,5 @@ "repository": { | ||
"license": "WTFPL", | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"bugs": { | ||
@@ -30,9 +33,8 @@ "url": "https://github.com/frux/express-csp/issues" | ||
"devDependencies": { | ||
"mocha": "^2.4.5", | ||
"should": "^8.3.0" | ||
"ava": "^0.18.2" | ||
}, | ||
"dependencies": { | ||
"csp-header": "^0.0.4", | ||
"csp-header": "^1.0.0", | ||
"parse-domain": "^0.2.1" | ||
} | ||
} |
@@ -98,3 +98,3 @@ # Content-Security-Policy middleware for Express | ||
})); | ||
// express will send header with a random nonce key "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send;" | ||
// express will send header "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send;" | ||
``` | ||
@@ -113,3 +113,3 @@ | ||
})); | ||
// express will send header with a random nonce key "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send?time=1460467355592;" | ||
// express will send header "Content-Security-Policy: script-src 'self'; report-uri https://cspreport.com/send?time=1460467355592;" | ||
``` | ||
@@ -119,5 +119,8 @@ | ||
#### v1.1.1 | ||
#### v1.2.1 | ||
* fix leaking to global scope by [@i-akhmadullin](https://github.com/i-akhmadullin) | ||
#### v1.2.0 | ||
* new csp-header with CSP 3 | ||
#### v1.1.0 | ||
@@ -124,0 +127,0 @@ * Policies extending |
@@ -1,140 +0,106 @@ | ||
var should = require('should'), | ||
expressCsp = require('../'), | ||
mockApp = { | ||
use: function(middleware, req, res){ | ||
var req = req || {}, | ||
res = res || { | ||
headers: {}, | ||
set: function(headerName, headerVal){ | ||
this.headers[headerName] = headerVal; | ||
} | ||
}; | ||
middleware(req, res, function(){}); | ||
return {req: req, res: res}; | ||
} | ||
}; | ||
import test from 'ava'; | ||
import expressCsp from '../'; | ||
describe('General', function(){ | ||
it('should sets CSP header', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'default-src': [ expressCsp.SELF ], | ||
'script-src': [ expressCsp.SELF, expressCsp.INLINE, 'somehost.com' ], | ||
'style-src': [ expressCsp.SELF, 'mystyles.net' ], | ||
'img-src': [ 'data:', 'images.com' ], | ||
'worker-src': [ expressCsp.NONE ], | ||
'block-all-mixed-content': true | ||
const mockApp = { | ||
use(middleware, req, res) { | ||
req = req || {}; | ||
res = res || { | ||
headers: {}, | ||
set(headerName, headerVal) { | ||
this.headers[headerName] = headerVal; | ||
} | ||
})); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal("default-src 'self'; script-src 'self' 'unsafe-inline' somehost.com; style-src 'self' mystyles.net; img-src data: images.com; worker-src 'none'; block-all-mixed-content;"); | ||
}); | ||
}; | ||
middleware(req, res, () => {}); | ||
return {req, res}; | ||
} | ||
}; | ||
it('should generates nonce key', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ expressCsp.NONCE ] | ||
} | ||
})); | ||
test('should sets CSP header', t => { | ||
const actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'default-src': [expressCsp.SELF], | ||
'script-src': [expressCsp.SELF, expressCsp.INLINE, 'somehost.com'], | ||
'style-src': [expressCsp.SELF, 'mystyles.net'], | ||
'img-src': ['data:', 'images.com'], | ||
'worker-src': [expressCsp.NONE], | ||
'block-all-mixed-content': true | ||
} | ||
})); | ||
const expected = "default-src 'self'; script-src 'self' 'unsafe-inline' somehost.com; style-src 'self' mystyles.net; img-src data: images.com; worker-src 'none'; block-all-mixed-content;"; | ||
/^script\-src \'nonce\-.+\'\;/.test(actual.res.headers['Content-Security-Policy']).should.be.ok(); | ||
actual.req.nonce.should.be.type('string'); | ||
}); | ||
t.is(actual.res.headers['Content-Security-Policy'], expected); | ||
}); | ||
it('should adds report-uri param as string', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { 'script-src': [ expressCsp.SELF ] }, | ||
reportUri: 'https://cspreport.com' | ||
})); | ||
test('Nonce', t => { | ||
const actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [expressCsp.NONCE] | ||
} | ||
})); | ||
/report\-uri https\:\/\/cspreport\.com\;$/.test(actual.res.headers['Content-Security-Policy']).should.be.ok(); | ||
}); | ||
t.true(/^script-src 'nonce-.+';/.test(actual.res.headers['Content-Security-Policy'])); | ||
t.is(typeof actual.req.nonce, 'string'); | ||
}); | ||
it('should adds report-uri param as function', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ expressCsp.SELF ] | ||
}, | ||
reportUri: function(req, res){ | ||
return 'https://cspreport.com/send?time=' + Number(new Date()); | ||
} | ||
})); | ||
test('report-uri | string', t => { | ||
const actual = mockApp.use(expressCsp({ | ||
policies: {'script-src': [expressCsp.SELF]}, | ||
reportUri: 'https://cspreport.com' | ||
})); | ||
/report\-uri https\:\/\/cspreport\.com\/send\?time\=[0-9]+\;$/.test(actual.res.headers['Content-Security-Policy']).should.be.ok(); | ||
}); | ||
t.is(actual.res.headers['Content-Security-Policy'], "script-src 'self'; report-uri https://cspreport.com;"); | ||
}); | ||
it('should replace tld', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
test('report-uri | function', t => { | ||
const actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [expressCsp.SELF] | ||
}, | ||
reportUri() { | ||
return 'https://cspreport.com/send?time=' + Number(new Date()); | ||
} | ||
})); | ||
t.true(/^script-src\s'self';\sreport-uri\shttps:\/\/cspreport\.com\/send\?time=[0-9]+;$/.test(actual.res.headers['Content-Security-Policy'])); | ||
}); | ||
test('tld', t => { | ||
const actual = mockApp.use( | ||
expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.' + expressCsp.TLD ] | ||
'script-src': ['myhost.' + expressCsp.TLD] | ||
} | ||
}), { | ||
}), | ||
{ | ||
hostname: 'example.com' | ||
}); | ||
} | ||
); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal('script-src myhost.com;'); | ||
}); | ||
t.is(actual.res.headers['Content-Security-Policy'], 'script-src myhost.com;'); | ||
}); | ||
it('shouldn\'t replace tld if tld is not defined', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
test('tld | req.tld is undefined', t => { | ||
const actual = mockApp.use( | ||
expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.' + expressCsp.TLD ] | ||
'script-src': ['myhost.' + expressCsp.TLD] | ||
} | ||
}), { | ||
}), | ||
{ | ||
hostname: 'localhost' | ||
}); | ||
} | ||
); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal('script-src myhost.%tld%;'); | ||
}); | ||
t.is(actual.res.headers['Content-Security-Policy'], 'script-src myhost.%tld%;'); | ||
}); | ||
it('should supports Report-Only mode', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.com' ] | ||
}, | ||
reportOnly: true | ||
})); | ||
test('Report-Only', t => { | ||
const actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': ['myhost.com'] | ||
}, | ||
reportOnly: true | ||
})); | ||
actual.res.headers['Content-Security-Policy-Report-Only'].should.be.equal('script-src myhost.com;'); | ||
}); | ||
t.is(actual.res.headers['Content-Security-Policy-Report-Only'], 'script-src myhost.com;'); | ||
}); | ||
describe('Extending', function(){ | ||
it('should extend existing policy', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.com' ] | ||
}, | ||
extend: { | ||
'script-src': [ 'additional.host.com' ] | ||
} | ||
})); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal('script-src myhost.com additional.host.com;'); | ||
}); | ||
it('shouldn\'t duplicate rule', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.com' ] | ||
}, | ||
extend: { | ||
'script-src': [ 'myhost.com' ] | ||
} | ||
})); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal('script-src myhost.com;'); | ||
}); | ||
it('should add new policy', function(){ | ||
var actual = mockApp.use(expressCsp({ | ||
policies: { | ||
'script-src': [ 'myhost.com' ] | ||
}, | ||
extend: { | ||
'style-src': [ 'newhost.com' ] | ||
} | ||
})); | ||
actual.res.headers['Content-Security-Policy'].should.be.equal('script-src myhost.com; style-src newhost.com;'); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
100192
1
8
133
1
+ Addedcsp-header@1.3.3(transitive)
- Removedcsp-header@0.0.4(transitive)
Updatedcsp-header@^1.0.0