Differential Revision: https://phabricator.services.mozilla.com/D42120

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Gijs Kruitbosch 2019-08-20 13:11:58 +00:00
Родитель 9237560765
Коммит d11d241116
3 изменённых файлов: 193 добавлений и 3 удалений

Просмотреть файл

@ -4,7 +4,7 @@
"use strict";
const { Ci, Cu, CC } = require("chrome");
const { components, Ci, Cr, Cu, CC } = require("chrome");
const ChromeUtils = require("ChromeUtils");
const Services = require("Services");
@ -121,6 +121,19 @@ Converter.prototype = {
// Initialize stuff.
const win = NetworkHelper.getWindowForRequest(request);
if (!win || !components.isSuccessCode(request.status)) {
return;
}
// We compare actual pointer identities here rather than using .equals(),
// because if things went correctly then the document must have exactly
// the principal we reset it to above. If not, something went wrong.
if (win.document.nodePrincipal != request.loadInfo.principalToInherit) {
// Whatever that document is, it's not ours.
request.cancel(Cr.NS_BINDING_ABORTED);
return;
}
this.data = exportData(win, headers);
insertJsonData(win, this.data.json);
win.addEventListener("contentMessage", onContentMessage, false, true);
@ -133,8 +146,10 @@ Converter.prototype = {
},
onStopRequest: function(request, statusCode) {
// Flush data.
this.decodeAndInsertBuffer(new ArrayBuffer(0), true);
// Flush data if we haven't been canceled.
if (components.isSuccessCode(statusCode)) {
this.decodeAndInsertBuffer(new ArrayBuffer(0), true);
}
// Stop the request.
this.listener.onStopRequest(request, statusCode);

Просмотреть файл

@ -39,6 +39,7 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
tags = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_jsonview_csp_json.js]
[browser_jsonview_data_blocking.js]
[browser_jsonview_empty_object.js]
[browser_jsonview_encoding.js]
[browser_jsonview_filter.js]

Просмотреть файл

@ -0,0 +1,174 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_PATH = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content",
"http://example.com"
);
const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
const nullP = Services.scriptSecurityManager.createNullPrincipal({});
// We need 3 levels of nesting just to get to run something against a content
// page, so the devtools limit of 4 levels of nesting don't help:
/* eslint max-nested-callbacks: 0 */
/**
* Check that we don't expose a JSONView object on data: URI windows where
* we block the load.
*/
add_task(async function test_blocked_data_exposure() {
await SpecialPowers.pushPrefEnv({
set: [["security.data_uri.block_toplevel_data_uri_navigations", true]],
});
await BrowserTestUtils.withNewTab(TEST_PATH + "empty.html", async browser => {
const tabCount = gBrowser.tabs.length;
await ContentTask.spawn(browser, null, function() {
content.w = content.window.open(
"data:application/vnd.mozilla.json.view,1",
"_blank"
);
ok(
!Cu.waiveXrays(content.w).JSONView,
"Should not have created a JSON View object"
);
// We need to wait for the JSON view machinery to actually have a chance to run.
// We have no way to detect that it has or hasn't, so a setTimeout is the best we
// can do, unfortunately.
return new Promise(resolve => {
content.setTimeout(function() {
// Putting the resolve before the check to avoid JS errors potentially causing
// the test to time out.
resolve();
ok(
!Cu.waiveXrays(content.w).JSONView,
"Should still not have a JSON View object"
);
}, 1000);
});
});
// Without this, if somehow the data: protocol blocker stops working, the
// test would just keep passing.
is(
tabCount,
gBrowser.tabs.length,
"Haven't actually opened a new window/tab"
);
});
});
/**
* Check that aborted channels also abort sending data from the stream converter.
*/
add_task(async function test_converter_abort_should_stop_data_sending() {
const loadInfo = NetUtil.newChannel({
uri: Services.io.newURI("data:text/plain,"),
loadingPrincipal: nullP,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
}).loadInfo;
// Stub all the things.
const chan = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIChannel,
Ci.nsIWritablePropertyBag,
]),
URI: Services.io.newURI("data:application/json,{}"),
// loadinfo is builtinclass, need to actually have one:
loadInfo,
notificationCallbacks: {
QueryInterface: ChromeUtils.generateQI([Ci.nsIInterfaceRequestor]),
getInterface() {
// We want a loadcontext here, which is also builtinclass, can't stub.
return docShell;
},
},
status: Cr.NS_OK,
setProperty() {},
};
let onStartFired = false;
const listener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener]),
onStartRequest() {
onStartFired = true;
// This should force the converter to abort, too:
chan.status = Cr.NS_BINDING_ABORTED;
},
onDataAvailable() {
ok(false, "onDataAvailable should never fire");
},
};
const conv = Cc[
"@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
].createInstance(Ci.nsIStreamConverter);
conv.asyncConvertData(
"application/vnd.mozilla.json.view",
"text/html",
listener,
null
);
conv.onStartRequest(chan);
ok(onStartFired, "Should have fired onStartRequest");
});
/**
* Check that principal mismatches break things. Note that we're stubbing
* the window associated with the channel to be a browser window; the
* converter should be bailing out because the window's principal won't
* match the null principal to which the converter tries to reset the
* inheriting principal of the channel.
*/
add_task(async function test_converter_principal_needs_matching() {
const loadInfo = NetUtil.newChannel({
uri: Services.io.newURI("data:text/plain,"),
loadingPrincipal: nullP,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
}).loadInfo;
// Stub all the things.
const chan = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIChannel,
Ci.nsIWritablePropertyBag,
]),
URI: Services.io.newURI("data:application/json,{}"),
// loadinfo is builtinclass, need to actually have one:
loadInfo,
notificationCallbacks: {
QueryInterface: ChromeUtils.generateQI([Ci.nsIInterfaceRequestor]),
getInterface() {
// We want a loadcontext here, which is also builtinclass, can't stub.
return docShell;
},
},
status: Cr.NS_OK,
setProperty() {},
cancel(arg) {
this.status = arg;
},
};
let onStartFired = false;
const listener = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIStreamListener]),
onStartRequest() {
onStartFired = true;
},
onDataAvailable() {
ok(false, "onDataAvailable should never fire");
},
};
const conv = Cc[
"@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
].createInstance(Ci.nsIStreamConverter);
conv.asyncConvertData(
"application/vnd.mozilla.json.view",
"text/html",
listener,
null
);
conv.onStartRequest(chan);
ok(onStartFired, "Should have fired onStartRequest");
is(chan.status, Cr.NS_BINDING_ABORTED, "Should have been aborted.");
});