gecko-dev/dom/base/test/bug704320.sjs
Kate McKinley c57d400961 Bug 1246540 - HSTS Priming Proof of Concept. r=ckerschb, r=mayhemer, r=jld, r=smaug, r=dkeeler, r=jmaher, p=ally
HSTS priming changes the order of mixed-content blocking and HSTS
upgrades, and adds a priming request to check if a mixed-content load is
accesible over HTTPS and the server supports upgrading via the
Strict-Transport-Security header.

Every call site that uses AsyncOpen2 passes through the mixed-content
blocker, and has a LoadInfo. If the mixed-content blocker marks the load as
needing HSTS priming, nsHttpChannel will build and send an HSTS priming
request on the same URI with the scheme upgraded to HTTPS. If the server
allows the upgrade, then channel performs an internal redirect to the HTTPS URI,
otherwise use the result of mixed-content blocker to allow or block the
load.

nsISiteSecurityService adds an optional boolean out parameter to
determine if the HSTS state is already cached for negative assertions.
If the host has been probed within the previous 24 hours, no HSTS
priming check will be sent.

MozReview-Commit-ID: ES1JruCtDdX

--HG--
extra : rebase_source : 2ac6c93c49f2862fc0b9e595eb0598cd1ea4bedf
2016-09-27 11:27:00 -04:00

300 lines
11 KiB
JavaScript

var BASE_URL = 'example.com/tests/dom/base/test/bug704320.sjs';
function createTestUrl(schemeFrom, schemeTo, policy, action, type) {
return schemeTo + '://' + BASE_URL + '?' +
'action=' + action + '&' +
'scheme=' + schemeFrom + '-to-' + schemeTo + '&' +
'policy=' + policy + '&' +
'type=' + type;
}
function create2ndLevelIframeUrl(schemeFrom, schemeTo, policy, type) {
return schemeFrom + '://' + BASE_URL + '?' +
'action=create-2nd-level-iframe&' +
'scheme-from=' + schemeFrom + '&' +
'scheme-to=' + schemeTo + '&' +
'policy=' + policy + '&' +
'type=' + type;
}
// Creates the following test cases for the specified scheme and referrer
// policy combination:
// <link>
// @import
// font-face
// bg-url
// <script>
// <img>
// <iframe>
// <audio>
// <video>
// <object type="bogus">
// <object type="image/svg+xml">
// <a>
// <a ping>
// <form>
// window.location
// window.open
// XMLHttpRequest
// EventSource
// TODO: XSLT?
//
// This returns a page that loads all of the above resources and contains a
// script that clicks a link after all resources are (hopefully)
// loaded. The click triggers a redirection to file_bug704320_redirect.html,
// which in turn notifies the main window that it's time to check the test
// results.
function createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy) {
var _createTestUrl = createTestUrl.bind(
null, schemeFrom, schemeTo, policy, 'test');
var _create2ndLevelIframeUrl = create2ndLevelIframeUrl.bind(
null, schemeFrom, schemeTo, policy);
var metaReferrerPolicyString = '';
if (optionalEarlierPolicy && optionalEarlierPolicy != '') {
metaReferrerPolicyString += '<meta name="referrer" content="' + optionalEarlierPolicy + '">\n';
}
metaReferrerPolicyString += '<meta name="referrer" content="' + policy + '">';
return '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
'+metaReferrerPolicyString+'\n\
<link rel="stylesheet" type="text/css" href="' + _createTestUrl('stylesheet') + '">\n\
<style type="text/css">\n\
@import "' + _createTestUrl('import-css') + '";\n\
@font-face {\n\
font-family: "Fake Serif Bold";\n\
src: url("' + _createTestUrl('font-face') + '");\n\
}\n\
body {\n\
font-family: "Fake Serif Bold", serif;\n\
background: url("' + _createTestUrl('bg-url') + '");\n\
}\n\
</style>\n\
</head>\n\
<body>\n\
<script src="' + _createTestUrl('script') + '"></script>\n\
<img src="' + _createTestUrl('img') + '"></img>\n\
<iframe src="' + _createTestUrl('iframe') + '"></iframe>\n\
<audio src="' + _createTestUrl('audio') + '"></audio>\n\
<video src="' + _createTestUrl('video') + '"></video>\n\
<object type="bogus" data="' + _createTestUrl('object') + '"></object>\n\
<object type="image/svg+xml" data="' + _createTestUrl('object-svg') + '"></object>\n\
<a id="link" href="' + _createTestUrl('link') + '" ping="' + _createTestUrl('link-ping') + '"></a>\n\
<iframe src="' + _create2ndLevelIframeUrl('form') + '"></iframe>\n\
<iframe src="' + _create2ndLevelIframeUrl('window.location') + '"></iframe>\n\
<script>\n\
var _testFinished = 0\n\
(function() {\n\
var x = new XMLHttpRequest();\n\
x.open("GET", "' + _createTestUrl('xmlhttprequest') + '");\n\
x.send();\n\
})();\n\
(function() {\n\
var eventSource = new EventSource("' + _createTestUrl('eventsource') + '");\n\
})();' +
// LOAD EVENT (most of the tests)
// fires when the resources for the page are loaded
'var _isLoaded = false;\n\
window.addEventListener("load", function() {\n\
this._isLoaded = true;\n\
this.checkForFinish();\n\
}.bind(window), false);' +
// WINDOW.OPEN test
// listen for incoming status from window.open, close the window
// and check if we're done.
'var _openedWindowLoaded = false;\n\
window.addEventListener("message", function(message) {\n\
if (message.data == "window.open") {\n\
this._openedWindowLoaded = true;\n\
this.win.close();\n\
this.checkForFinish();\n\
}\n\
}.bind(window), false);\n\
var win = window.open("' + _createTestUrl('window.open') + '", "");' +
// called by the two things that must complete: window.open page
// and the window load event. When both are complete, this
// "finishes" the iframe subtest by clicking the link.
// _testFinished avoids calling this function twice (which may happen)
'function checkForFinish() {\n\
if (window._isLoaded && window._openedWindowLoaded && !window._testFinished) {\n\
window._testFinished = 1;\n\
document.getElementById("link").click();\n\
}\n\
}\n\
</script>\n\
</body>\n\
</html>';
}
function createIframedFormTest(schemeFrom, schemeTo, policy) {
var actionUrl = schemeTo + '://' + BASE_URL;
return '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
<meta name="referrer" content="' + policy + '">\n\
</head>\n\
<body>\n\
<form id="form" action="' + actionUrl + '">\n\
<input type="hidden" name="action" value="test">\n\
<input type="hidden" name="scheme" value="' + schemeFrom + '-to-' + schemeTo + '">\n\
<input type="hidden" name="policy" value="' + policy + '">\n\
<input type="hidden" name="type" value="form">\n\
</form>\n\
<script>\n\
document.getElementById("form").submit();\n\
</script>\n\
</body>\n\
</html>';
}
function createIframedWindowLocationTest(schemeFrom, schemeTo, policy) {
var url = createTestUrl(
schemeFrom, schemeTo, policy, 'test', 'window.location');
return '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
<meta name="referrer" content="' + policy + '">\n\
</head>\n\
<body>\n\
<script>\n\
window.location = "' + url + '";\n\
</script>\n\
</body>\n\
</html>';
}
function createPolicyTest(policy, optionalEarlierPolicy) {
var metaReferrerPolicyString = '';
if (optionalEarlierPolicy && optionalEarlierPolicy != '') {
metaReferrerPolicyString += '<meta name="referrer" content="' + optionalEarlierPolicy + '">\n';
}
metaReferrerPolicyString += '<meta name="referrer" content="' + policy + '">';
return '<!DOCTYPE HTML>\n\
<html>\n\
<head>\n\
'+metaReferrerPolicyString+'\n\
<script type="text/javascript" src="/tests/dom/base/test/file_bug704320_preload_common.js"></script>\n\
</head>\n\
<body>\n\
<img src="/tests/dom/base/test/bug704320_counter.sjs?type=img"\n\
onload="incrementLoad2(\'img\', 2);">\n\
<img src="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=img"\n\
onload="incrementLoad2(\'img\', 2);">\n\
</body>\n\
</html>';
}
function handleRequest(request, response) {
if (request.method == 'HEAD') {
// respond to a HEAD request with a 418 so that we can easily distinguish
// HSTS priming responses and ignore them
response.setStatusLine('1.1', 418, "I'm a teapot");
return;
}
var sharedKey = 'bug704320.sjs';
var params = request.queryString.split('&');
var action = params[0].split('=')[1];
if (action === 'create-1st-level-iframe') {
// ?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin
var schemeFrom = params[1].split('=')[1];
var schemeTo = params[2].split('=')[1];
var policy = params[3].split('=')[1];
var optionalEarlierPolicy = '';
if (params[4]) {
optionalEarlierPolicy = params[4].split('=')[1];
}
response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
response.setHeader('Cache-Control', 'no-cache', false);
response.write(createTest(schemeFrom, schemeTo, policy, optionalEarlierPolicy));
}
else if (action === 'create-2nd-level-iframe') {
// ?action=create-2nd-level-iframe&scheme-from=http&scheme-to=https&policy=origin&type=form"
var schemeFrom = params[1].split('=')[1];
var schemeTo = params[2].split('=')[1];
var policy = params[3].split('=')[1];
var type = params[4].split('=')[1];
response.setHeader('Content-Type', 'text/html; charset=utf-8', false);
response.setHeader('Cache-Control', 'no-cache', false);
if (type === 'form') {
response.write(createIframedFormTest(schemeFrom, schemeTo, policy));
} else if (type === 'window.location') {
response.write(createIframedWindowLocationTest(
schemeFrom, schemeTo, policy));
}
}
else if (action === 'test') {
// ?action=test&scheme=http-to-https&policy=origin&type=img
var scheme = params[1].split('=')[1];
var policy = params[2].split('=')[1];
var type = params[3].split('=')[1];
var result = getSharedState(sharedKey);
if (result === '') {
result = {};
} else {
result = JSON.parse(result);
}
if (!result[type]) {
result[type] = {};
}
if (!result[type][scheme]) {
result[type][scheme] = {};
}
if (request.hasHeader('Referer')) {
result[type][scheme][policy] = request.getHeader('Referer');
} else {
result[type][scheme][policy] = '';
}
setSharedState(sharedKey, JSON.stringify(result));
if (type === 'link') {
var loc = 'https://example.com/tests/dom/base/test/file_bug704320_redirect.html';
response.setStatusLine('1.1', 302, 'Found');
response.setHeader('Location', loc, false);
}
if (type === 'window.open') {
response.setHeader('Cache-Control', 'no-cache', false);
response.setHeader('Content-Type', 'text/html', false);
response.write('<html><body><script>'
+ 'window.opener.postMessage("window.open", "*");'
+ '</script></body></html>');
}
}
else if (action === 'get-test-results') {
// ?action=get-result
response.setHeader('Cache-Control', 'no-cache', false);
response.setHeader('Content-Type', 'text/plain', false);
response.write(getSharedState(sharedKey));
}
else if (action === 'generate-policy-test') {
// ?action=generate-policy-test&policy=b64-encoded-string
response.setHeader('Cache-Control', 'no-cache', false);
response.setHeader('Content-Type', 'text/html', false);
var policy = unescape(params[1].split('=')[1]);
var optionalEarlierPolicy = '';
if (params[2]) {
optionalEarlierPolicy = params[2].split('=')[1];
}
response.write(createPolicyTest(policy, optionalEarlierPolicy));
}
}