mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 879658: don't expose localStorage to FrameWorker for non-whitelisted social providers, r=mixedpuppy
--HG-- extra : rebase_source : 225b3903057e362e180f8ded7e18bf26a63bacb0
This commit is contained in:
parent
7dca0eb71c
commit
72d2cfc005
@ -30,7 +30,7 @@ var _nextPortId = 1;
|
||||
// Retrieves a reference to a WorkerHandle associated with a FrameWorker and a
|
||||
// new ClientPort.
|
||||
this.getFrameWorkerHandle =
|
||||
function getFrameWorkerHandle(url, clientWindow, name, origin) {
|
||||
function getFrameWorkerHandle(url, clientWindow, name, origin, exposeLocalStorage = false) {
|
||||
// first create the client port we are going to use. Later we will
|
||||
// message the worker to create the worker port.
|
||||
let portid = _nextPortId++;
|
||||
@ -39,7 +39,7 @@ this.getFrameWorkerHandle =
|
||||
let existingWorker = workerCache[url];
|
||||
if (!existingWorker) {
|
||||
// setup the worker and add this connection to the pending queue
|
||||
let worker = new FrameWorker(url, name, origin);
|
||||
let worker = new FrameWorker(url, name, origin, exposeLocalStorage);
|
||||
worker.pendingPorts.push(clientPort);
|
||||
existingWorker = workerCache[url] = worker;
|
||||
} else {
|
||||
@ -69,7 +69,7 @@ this.getFrameWorkerHandle =
|
||||
* the script does not have a full DOM but is instead run in a sandbox
|
||||
* that has a select set of methods cloned from the URL's domain.
|
||||
*/
|
||||
function FrameWorker(url, name, origin) {
|
||||
function FrameWorker(url, name, origin, exposeLocalStorage) {
|
||||
this.url = url;
|
||||
this.name = name || url;
|
||||
this.ports = new Map();
|
||||
@ -78,6 +78,7 @@ function FrameWorker(url, name, origin) {
|
||||
this.reloading = false;
|
||||
this.origin = origin;
|
||||
this._injectController = null;
|
||||
this.exposeLocalStorage = exposeLocalStorage;
|
||||
|
||||
this.frame = makeHiddenFrame();
|
||||
this.load();
|
||||
@ -133,11 +134,17 @@ FrameWorker.prototype = {
|
||||
// copy the window apis onto the sandbox namespace only functions or
|
||||
// objects that are naturally a part of an iframe, I'm assuming they are
|
||||
// safe to import this way
|
||||
let workerAPI = ['WebSocket', 'localStorage', 'atob', 'btoa',
|
||||
let workerAPI = ['WebSocket', 'atob', 'btoa',
|
||||
'clearInterval', 'clearTimeout', 'dump',
|
||||
'setInterval', 'setTimeout', 'XMLHttpRequest',
|
||||
'FileReader', 'Blob', 'EventSource', 'indexedDB',
|
||||
'location'];
|
||||
|
||||
// Only expose localStorage if the caller opted-in
|
||||
if (this.exposeLocalStorage) {
|
||||
workerAPI.push('localStorage');
|
||||
}
|
||||
|
||||
// Bug 798660 - XHR and WebSocket have issues in a sandbox and need
|
||||
// to be unwrapped to work
|
||||
let needsWaive = ['XMLHttpRequest', 'WebSocket'];
|
||||
|
@ -137,6 +137,42 @@ let SocialServiceInternal = {
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
|
||||
initService();
|
||||
let providers = {};
|
||||
for (let manifest of this.manifests) {
|
||||
try {
|
||||
if (ActiveProviders.has(manifest.origin)) {
|
||||
let activationType = getOriginActivationType(manifest.origin);
|
||||
let blessed = activationType == "builtin" ||
|
||||
activationType == "whitelist";
|
||||
let provider = new SocialProvider(manifest, blessed);
|
||||
providers[provider.origin] = provider;
|
||||
}
|
||||
} catch (err) {
|
||||
Cu.reportError("SocialService: failed to load provider: " + manifest.origin +
|
||||
", exception: " + err);
|
||||
}
|
||||
}
|
||||
return providers;
|
||||
});
|
||||
|
||||
function getOriginActivationType(origin) {
|
||||
let prefname = SocialServiceInternal.getManifestPrefname(origin);
|
||||
if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
|
||||
return 'builtin';
|
||||
|
||||
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
|
||||
if (whitelist.indexOf(origin) >= 0)
|
||||
return 'whitelist';
|
||||
|
||||
let directories = Services.prefs.getCharPref("social.directories").split(',');
|
||||
if (directories.indexOf(origin) >= 0)
|
||||
return 'directory';
|
||||
|
||||
return 'foreign';
|
||||
}
|
||||
|
||||
let ActiveProviders = {
|
||||
get _providers() {
|
||||
delete this._providers;
|
||||
@ -304,23 +340,6 @@ function initService() {
|
||||
MozSocialAPI.enabled = true;
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
|
||||
initService();
|
||||
let providers = {};
|
||||
for (let manifest of this.manifests) {
|
||||
try {
|
||||
if (ActiveProviders.has(manifest.origin)) {
|
||||
let provider = new SocialProvider(manifest);
|
||||
providers[provider.origin] = provider;
|
||||
}
|
||||
} catch (err) {
|
||||
Cu.reportError("SocialService: failed to load provider: " + manifest.origin +
|
||||
", exception: " + err);
|
||||
}
|
||||
}
|
||||
return providers;
|
||||
});
|
||||
|
||||
function schedule(callback) {
|
||||
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
@ -444,20 +463,8 @@ this.SocialService = {
|
||||
SocialServiceInternal.orderedProviders(onDone);
|
||||
},
|
||||
|
||||
getOriginActivationType: function(origin) {
|
||||
let prefname = SocialServiceInternal.getManifestPrefname(origin);
|
||||
if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
|
||||
return 'builtin';
|
||||
|
||||
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
|
||||
if (whitelist.indexOf(origin) >= 0)
|
||||
return 'whitelist';
|
||||
|
||||
let directories = Services.prefs.getCharPref("social.directories").split(',');
|
||||
if (directories.indexOf(origin) >= 0)
|
||||
return 'directory';
|
||||
|
||||
return 'foreign';
|
||||
getOriginActivationType: function (origin) {
|
||||
return getOriginActivationType(origin);
|
||||
},
|
||||
|
||||
_providerListeners: new Map(),
|
||||
@ -585,7 +592,7 @@ this.SocialService = {
|
||||
let sourceURI = aDOMDocument.location.href;
|
||||
let installOrigin = aDOMDocument.nodePrincipal.origin;
|
||||
|
||||
let installType = this.getOriginActivationType(installOrigin);
|
||||
let installType = getOriginActivationType(installOrigin);
|
||||
let manifest;
|
||||
if (data) {
|
||||
// if we get data, we MUST have a valid manifest generated from the data
|
||||
@ -649,8 +656,9 @@ this.SocialService = {
|
||||
*
|
||||
* @constructor
|
||||
* @param {jsobj} object representing the manifest file describing this provider
|
||||
* @param {bool} boolean indicating whether this provider is "built in"
|
||||
*/
|
||||
function SocialProvider(input) {
|
||||
function SocialProvider(input, blessed = false) {
|
||||
if (!input.name)
|
||||
throw new Error("SocialProvider must be passed a name");
|
||||
if (!input.origin)
|
||||
@ -674,6 +682,7 @@ function SocialProvider(input) {
|
||||
this.ambientNotificationIcons = {};
|
||||
this.errorState = null;
|
||||
this.frecency = 0;
|
||||
this.blessed = blessed;
|
||||
try {
|
||||
this.domain = etld.getBaseDomainFromHost(originUri.host);
|
||||
} catch(e) {
|
||||
@ -869,8 +878,12 @@ SocialProvider.prototype = {
|
||||
getWorkerPort: function getWorkerPort(window) {
|
||||
if (!this.workerURL || !this.enabled)
|
||||
return null;
|
||||
return getFrameWorkerHandle(this.workerURL, window,
|
||||
"SocialProvider:" + this.origin, this.origin).port;
|
||||
// Only allow localStorage in the frameworker for blessed providers
|
||||
let allowLocalStorage = this.blessed;
|
||||
let handle = getFrameWorkerHandle(this.workerURL, window,
|
||||
"SocialProvider:" + this.origin, this.origin,
|
||||
allowLocalStorage);
|
||||
return handle.port;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -241,7 +241,7 @@ let tests = {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage");
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true);
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check the localStorage test worked");
|
||||
@ -251,6 +251,30 @@ let tests = {
|
||||
}
|
||||
},
|
||||
|
||||
testNoLocalStorage: function(cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
try {
|
||||
localStorage.setItem("foo", "1");
|
||||
} catch(e) {
|
||||
port.postMessage({topic: "done", result: "ok"});
|
||||
return;
|
||||
}
|
||||
|
||||
port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" });
|
||||
}
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "done") {
|
||||
is(e.data.result, "ok", "check that retrieving localStorage fails by default");
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
testBase64: function (cbnext) {
|
||||
let run = function() {
|
||||
onconnect = function(e) {
|
||||
|
Loading…
Reference in New Issue
Block a user