blob: 7ca4edef8abf31dd2f2188488e1da9aa127a6e12 [file] [log] [blame]
/**
* This test checks the Secure Context state of documents for various
* permutations of document URI types and loading methods.
*
* The hierarchy that is tested is:
*
* creator-doc > createe-doc
*
* The creator-doc is one of:
*
* http:
* https:
*
* The createe-doc is loaded as either a:
*
* popup
* iframe
* sandboxed-iframe
*
* into which we load and test:
*
* http:
* https:
* blob:
* javascript:
* about:blank
* initial about:blank
* srcdoc
*
* TODO once web-platform-tests supports it:
* - test http://localhost
* - test file:
*
* TODO once https://github.com/w3c/webappsec-secure-contexts/issues/26 is resolved
* - test data:
*/
setup({explicit_done:true});
const host_and_dirname = location.host +
location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1);
// Flags to indicate where document types should be loaded for testing:
const eLoadInPopup = (1 << 0);
const eLoadInUnsandboxedIframe = (1 << 1);
const eLoadInSandboxedIframe = (1 << 2);
const eLoadInEverything = eLoadInPopup | eLoadInUnsandboxedIframe | eLoadInSandboxedIframe;
// Flags indicating if a document type is expected to be a Secure Context:
const eSecureNo = 1;
const eSecureIfNewWindow = 2;
const eSecureIfCreatorSecure = 3;
// Flags indicating how the result of a test is obtained:
const eResultFromPostMessage = 1;
const eResultFromExaminationOnLoad = 2;
const eResultFromExaminationSync = 3;
const loadTypes = [
new LoadType("an http: URI",
eLoadInEverything,
http_dir + "postMessage-helper.html",
eSecureNo,
eResultFromPostMessage),
new LoadType("an https: URI",
eLoadInEverything,
https_dir + "postMessage-helper.https.html",
eSecureIfNewWindow,
eResultFromPostMessage),
new LoadType("a blob: URI",
eLoadInEverything,
URL.createObjectURL(new Blob(["<script>(opener||parent).postMessage(isSecureContext, '*')</script>"], {type: "text/html"})),
eSecureIfCreatorSecure,
eResultFromPostMessage),
new LoadType("a srcdoc",
// popup not relevant:
eLoadInUnsandboxedIframe | eLoadInSandboxedIframe,
"<script>(opener||parent).postMessage(isSecureContext, '*')</script>",
eSecureIfNewWindow,
eResultFromPostMessage),
new LoadType("a javascript: URI",
// can't load in sandbox:
eLoadInUnsandboxedIframe | eLoadInPopup,
"javascript:(opener||parent).postMessage(isSecureContext, '*')",
eSecureIfCreatorSecure,
eResultFromPostMessage),
new LoadType("about:blank",
// can't obtain state if sandboxed:
eLoadInUnsandboxedIframe | eLoadInPopup,
"about:blank",
eSecureIfCreatorSecure,
eResultFromExaminationOnLoad),
new LoadType("initial about:blank",
// can't obtain state if sandboxed:
eLoadInUnsandboxedIframe | eLoadInPopup,
"about:blank", // we don't wait for this to load, so whatever
eSecureIfCreatorSecure,
eResultFromExaminationSync),
];
const loadTargets = [
new LoadTarget("an iframe", eLoadInUnsandboxedIframe),
new LoadTarget("a sandboxed iframe", eLoadInSandboxedIframe),
new LoadTarget("a popup", eLoadInPopup),
];
function LoadType(description, loadInFlags, uri, expectedSecureFlag, resultFrom) {
this.desc = description;
this.loadInFlags = loadInFlags;
this.uri = uri;
this.expectedSecureFlag = expectedSecureFlag;
this.resultFrom = resultFrom;
}
function LoadTarget(description, loadInFlag) {
this.desc = description;
this.loadInFlag = loadInFlag;
}
LoadTarget.prototype.open = function(loadType) {
let loadTarget = this;
this.currentTest.step(function() {
assert_true((loadTarget.loadInFlag & loadType.loadInFlags) != 0,
loadType.desc + " cannot be tested in " + loadTarget.desc);
});
if (this.loadInFlag == eLoadInUnsandboxedIframe) {
let iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
return iframe;
}
if (this.loadInFlag == eLoadInSandboxedIframe) {
let iframe = document.body.appendChild(document.createElement("iframe"));
iframe.setAttribute("sandbox", "allow-scripts");
iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
return iframe;
}
if (this.loadInFlag == eLoadInPopup) {
return window.open(loadType.uri);
}
this.currentTest.step(function() {
assert_unreached("Unknown load type flag: " + loadInFlags);
});
return null;
}
LoadTarget.prototype.close = function(domTarget) {
if (this.loadInFlag == eLoadInUnsandboxedIframe ||
this.loadInFlag == eLoadInSandboxedIframe) {
domTarget.remove();
return;
}
if (this.loadInFlag == eLoadInPopup) {
domTarget.close();
return;
}
this.currentTest.step(function() {
assert_unreached("Unknown load type flag: " + loadInFlags);
});
}
LoadTarget.prototype.load_and_get_result_for = function(loadType) {
if (!(loadType.loadInFlags & this.loadInFlag)) {
return Promise.reject("not applicable");
}
if (!(this.loadInFlag & eLoadInPopup) &&
location.protocol == "https:" &&
loadType.uri.substr(0,5) == "http:") {
// Mixed content blocker will prevent this load
return Promise.reject("not applicable");
}
this.currentTest = async_test("Test Window.isSecureContext in " + this.desc +
" loading " + loadType.desc)
if (loadType.resultFrom == eResultFromExaminationSync) {
let domTarget = this.open(loadType);
let isFrame = domTarget instanceof HTMLIFrameElement;
let result = !isFrame ?
domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
this.close(domTarget);
return Promise.resolve({ result: result, isFrame: isFrame});
}
let target = this;
if (loadType.resultFrom == eResultFromExaminationOnLoad) {
return new Promise(function(resolve, reject) {
function handleLoad(event) {
clearTimeout(timer);
let isFrame = domTarget instanceof HTMLIFrameElement;
let result = !isFrame ?
domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
domTarget.removeEventListener("load", handleLoad);
target.close(domTarget);
resolve({ result: result, isFrame: isFrame});
}
let domTarget = target.open(loadType);
domTarget.addEventListener("load", handleLoad, false);
// Some browsers don't fire `load` events for `about:blank`. That's weird, but it also
// isn't what we're testing here.
let timer = setTimeout(handleLoad, 500);
});
}
if (loadType.resultFrom == eResultFromPostMessage) {
return new Promise(function(resolve, reject) {
function handleMessage(event) {
let isFrame = domTarget instanceof HTMLIFrameElement;
window.removeEventListener("message", handleMessage);
target.close(domTarget);
resolve({ result: event.data, isFrame: isFrame});
}
window.addEventListener("message", handleMessage, false);
let domTarget = target.open(loadType);
});
}
return Promise.reject("unexpected 'result from' type");
}
let current_type_index = -1;
let current_target_index = 0;
function run_next_test() {
current_type_index++;
if (current_type_index >= loadTypes.length) {
current_type_index = 0;
current_target_index++;
if (current_target_index >= loadTargets.length) {
done();
return; // all test permutations complete
}
}
let loadTarget = loadTargets[current_target_index];
let loadType = loadTypes[current_type_index];
loadTarget.load_and_get_result_for(loadType).then(
function(value) {
run_next_test_soon();
loadTarget.currentTest.step(function() {
// If the new context is always non-secure, the assertion is straightforward.
if (loadType.expectedSecureFlag == eSecureNo) {
assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context");
// If the new context is always secure if opened in a new window, and it's
// been opened in a new window, the assertion is likewise straightforward.
} else if (loadType.expectedSecureFlag == eSecureIfNewWindow && !value.isFrame) {
assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a secure context regardless of its creator's state.");
// Otherwise, we're either dealing with a context that's secure if and only
// if its creator context (e.g. this window) is secure.
} else if ((loadType.expectedSecureFlag == eSecureIfNewWindow && value.isFrame) ||
(loadType.expectedSecureFlag == eSecureIfCreatorSecure)) {
if (!window.isSecureContext) {
assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context when its creator is not a Secure Context.");
} else {
assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a Secure Context when its creator is a Secure Context");
}
} else {
assert_unreached(loadType.desc + " - unknown expected secure flag: " + expectedSecureFlag);
}
loadTarget.currentTest.done();
});
},
function(failReason) {
run_next_test_soon();
if (failReason == "not applicable") {
return;
}
loadTarget.currentTest.step(function() {
assert_unreached(loadType.desc + " - got unexpected rejected promise");
});
}
);
}
function run_next_test_soon() {
setTimeout(run_next_test, 0);
}
function begin() {
test(function() {
if (location.protocol == "http:") {
assert_false(isSecureContext,
"http: creator should not be a Secure Context");
} else if (location.protocol == "https:") {
assert_true(isSecureContext,
"https: creator should be a Secure Context");
} else {
assert_unreached("Unknown location.protocol");
}
});
run_next_test();
}