helmet-csp-uc
Version:
Content Security Policy middleware.
388 lines (351 loc) • 13.9 kB
JavaScript
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");
});
});