This repository has been archived by the owner on Dec 20, 2019. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7591c4c
Showing
7 changed files
with
575 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
coverage | ||
.git | ||
npm-debug.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
node_modules | ||
coverage | ||
test | ||
examples | ||
npm-debug.log | ||
.editorconfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "node-csp", | ||
"version": "1.0.0", | ||
"description": "I/O module to add csp headers to http response", | ||
"main": "index.js", | ||
"directories": {}, | ||
"scripts": { | ||
"test": "_mocha test/ --bail", | ||
"coverage": "istanbul cover _mocha test/" | ||
}, | ||
"keywords": [ | ||
"csp" | ||
], | ||
"author": "amanvirk", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"chai": "^3.4.1", | ||
"co-mocha": "^1.1.2", | ||
"coveralls": "^2.11.6", | ||
"istanbul": "^0.4.1", | ||
"mocha": "^2.3.4", | ||
"mocha-lcov-reporter": "^1.0.0", | ||
"standard": "^5.4.1", | ||
"supertest": "^1.1.0" | ||
}, | ||
"dependencies": { | ||
"platform": "^1.3.1" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
'use strict' | ||
|
||
/** | ||
* node-scp | ||
* Copyright(c) 2016-2016 Harminder Virk | ||
* MIT Licensed | ||
*/ | ||
|
||
let headers = exports = module.exports = {} | ||
|
||
/** | ||
* @description list of csp headers used by different browsers | ||
* @type {Array} | ||
*/ | ||
const cspHeaders = [ | ||
'Content-Security-Policy', | ||
'X-Content-Security-Policy', | ||
'X-WebKit-CSP' | ||
] | ||
|
||
/** | ||
* @description returns headers required by IE | ||
* @method IE | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.IE = function (browser) { | ||
const version = parseFloat(browser.version) | ||
if(version < 12 & version > 9) { | ||
return ['X-Content-Security-Policy'] | ||
} else if(version > 12) { | ||
return ['Content-Security-Policy'] | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
/** | ||
* @description returns headers required by FireFox | ||
* @method FireFox | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.Firefox = function (browser) { | ||
const version = parseFloat(browser.version) | ||
if (version >= 23) { | ||
return ['Content-Security-Policy'] | ||
} else if (version >=4 && version < 23) { | ||
return ['X-Content-Security-Policy'] | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
/** | ||
* @description returns headers required by FireFox | ||
* @method Chrome | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.Chrome = function (browser) { | ||
const version = parseFloat(browser.version) | ||
if (version >= 14 && version < 25) { | ||
return ['X-WebKit-CSP'] | ||
} else if (version >= 25) { | ||
return ['Content-Security-Policy'] | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
/** | ||
* @description returns headers required by FireFox | ||
* @method Safari | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.Safari = function (browser) { | ||
const version = parseFloat(browser.version) | ||
if (version >= 7) { | ||
return ['Content-Security-Policy'] | ||
} else if (version >= 6) { | ||
return ['X-WebKit-CSP'] | ||
} else { | ||
return [] | ||
} | ||
} | ||
|
||
/** | ||
* @description returns headers required by FireFox | ||
* @method Opera | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.Opera = function (browser) { | ||
const version = parseFloat(browser.version) | ||
return version >= 15 ? ['Content-Security-Policy'] : [] | ||
} | ||
|
||
/** | ||
* @descrption returns headers required by Android Browser | ||
* @method Android Browser | ||
* @param {Object} browser | ||
* @param {Object} options | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers['Android Browser'] = function (browser, options) { | ||
const version = parseFloat(browser.os.version) | ||
return (version < 4.4 || options.disableAndroid) ? [] : ['Content-Security-Policy'] | ||
} | ||
|
||
/** | ||
* @description returns headers required by Chrome Mobile | ||
* @method Chrome Mobile | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers['Chrome Mobile'] = function (browser) { | ||
return browser.os.family === 'iOS' ? ['Content-Security-Policy'] : headers['Android Browser'].apply(this, arguments) | ||
} | ||
|
||
/** | ||
* @description returns headers required by IE Mobile | ||
* @method IE Mobile | ||
* @param {Object} browser | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers['IE Mobile'] = headers.IE | ||
|
||
/** | ||
* @description returns all csp headers | ||
* @method getAllHeaders | ||
* @return {Array} | ||
* @public | ||
*/ | ||
headers.getAllHeaders = function () { | ||
return cspHeaders | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
'use strict' | ||
|
||
/** | ||
* node-csp | ||
* Copyright(c) 2016-2016 Harminder Virk | ||
* http://www.kayako.com/license | ||
*/ | ||
|
||
const platform = require('platform') | ||
const headers = require('./headers') | ||
|
||
let Csp = exports = module.exports = {} | ||
|
||
/** | ||
* Add quotes to below keywords | ||
* @example | ||
* self becomes 'self' | ||
* @type {RegExp} | ||
*/ | ||
const keywords = /(none|self|unsafe-inline|unsafe-eval)/g | ||
|
||
/** | ||
* @description list of allowed directives by all browsers | ||
* @type {Array} | ||
*/ | ||
const directivesList = [ | ||
'base-uri', | ||
'child-src', | ||
'connect-src', | ||
'default-src', | ||
'font-src', | ||
'form-action', | ||
'frame-ancestors', | ||
'frame-src', | ||
'img-src', | ||
'media-src', | ||
'object-src', | ||
'plugin-types', | ||
'report-uri', | ||
'style-src', | ||
'script-src', | ||
'upgrade-insecure-requests' | ||
] | ||
|
||
/** | ||
* @description adds csp policy to http response | ||
* @method add | ||
* @param {Object} request | ||
* @param {Object} response | ||
* @param {Ojbect} options | ||
* @public | ||
*/ | ||
Csp.add = function (request, response, options) { | ||
const cspHeaders = Csp.build(request, options) | ||
const headerKeys = Object.keys(cspHeaders) | ||
if(headerKeys.length) { | ||
headerKeys.forEach(function (key) { | ||
response.setHeader(key, cspHeaders[key]) | ||
}) | ||
} | ||
} | ||
|
||
/** | ||
* @description builds the final object to be used | ||
* by response for adding csp header | ||
* @method build | ||
* @param {Object} request | ||
* @param {Object} options | ||
* @return {Object} | ||
* @public | ||
*/ | ||
Csp.build = function (request, options) { | ||
const userAgent = request.headers['user-agent'] | ||
let cspHeaders = headers.getAllHeaders() | ||
|
||
if(userAgent && !options.setAllHeaders) { | ||
const browser = platform.parse(userAgent) || {} | ||
cspHeaders = typeof(headers[browser.name]) === 'function' ? headers[browser.name](browser, options) : headers.getAllHeaders() | ||
} | ||
|
||
if(!cspHeaders.length) { | ||
return {} | ||
} | ||
|
||
const cspString = Csp._quoteKeywords(Csp._formatDirectives(options.directives)) | ||
|
||
if(cspString.trim().length <= 0) { | ||
return {} | ||
} | ||
|
||
let cspBuildHeaders = {} | ||
cspHeaders.forEach(function (headerKey) { | ||
if(options.reportOnly) { | ||
headerKey += '-Report-Only' | ||
} | ||
cspBuildHeaders[headerKey] = cspString | ||
}) | ||
|
||
return cspBuildHeaders | ||
} | ||
|
||
/** | ||
* @description format directives objects to a string | ||
* to be consumed by web browsers. | ||
* @method _formatDirectives | ||
* @param {Object} directives | ||
* @return {String} | ||
* @public | ||
*/ | ||
Csp._formatDirectives = function (directives) { | ||
const directiveNames = Object.keys(directives) | ||
let cspString = '' | ||
directiveNames.forEach(function (name) { | ||
const directive = directives[name] | ||
if(directivesList.indexOf(name) <= -1) { | ||
throw new Error(`invalid directive: ${name}`) | ||
} | ||
cspString += `${name} ${directive.join(' ')}; ` | ||
}) | ||
return cspString | ||
} | ||
|
||
/** | ||
* @description add quotes to special keywords using | ||
* regex. | ||
* @method _quoteKeywords | ||
* @param {String} cspString | ||
* @return {String} | ||
* @public | ||
*/ | ||
Csp._quoteKeywords = function (cspString) { | ||
return cspString.replace(keywords, function (index, group) { | ||
return `'${group}'` | ||
}) | ||
} |
Oops, something went wrong.