Bug 1193837: Part 2 - [webext] Enforce host matching and load URI restrictions on tabs.executeScript and insertCSS. r=billm

This commit is contained in:
Kris Maglione 2015-12-01 20:32:18 -08:00
parent f615244110
commit ee06b6660a
4 changed files with 63 additions and 8 deletions

View File

@ -477,12 +477,32 @@ extensions.registerAPI((extension, context) => {
let tab = tabId ? TabManager.getTab(tabId) : TabManager.activeTab;
let mm = tab.linkedBrowser.messageManager;
let options = {js: [], css: []};
let options = {
js: [],
css: [],
// We need to send the inner window ID to make sure we only
// execute the script if the window is currently navigated to
// the document that we expect.
//
// TODO: When we add support for callbacks, non-matching
// window IDs and insufficient permissions need to result in a
// callback with |lastError| set.
innerWindowID: tab.linkedBrowser.innerWindowID,
matchesHost: extension.whiteListedHosts.serialize(),
};
if (details.code) {
options[kind + 'Code'] = details.code;
}
if (details.file) {
options[kind].push(extension.baseURI.resolve(details.file));
let url = context.uri.resolve(details.file);
if (extension.isExtensionURL(url)) {
// We should really set |lastError| here, and go straight to
// the callback, but we don't have |lastError| yet.
options[kind].push(url);
}
}
if (details.allFrames) {
options.all_frames = details.allFrames;

View File

@ -3,7 +3,7 @@ add_task(function* () {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"permissions": ["tabs"]
"permissions": ["http://mochi.test/"]
},
background: function() {

View File

@ -213,6 +213,29 @@ ExtensionPage.prototype = {
return this.contentWindow;
},
get principal() {
return this.contentWindow.document.nodePrincipal;
},
checkLoadURL(url, options = {}) {
let ssm = Services.scriptSecurityManager;
let flags = ssm.STANDARD;
if (!options.allowScript) {
flags |= ssm.DISALLOW_SCRIPT;
}
if (!options.allowInheritsPrincipal) {
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
}
try {
ssm.checkLoadURIStrWithPrincipal(this.principal, url, flags);
} catch (e) {
return false;
}
return true;
},
callOnClose(obj) {
this.onClose.add(obj);
},
@ -860,10 +883,10 @@ Extension.prototype = extend(Object.create(ExtensionData.prototype), {
let whitelist = [];
for (let perm of permissions) {
if (perm.match(/:\/\//)) {
whitelist.push(perm);
} else {
if (/^\w+(\.\w+)*$/.test(perm)) {
this.permissions.add(perm);
} else {
whitelist.push(perm);
}
}
this.whiteListedHosts = new MatchPattern(whitelist);

View File

@ -112,6 +112,9 @@ function Script(options)
this.matches_ = new MatchPattern(this.options.matches);
this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null);
// TODO: MatchPattern should pre-mangle host-only patterns so that we
// don't need to call a separate match function.
this.matches_host_ = new MatchPattern(this.options.matchesHost || null);
// TODO: Support glob patterns.
}
@ -119,7 +122,7 @@ function Script(options)
Script.prototype = {
matches(window) {
let uri = window.document.documentURIObject;
if (!this.matches_.matches(uri)) {
if (!(this.matches_.matches(uri) || this.matches_host_.matchesIgnoringPath(uri))) {
return false;
}
@ -131,6 +134,16 @@ Script.prototype = {
return false;
}
if ("innerWindowID" in this.options) {
let innerWindowID = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.currentInnerWindowID;
if (innerWindowID !== this.options.innerWindowID) {
return false;
}
}
// TODO: match_about_blank.
return true;
@ -569,7 +582,6 @@ this.ExtensionContent = {
receiveMessage({target, name, data}) {
switch (name) {
case "Extension:Execute":
data.options.matches = "<all_urls>";
let script = new Script(data.options);
let {extensionId} = data;
DocumentManager.executeScript(target, extensionId, script);