mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Bug 515460 - Mochitests for CSP redirect handling, a=dholbert_sheriff
This commit is contained in:
parent
2265b71345
commit
5d4557215c
@ -373,6 +373,10 @@ _TEST_FILES = test_bug5141.html \
|
||||
test_bug557892.html \
|
||||
file_bug557892.html \
|
||||
test_bug559526.html \
|
||||
test_csp_redirects.html \
|
||||
file_csp_redirects_page.sjs \
|
||||
file_csp_redirects_main.html \
|
||||
file_csp_redirects_resource.sjs \
|
||||
$(NULL)
|
||||
|
||||
# This test fails on the Mac for some reason
|
||||
|
35
content/base/test/file_csp_redirects_main.html
Normal file
35
content/base/test/file_csp_redirects_main.html
Normal file
@ -0,0 +1,35 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>CSP redirect tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var thisSite = "http://mochi.test:8888";
|
||||
var otherSite = "http://example.com";
|
||||
var page = "/tests/content/base/test/file_csp_redirects_page.sjs";
|
||||
|
||||
var tests = { "font-src": thisSite+page+"?testid=font-src&csp=1",
|
||||
"frame-src": thisSite+page+"?testid=frame-src&csp=1",
|
||||
"img-src": thisSite+page+"?testid=img-src&csp=1",
|
||||
"media-src": thisSite+page+"?testid=media-src&csp=1",
|
||||
"object-src": thisSite+page+"?testid=object-src&csp=1",
|
||||
"script-src": thisSite+page+"?testid=script-src&csp=1",
|
||||
"style-src": thisSite+page+"?testid=style-src&csp=1",
|
||||
"worker": thisSite+page+"?testid=worker&csp=1",
|
||||
"xhr-src": thisSite+page+"?testid=xhr-src&csp=1",
|
||||
};
|
||||
|
||||
var container = document.getElementById("container");
|
||||
|
||||
// load each test in its own iframe
|
||||
for (tid in tests) {
|
||||
var i = document.createElement("iframe");
|
||||
i.id = tid;
|
||||
i.src = tests[tid];
|
||||
container.appendChild(i);
|
||||
}
|
||||
</script>
|
||||
</html>
|
79
content/base/test/file_csp_redirects_page.sjs
Normal file
79
content/base/test/file_csp_redirects_page.sjs
Normal file
@ -0,0 +1,79 @@
|
||||
// SJS file for CSP redirect mochitests
|
||||
// This file serves pages which can optionally specify a Content Security Policy
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
|
||||
var resource = "/tests/content/base/test/file_csp_redirects_resource.sjs";
|
||||
|
||||
// CSP header value
|
||||
if (query["csp"] == 1) {
|
||||
response.setHeader("X-Content-Security-Policy", "allow 'self'", false);
|
||||
}
|
||||
|
||||
// downloadable font that redirects to another site
|
||||
if (query["testid"] == "font-src") {
|
||||
var resp = '<style type="text/css"> @font-face { font-family:' +
|
||||
'"Redirecting Font"; src: url("' + resource +
|
||||
'?res=font&redir=other&id=font-src-redir")} #test{font-family:' +
|
||||
'"Redirecting Font"}</style></head><body>' +
|
||||
'<div id="test">test</div></body>';
|
||||
response.write(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// iframe that redirects to another site
|
||||
if (query["testid"] == "frame-src") {
|
||||
response.write('<iframe src="'+resource+'?res=iframe&redir=other&id=frame-src-redir"></iframe>');
|
||||
return;
|
||||
}
|
||||
|
||||
// image that redirects to another site
|
||||
if (query["testid"] == "img-src") {
|
||||
response.write('<img src="'+resource+'?res=image&redir=other&id=img-src-redir" />');
|
||||
return;
|
||||
}
|
||||
|
||||
// video content that redirects to another site
|
||||
if (query["testid"] == "media-src") {
|
||||
response.write('<video src="'+resource+'?res=media&redir=other&id=media-src-redir"></video>');
|
||||
return;
|
||||
}
|
||||
|
||||
// object content that redirects to another site
|
||||
if (query["testid"] == "object-src") {
|
||||
response.write('<object type="text/html" data="'+resource+'?res=object&redir=other&id=object-src-redir"></object>');
|
||||
return;
|
||||
}
|
||||
|
||||
// external script that redirects to another site
|
||||
if (query["testid"] == "script-src") {
|
||||
response.write('<script src="'+resource+'?res=script&redir=other&id=script-src-redir"></script>');
|
||||
return;
|
||||
}
|
||||
|
||||
// external stylesheet that redirects to another site
|
||||
if (query["testid"] == "style-src") {
|
||||
response.write('<link rel="stylesheet" type="text/css" href="'+resource+'?res=style&redir=other&id=style-src-redir"></script>');
|
||||
return;
|
||||
}
|
||||
|
||||
// worker script resource that redirects to another site
|
||||
if (query["testid"] == "worker") {
|
||||
response.write('<script src="'+resource+'?res=worker&redir=other&id=worker-redir"></script>');
|
||||
return;
|
||||
}
|
||||
|
||||
// script that XHR's to a resource that redirects to another site
|
||||
if (query["testid"] == "xhr-src") {
|
||||
response.write('<script src="'+resource+'?res=xhr"></script>');
|
||||
return;
|
||||
}
|
||||
}
|
112
content/base/test/file_csp_redirects_resource.sjs
Normal file
112
content/base/test/file_csp_redirects_resource.sjs
Normal file
@ -0,0 +1,112 @@
|
||||
// SJS file to serve resources for CSP redirect tests
|
||||
// This file mimics serving resources, e.g. fonts, images, etc., which a CSP
|
||||
// can include. The resource may redirect to a different resource, if specified.
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = {};
|
||||
request.queryString.split('&').forEach(function (val) {
|
||||
var [name, value] = val.split('=');
|
||||
query[name] = unescape(value);
|
||||
});
|
||||
|
||||
var thisSite = "http://mochi.test:8888";
|
||||
var otherSite = "http://example.com";
|
||||
var resource = "/tests/content/base/test/file_csp_redirects_resource.sjs";
|
||||
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
|
||||
// redirect to a resource on this site
|
||||
if (query["redir"] == "same") {
|
||||
var loc = thisSite+resource+"?res="+query["res"]+"&testid="+query["id"];
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", loc, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// redirect to a resource on a different site
|
||||
else if (query["redir"] == "other") {
|
||||
var loc = otherSite+resource+"?res="+query["res"]+"&testid="+query["id"];
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", loc, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// not a redirect. serve some content.
|
||||
// the content doesn't have to be valid, since we're only checking whether
|
||||
// the request for the content was sent or not.
|
||||
|
||||
// downloadable font
|
||||
if (query["res"] == "font") {
|
||||
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.write("font data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// iframe with arbitrary content
|
||||
if (query["res"] == "iframe") {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write("iframe content...");
|
||||
return;
|
||||
}
|
||||
|
||||
// image
|
||||
if (query["res"] == "image") {
|
||||
response.setHeader("Content-Type", "image/gif", false);
|
||||
response.write("image data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// media content, e.g. Ogg video
|
||||
if (query["res"] == "media") {
|
||||
response.setHeader("Content-Type", "video/ogg", false);
|
||||
response.write("video data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// plugin content, e.g. <object>
|
||||
if (query["res"] == "object") {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write("object data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// script
|
||||
if (query["res"] == "script") {
|
||||
response.setHeader("Content-Type", "application/javascript", false);
|
||||
response.write("some script...");
|
||||
return;
|
||||
}
|
||||
|
||||
// external stylesheet
|
||||
if (query["res"] == "style") {
|
||||
response.setHeader("Content-Type", "text/css", false);
|
||||
response.write("css data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// web worker resource
|
||||
if (query["res"] == "worker") {
|
||||
response.setHeader("Content-Type", "application/javascript", false);
|
||||
response.write("worker script data...");
|
||||
return;
|
||||
}
|
||||
|
||||
// script that invokes XHR
|
||||
if (query["res"] == "xhr") {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
var resp = 'var x = new XMLHttpRequest(); x.open("GET", "' + otherSite +
|
||||
resource+'?res=xhr-resp&testid=xhr-src-redir", false); ' +
|
||||
'x.send(null);';
|
||||
response.write(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
// response to XHR
|
||||
if (query["res"] == "xhr-resp") {
|
||||
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write('XHR response...');
|
||||
return;
|
||||
}
|
||||
}
|
131
content/base/test/test_csp_redirects.html
Normal file
131
content/base/test/test_csp_redirects.html
Normal file
@ -0,0 +1,131 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests for Content Security Policy during redirects</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
|
||||
<iframe style="width:100%;height:300px;" id="harness"></iframe>
|
||||
<pre id="log"></pre>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/";
|
||||
|
||||
// debugging
|
||||
function log(s) {
|
||||
return;
|
||||
var log = document.getElementById("log");
|
||||
log.textContent = log.textContent+s+"\n";
|
||||
}
|
||||
|
||||
// used to watch if requests are blocked by CSP or allowed through
|
||||
function examiner() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var obsvc = Components.classes['@mozilla.org/observer-service;1']
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
obsvc.addObserver(this, "csp-on-violate-policy", false);
|
||||
obsvc.addObserver(this, "http-on-modify-request", false);
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe: function(subject, topic, data) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
// subject should be an nsURI, and should be either allowed or blocked.
|
||||
if(!subject.QueryInterface)
|
||||
return;
|
||||
|
||||
var testpat = new RegExp("testid=([a-z0-9-]+)");
|
||||
var uri;
|
||||
var testid;
|
||||
|
||||
if (topic === "http-on-modify-request") {
|
||||
// request was sent
|
||||
uri = subject.QueryInterface(Components.interfaces.nsIHttpChannel).URI;
|
||||
if (!testpat.test(uri.asciiSpec)) return;
|
||||
testid = testpat.exec(uri.asciiSpec)[1];
|
||||
if (testExpectedResults[testid] == "completed") return;
|
||||
log("allowed: "+uri.asciiSpec);
|
||||
window.testResult(testid, uri.asciiSpec, true);
|
||||
}
|
||||
|
||||
else if (topic === "csp-on-violate-policy") {
|
||||
// request was blocked
|
||||
uri = subject.QueryInterface(Components.interfaces.nsIURI);
|
||||
if (!testpat.test(uri.asciiSpec)) return;
|
||||
testid = testpat.exec(uri.asciiSpec)[1];
|
||||
// had to add this check because http-on-modify-request can fire after
|
||||
// csp-on-violate-policy, apparently, even though the request does
|
||||
// not hit the wire.
|
||||
if (testExpectedResults[testid] == "completed") return;
|
||||
log("BLOCKED: "+uri.asciiSpec);
|
||||
window.testResult(testid, uri.asciiSpec, false);
|
||||
}
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var obsvc = Components.classes['@mozilla.org/observer-service;1']
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
obsvc.removeObserver(this, "csp-on-violate-policy");
|
||||
obsvc.removeObserver(this, "http-on-modify-request");
|
||||
}
|
||||
}
|
||||
window.examiner = new examiner();
|
||||
|
||||
// contains { test_frame_id : expected_result }
|
||||
var testExpectedResults = { "font-src": true,
|
||||
"font-src-redir": false,
|
||||
"frame-src": true,
|
||||
"frame-src-redir": false,
|
||||
"img-src": true,
|
||||
"img-src-redir": false,
|
||||
"media-src": true,
|
||||
"media-src-redir": false,
|
||||
"object-src": true,
|
||||
"object-src-redir": false,
|
||||
"script-src": true,
|
||||
"script-src-redir": false,
|
||||
"style-src": true,
|
||||
"style-src-redir": false,
|
||||
"worker": true,
|
||||
"worker-redir": false,
|
||||
"xhr-src": true,
|
||||
"xhr-src-redir": false,
|
||||
};
|
||||
|
||||
// takes the name of the test, the URL that was tested, and whether the
|
||||
// load occured
|
||||
var testResult = function(testName, url, result) {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
log(" testName: "+testName+", result: "+result+", expected: "+testExpectedResults[testName]+"\n");
|
||||
is(result, testExpectedResults[testName], testName+" test: "+url);
|
||||
|
||||
// mark test as completed
|
||||
testExpectedResults[testName] = "completed";
|
||||
|
||||
// don't finish until we've run all the tests
|
||||
for (var t in testExpectedResults) {
|
||||
if (testExpectedResults[t] != "completed")
|
||||
return;
|
||||
}
|
||||
|
||||
window.examiner.remove();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// load the test harness
|
||||
document.getElementById("harness").src = "file_csp_redirects_main.html";
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user