UNPKG

helmet-csp-uc

Version:

Content Security Policy middleware.

388 lines (351 loc) 13.9 kB
var csp = require(".."); var _ = require("lodash"); var connect = require("connect"); var request = require("supertest"); var assert = require("assert"); function helloWorld(req, res) { res.end("Hello world!"); } describe("csp middleware", function () { var POLICY = { "default-src": ["'self'", "default.com"], "script-src": ["scripts.com"], "style-src": ["style.com"], "img-src": ["img.com"], "connect-src": ["connect.com"], "font-src": ["font.com"], "object-src": ["object.com"], "media-src": ["media.com"], "frame-src": ["frame.com"], "sandbox": ["allow-forms", "allow-scripts"], "report-uri": "/report-violation" }; var CAMELCASE_POLICY = { defaultSrc: ["'self'", "default.com"], scriptSrc: ["scripts.com"], styleSrc: ["style.com"], imgSrc: ["img.com"], connectSrc: ["connect.com"], fontSrc: ["font.com"], objectSrc: ["object.com"], mediaSrc: ["media.com"], frameSrc: ["frame.com"], sandbox: ["allow-forms", "allow-scripts"], reportUri: "/report-violation" }; var AGENTS = { "Firefox 22": { string: "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/22.0", special: true }, "Firefox 23": { string: "Mozilla/5.0 (Windows NT 6.2; rv:22.0) Gecko/20130405 Firefox/23.0", header: "Content-Security-Policy" }, "Chrome 24": { string: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.60 Safari/537.17", header: "X-WebKit-CSP" }, "Chrome 27": { string: "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.93 Safari/537.36", header: "Content-Security-Policy" }, "Opera 15": { string: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36 OPR/15.0.1387.64", header: "Content-Security-Policy" }, "Opera 21": { string: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.132 Safari/537.36 OPR/21.0.1432.67", header: "Content-Security-Policy" }, "Safari 5.1": { string: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/534.55.3 (KHTML, like Gecko) Version/5.1.3 Safari/534.53.10", special: true }, "Safari 6": { string: "Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1", header: "X-WebKit-CSP" }, "Safari 7": { string: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.74.9 (KHTML, like Gecko) Version/7.0.2 Safari/537.74.9", header: "Content-Security-Policy" }, "Internet Explorer 8": { string: "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; GTB7.4; InfoPath.2; SV1; .NET CLR 3.3.69573; WOW64; en-US)", special: true }, "Internet Explorer 9": { string: "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)", special: true }, "Internet Explorer 10": { string: "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)", header: "X-Content-Security-Policy", special: true }, "Internet Explorer 11": { string: "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko", header: "X-Content-Security-Policy", special: true }, "iOS Chrome 40": { string: "Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/40.0.2214.69 Mobile/12B440 Safari/600.1.4 (000920)", header: "Content-Security-Policy", special: true }, "Android 4.1.2": { string: "Mozilla/5.0 (Linux; U; Android 4.1.2; fr-fr; GT-P5110 Build/JZO54K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30", special: true }, "Android 4.4.3": { string: "Mozilla/5.0 (Linux; Android 4.4.3; HTC_One Build/KTU84L) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36", header: "Content-Security-Policy" }, "UC 10.4.1.567 Android 4.4.4": { string: "Mozilla/5.0 (Linux; U; Android 4.4.4; zh-CN; HM NOTE 1LTE Build/KTU84P) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/10.4.1.567 U3/0.8.0 Mobile Safari/534.30", header: "X-WebKit-CSP" } }; function use (policy) { var result = connect(); result.use(csp(policy)); result.use(helloWorld); return result; } it("sets headers by string", function (done) { var app = use({ "default-src": "a.com b.biz" }); request(app).get("/") .expect("Content-Security-Policy", /default-src a.com b.biz/, done); }); it("sets all the headers if you tell it to", function (done) { var app = use({ setAllHeaders: true, "default-src": ["'self'", "domain.com"] }); request(app).get("/").set("User-Agent", AGENTS["Firefox 23"].string) .expect("X-Content-Security-Policy", /default-src 'self' domain.com/) .expect("Content-Security-Policy", /default-src 'self' domain.com/) .expect("X-WebKit-CSP", /default-src 'self' domain.com/) .end(done); }); it("sets all the headers if you provide an unknown user-agent", function (done) { var app = use({ "default-src": ["'self'", "domain.com"] }); request(app).get("/").set("User-Agent", "Burrito Browser") .expect("X-Content-Security-Policy", /default-src 'self' domain.com/) .expect("Content-Security-Policy", /default-src 'self' domain.com/) .expect("X-WebKit-CSP", /default-src 'self' domain.com/) .end(function(err) { if (err) { return done(err); } // unknown browser doesn't affect the next request request(app).get("/").set("User-Agent", AGENTS["Chrome 27"].string) .expect("Content-Security-Policy", /default-src 'self' domain.com/) .expect(function(res) { assert(!res.get("X-Content-Security-Policy")); assert(!res.get("X-WebKit-CSP")); }) .end(done); }); }); it("sets the report-only headers", function (done) { var app = use({ reportOnly: true, setAllHeaders: true, "default-src": ["'self'"], "report-uri": "/reporter" }); request(app).get("/").set("User-Agent", AGENTS["Firefox 23"].string) .expect("X-Content-Security-Policy-Report-Only", /default-src 'self'/) .expect("Content-Security-Policy-Report-Only", /default-src 'self'/) .expect("X-WebKit-CSP-Report-Only", /default-src 'self'/) .end(done); }); it("throws an error when directives need quotes", function () { assert.throws(function() { csp({ "default-src": ["none"] }); }, Error); assert.throws(function() { csp({ "default-src": ["self"] }); }, Error); assert.throws(function() { csp({ "script-src": ["unsafe-inline"] }); }, Error); assert.throws(function() { csp({ "script-src": ["unsafe-eval"] }); }, Error); assert.throws(function() { csp({ "default-src": "self" }); }, Error); assert.throws(function() { csp({ "default-src": "self unsafe-inline" }); }, Error); }); it("throws an error reportOnly is true and there is no report-uri", function () { assert.throws(function() { csp({ reportOnly: true }); }, Error); }); _.each(AGENTS, function(agent, name) { if (agent.special) { return; } it("sets the header properly for " + name + " given dashed names", function (done) { var app = use(POLICY); var header = agent.header; request(app).get("/").set("User-Agent", agent.string) .expect(header, /default-src 'self' default.com/) .expect(header, /script-src scripts.com/) .expect(header, /img-src img.com/) .expect(header, /connect-src connect.com/) .expect(header, /font-src font.com/) .expect(header, /object-src object.com/) .expect(header, /media-src media.com/) .expect(header, /frame-src frame.com/) .expect(header, /sandbox allow-forms allow-scripts/) .expect(header, /report-uri \/report-violation/) .end(done); }); it("sets the header properly for " + name + " given camelCased names", function (done) { var app = use(CAMELCASE_POLICY); var header = agent.header; request(app).get("/").set("User-Agent", agent.string) .expect(header, /default-src 'self' default.com/) .expect(header, /script-src scripts.com/) .expect(header, /img-src img.com/) .expect(header, /connect-src connect.com/) .expect(header, /font-src font.com/) .expect(header, /object-src object.com/) .expect(header, /media-src media.com/) .expect(header, /frame-src frame.com/) .expect(header, /sandbox allow-forms allow-scripts/) .expect(header, /report-uri \/report-violation/) .end(done); }); }); it("sets the header properly for Firefox 22", function (done) { var policy = _.extend({ safari5: true }, POLICY); var app = use(policy); var header = "X-Content-Security-Policy"; request(app).get("/").set("User-Agent", AGENTS["Firefox 22"].string) .expect(header, /default-src 'self' default.com/) .expect(header, /script-src scripts.com/) .expect(header, /img-src img.com/) .expect(header, /xhr-src connect.com/) .expect(header, /font-src font.com/) .expect(header, /object-src object.com/) .expect(header, /media-src media.com/) .expect(header, /frame-src frame.com/) .expect(header, /sandbox allow-forms allow-scripts/) .expect(header, /report-uri \/report-violation/) .end(done); }); [ "Safari 5.1", "Internet Explorer 8", "Internet Explorer 9", "Android 4.1.2" ].forEach(function (browser) { it("doesn't set the property for " + browser + " by default", function (done) { var app = use(POLICY); request(app).get("/").set("User-Agent", AGENTS[browser].string) .end(function(err, res) { if (err) { return done(err); } assert.equal(res.header["x-webkit-csp"], undefined); assert.equal(res.header["content-security-policy"], undefined); assert.equal(res.header["x-content-security-policy"], undefined); done(); }); }); }); it("sets the header for Safari 5.1 if you force it", function (done) { var app = use({ safari5: true, "default-src": "a.com" }); request(app).get("/").set("User-Agent", AGENTS["Safari 5.1"].string) .expect("X-WebKit-CSP", "default-src a.com", done); }); it("lets you disable Android", function (done) { var app = use({ disableAndroid: true, "default-src": "a.com" }); request(app).get("/").set("User-Agent", AGENTS["Android 4.4.3"].string) .end(function(err, res) { if (err) { return done(err); } assert.equal(res.header["x-webkit-csp"], undefined); assert.equal(res.header["content-security-policy"], undefined); assert.equal(res.header["x-content-security-policy"], undefined); done(); }); }); [10, 11].forEach(function (version) { var ua = AGENTS["Internet Explorer " + version]; it("sets the header for IE " + version + " if sandbox is true", function (done) { var app = use({ sandbox: true }); request(app).get("/").set("User-Agent", ua.string) .expect(ua.header, "sandbox", done); }); it("sets the header for IE " + version + " if sandbox is an array", function (done) { var app = use({ sandbox: ["allow-forms", "allow-scripts"] }); request(app).get("/").set("User-Agent", ua.string) .expect(ua.header, /sandbox allow-forms allow-scripts/, done); }); it("doesn't set the header for IE " + version + " if sandbox isn't specified", function (done) { var app = use({ "default-src": ["'self'"] }); request(app).get("/").set("User-Agent", ua.string) .end(function (err, res) { if (err) { return done(err); } assert.equal(res.header[ua.header.toLowerCase()], undefined); done(); }); }); }); it("appends connect-src 'self' in iOS Chrome when connect-src is already defined", function (done) { var app = use(POLICY); var iosChrome = AGENTS["iOS Chrome 40"]; request(app).get("/").set("User-Agent", iosChrome.string) .expect(iosChrome.header, /connect-src (?:'self' connect.com)|(?:connect.com 'self')/) .end(done); }); it("adds connect-src 'self' in iOS Chrome when connect-src is undefined", function (done) { var app = use({ styleSrc: ["'self'"] }); var iosChrome = AGENTS["iOS Chrome 40"]; request(app).get("/").set("User-Agent", iosChrome.string) .expect(iosChrome.header, /connect-src 'self'/) .end(done); }); it("does nothing in iOS Chrome if connect-src 'self' is defined", function (done) { var app = use({ connectSrc: ["'self'"] }); var iosChrome = AGENTS["iOS Chrome 40"]; request(app).get("/").set("User-Agent", iosChrome.string) .expect(iosChrome.header, /connect-src 'self'/) .end(done); }); it("doesn't splice the original array", function (done) { var app = use({ "style-src": [ "'self'", "'unsafe-inline'" ] }); var chrome = AGENTS["Chrome 27"]; var ff = AGENTS["Firefox 22"]; request(app).get("/").set("User-Agent", chrome.string) .expect(chrome.header, /style-src 'self' 'unsafe-inline'/) .end(function() { request(app).get("/").set("User-Agent", ff.string) .expect(ff.header, /style-src 'self'/) .end(function() { request(app).get("/").set("User-Agent", chrome.string) .expect(chrome.header, /style-src 'self' 'unsafe-inline'/) .end(done); }); }); }); it("names its function and middleware", function () { assert.equal(csp.name, "csp"); assert.equal(csp().name, "csp"); }); });