From 69fccd83fdb70c7b68aa0fd5acb0cc2cc969cab5 Mon Sep 17 00:00:00 2001 From: Alexandre Poirot Date: Mon, 27 Oct 2014 11:17:40 -0400 Subject: [PATCH] Bug 1019714 - Forbid overloading apps not being pushed via devtools. r=jryans, r=fabrice --- dom/apps/AppsUtils.jsm | 1 + .../devtools/apps/tests/data/app-overload.zip | Bin 0 -> 526 bytes .../devtools/apps/tests/data/app-system.zip | Bin 0 -> 501 bytes .../devtools/apps/tests/data/mochitest.ini | 2 + .../apps/tests/debugger-protocol-helper.js | 12 ++ .../apps/tests/test_webapps_actor.html | 88 ++++++++-- .../apps/tests/unit/test_webappsActor.js | 16 ++ toolkit/devtools/server/actors/webapps.js | 164 +++++++++--------- 8 files changed, 193 insertions(+), 90 deletions(-) create mode 100644 toolkit/devtools/apps/tests/data/app-overload.zip create mode 100644 toolkit/devtools/apps/tests/data/app-system.zip diff --git a/dom/apps/AppsUtils.jsm b/dom/apps/AppsUtils.jsm index ef92d84e8a05..da801e4b2d2f 100644 --- a/dom/apps/AppsUtils.jsm +++ b/dom/apps/AppsUtils.jsm @@ -108,6 +108,7 @@ function _setAppProperties(aObj, aApp) { aObj.widgetPages = aApp.widgetPages || []; aObj.kind = aApp.kind; aObj.enabled = aApp.enabled !== undefined ? aApp.enabled : true; + aObj.sideloaded = aApp.sideloaded; } this.AppsUtils = { diff --git a/toolkit/devtools/apps/tests/data/app-overload.zip b/toolkit/devtools/apps/tests/data/app-overload.zip new file mode 100644 index 0000000000000000000000000000000000000000..ab0a190de825a50c53aa157b612337a9b9f7d679 GIT binary patch literal 526 zcmWIWW@Zs#U|`^2IFdTgi7D`)iaU@O3&dOuG7On{DXA5D86~+np&^_M%pVri2ZC^E z1vdjD%L`@(1~9QX(C4JT-bx*h&=VRb&u)CYsWEV6r)HU%>P%J3m1SnX45V~UrB0qQ zKPdC~(ghD@#7v4`5-=$$X3>O*fCa}>7xPbK)MA;<#1H^?ipOLl*RZ+KhwFjPSOvuV zK&Rv;=4Ga(7MJLirzRy96oB2coLxEugwfoi+Uw1CSb>N2`y8f?>k{T?Z~1+Sae4$( z&w;-#q3`brZ2Ro?d7oEyu)^ZVEj!=E8f34y8pFnaqjs^@k<>HhZ=6(LnXhZVWLoji z>@3UqN877eZ??T$!Sd&ZZ_%vXZ@$)ixX~ill9-ey@-j#M;8DxEL~ovhZVD&%1Ua47 zwOU;Fia)@ckx7mjSD;D&{lmb(2*g_&K`i7TW`zVXns<=R!weZ@^SXiNVT2RVP%L2- T;LXYgvYrVDmjdZvkc$`qjew>Y literal 0 HcmV?d00001 diff --git a/toolkit/devtools/apps/tests/data/app-system.zip b/toolkit/devtools/apps/tests/data/app-system.zip new file mode 100644 index 0000000000000000000000000000000000000000..3dbdedb8cafbc21377cecd74db25d8bea24cdda6 GIT binary patch literal 501 zcmWIWW@Zs#U|`^2IFdTgi7D`)iaU@O3&dOuG7On{DXA5D86~+np&^_M%pVri2ZC^E z1vdjD%L`@(1~9QX(C4JT-bx*h&=VRb&u)CYsWEV6r)HU%>P%J3m1SnX45V~UrB0qQ zKPdC~(ghD@#7v4`5-=$$X3>O*fCa}>7xPbK)MA;<#1H^?N&{1X^TH1+W~Tw2Q4hrY zK&Rv;=4Ga(7MJLirzRy96oB0`VSj882&1{j^sFJ*VFMnAi+7g`%yQnHepYms_|>p% z$3+~`s+>#9{pVizY5!wBLxY!aGQUMZVI`Nkg7KRuub5=P;+b1JrNhN*4<725U+Hl1 zdC{qf_oP1GJyAS4{Nj< { - return aAppType; + return resolution; }); } else { let manFile = aDir.clone(); manFile.append("manifest.webapp"); manFile.moveTo(installDir, "manifest.webapp"); } - return null; + return promise.resolve(resolution); } function readMetadata(aAppType) { if (aMetadata) { @@ -413,7 +416,7 @@ WebappsActor.prototype = { throw("Error parsing metadata.json."); } if (!aMetadata.origin) { - throw("Missing 'origin' property in metadata.json"); + throw("Missing 'origin' property in metadata.json."); } return { metadata: aMetadata, appType: aAppType }; }); @@ -421,9 +424,8 @@ WebappsActor.prototype = { let runnable = { run: function run() { try { + let metadata, appType; readManifest(). - then(writeManifest). - then(checkSideloading). then(readMetadata). then(function ({ metadata, appType }) { let origin = metadata.origin; @@ -438,6 +440,8 @@ WebappsActor.prototype = { receipts: aReceipts, }; + return writeManifest(app); + }).then(function (app) { self._registerApp(deferred, app, aId, aDir); }, function (error) { self._sendError(deferred, error, aId); @@ -483,6 +487,7 @@ WebappsActor.prototype = { manifest = JSON.parse(jsonString); } catch(e) { self._sendError(deferred, "Error Parsing manifest.webapp: " + e, aId); + return; } let appType = self._getAppType(manifest.type); @@ -505,6 +510,14 @@ WebappsActor.prototype = { id = uri.prePath.substring(6); } + // Prevent overriding preinstalled apps + if (id in DOMApplicationRegistry.webapps && + DOMApplicationRegistry.webapps[id].removable === false && + !self._isUnrestrictedAccessAllowed()) { + self._sendError(deferred, "The application " + id + " can't be overridden."); + return; + } + // Only after security checks are made and after final app id is computed // we can move application.zip to the destination directory, and // extract manifest.webapp there. @@ -586,9 +599,9 @@ WebappsActor.prototype = { // Check that we are not overriding a preinstalled application. if (appId in reg.webapps && reg.webapps[appId].removable === false) { - return { error: "badParameterType", - message: "The application " + appId + " can't be overriden." - } + return { error: "installationFailed", + message: "The application " + appId + " can't be overridden." + }; } let appDir = FileUtils.getDir("TmpD", ["b2g", appId], false, false); @@ -680,38 +693,36 @@ WebappsActor.prototype = { return { error: "appNotFound" }; } - return this._isAppAllowedForURL(app.manifestURL).then(allowed => { - if (!allowed) { - return { error: "forbidden" }; - } - return reg.getManifestFor(manifestURL).then(function (manifest) { - app.manifest = manifest; - return { app: app }; - }); + if (!this._isAppAllowedForURL(app.manifestURL)) { + return { error: "forbidden" }; + } + + return reg.getManifestFor(manifestURL).then(function (manifest) { + app.manifest = manifest; + return { app: app }; }); }, - _areCertifiedAppsAllowed: function wa__areCertifiedAppsAllowed() { + _isUnrestrictedAccessAllowed: function() { let pref = "devtools.debugger.forbid-certified-apps"; return !Services.prefs.getBoolPref(pref); }, - _isAppAllowedForManifest: function wa__isAppAllowedForManifest(aManifest) { - if (this._areCertifiedAppsAllowed()) { + _isAppAllowed: function(aApp) { + if (this._isUnrestrictedAccessAllowed()) { return true; } - let type = this._getAppType(aManifest.type); - return type !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED; + return aApp.sideloaded; }, _filterAllowedApps: function wa__filterAllowedApps(aApps) { - return aApps.filter(app => this._isAppAllowedForManifest(app.manifest)); + return aApps.filter(app => this._isAppAllowed(app)); }, _isAppAllowedForURL: function wa__isAppAllowedForURL(aManifestURL) { - return this._findManifestByURL(aManifestURL).then(manifest => { - return this._isAppAllowedForManifest(manifest); - }); + let reg = DOMApplicationRegistry; + let app = reg.getAppByManifestURL(aManifestURL); + return this._isAppAllowed(app); }, uninstall: function wa_actorUninstall(aRequest) { @@ -719,8 +730,12 @@ WebappsActor.prototype = { let manifestURL = aRequest.manifestURL; if (!manifestURL) { - return Promise.resolve({ error: "missingParameter", - message: "missing parameter manifestURL" }); + return { error: "missingParameter", + message: "missing parameter manifestURL" }; + } + + if (!this._isAppAllowedForURL(manifestURL)) { + return { error: "forbidden" }; } return DOMApplicationRegistry.uninstall(manifestURL); @@ -882,17 +897,12 @@ WebappsActor.prototype = { if (apps.indexOf(manifestURL) != -1) { continue; } - - appPromises.push(this._isAppAllowedForURL(manifestURL).then(allowed => { - if (allowed) { - apps.push(manifestURL); - } - })); + if (this._isAppAllowedForURL(manifestURL)) { + apps.push(manifestURL); + } } - return promise.all(appPromises).then(() => { - return { apps: apps }; - }); + return { apps: apps }; }, getAppActor: function ({ manifestURL }) { @@ -927,32 +937,30 @@ WebappsActor.prototype = { return notFoundError; } - return this._isAppAllowedForURL(manifestURL).then(allowed => { - if (!allowed) { - return notFoundError; - } + if (!this._isAppAllowedForURL(manifestURL)) { + return notFoundError; + } - // Only create a new actor, if we haven't already - // instanciated one for this connection. - let map = this._appActorsMap; - let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner) - .frameLoader - .messageManager; - let actor = map.get(mm); - if (!actor) { - let onConnect = actor => { - map.set(mm, actor); - return { actor: actor }; - }; - let onDisconnect = mm => { - map.delete(mm); - }; - return DebuggerServer.connectToChild(this.conn, appFrame, onDisconnect) - .then(onConnect); - } + // Only create a new actor, if we haven't already + // instanciated one for this connection. + let map = this._appActorsMap; + let mm = appFrame.QueryInterface(Ci.nsIFrameLoaderOwner) + .frameLoader + .messageManager; + let actor = map.get(mm); + if (!actor) { + let onConnect = actor => { + map.set(mm, actor); + return { actor: actor }; + }; + let onDisconnect = mm => { + map.delete(mm); + }; + return DebuggerServer.connectToChild(this.conn, appFrame, onDisconnect) + .then(onConnect); + } - return { actor: actor }; - }); + return { actor: actor }; }, watchApps: function () { @@ -988,14 +996,12 @@ WebappsActor.prototype = { return; } - this._isAppAllowedForURL(manifestURL).then(allowed => { - if (allowed) { - this.conn.send({ from: this.actorID, - type: "appOpen", - manifestURL: manifestURL - }); - } - }); + if (this._isAppAllowedForURL(manifestURL)) { + this.conn.send({ from: this.actorID, + type: "appOpen", + manifestURL: manifestURL + }); + } }, onFrameDestroyed: function (frame, isLastAppFrame) { @@ -1010,14 +1016,12 @@ WebappsActor.prototype = { return; } - this._isAppAllowedForURL(manifestURL).then(allowed => { - if (allowed) { - this.conn.send({ from: this.actorID, - type: "appClose", - manifestURL: manifestURL - }); - } - }); + if (this._isAppAllowedForURL(manifestURL)) { + this.conn.send({ from: this.actorID, + type: "appClose", + manifestURL: manifestURL + }); + } }, observe: function (subject, topic, data) {