Closed Bug 794407 Opened 11 years ago Closed 5 years ago

Web Activities should only be started by a user interaction

Categories

(Core :: DOM: Core & HTML, defect, P2)

defect

Tracking

()

RESOLVED INCOMPLETE
mozilla19
blocking-basecamp -
Tracking Status
firefox18 --- fixed

People

(Reporter: mounir, Unassigned)

References

Details

(Keywords: sec-low)

Attachments

(3 files, 2 obsolete files)

That was part of the security discussion we had and it seems this hasn't been done: Web Activities shouldn't be started if not from a user interaction. It will prevent a lot of possible attacks (or will at least make them harder).
Summary: Web Activites can only be started by a user interaction → Web Activites should only be started by a user interaction
Mounir, do we already have other features using this kind of check?
Popup, but it's more complex than simply user interaction. getUserMedia(). Soon browser id should do that. Those are only the one I know.
blocking-basecamp: ? → +
Mounir, do you have time to take a look at this?
Assignee: nobody → mounir
Isn't this check something similar to what  nsContentUtils::IsRequestFullScreenAllowed() does?

Although that returns true on any user interaction, so an onFocus even can bypass the check (tested it myself), which wouldn't be good enough for web activities. 

In any case, and FWIW, I wouldn't mind much this not being done at all if certified apps (which are the ones that can be dangerous, and I'm thinking about the dialer and the sms app specifically here) enforce not taking any action on a web activity unless the user approves it (hits the 'dial' button, the 'send' button, or the whatever thing before the action happens).

[1] https://mxr.mozilla.org/mozilla-central/source/content/base/src/nsContentUtils.cpp#6795 does
Do you want us to take this if Mounir doesn't have time?
I repeat the question :) Do you wish me to take this?
Mounir told me on IRC that he has time to work on this and is doing so.  Thanks for the offer!  Sorry, I should have mentioned it here.
Depends on: 798298
Keywords: sec-low
Attached patch PatchSplinter Review
Attachment #668495 - Flags: review?(fabrice)
Whiteboard: [need review]
Just out of curiosity (and quite possibly, my own ignorance), why can't you just add the nsEventStateManager::IsHandlingUserInput() on the Activity constructor in /dom/activities/src/Activity.cpp and return the error there instead of calling it from Javascript later on?
Comment on attachment 668495 [details] [diff] [review]
Patch

Review of attachment 668495 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/activities/src/ActivityProxy.js
@@ +43,5 @@
> +    if (!util.handlingUserInput) {
> +      debug("The call was not made from a user input handler");
> +      let runnable = {
> +        run: function run() {
> +          Services.DOMRequest.fireError(aActivity, "USER_INPUT");

Nit: Maybe "NOT_USER_INPUT" as an error message?
Attachment #668495 - Flags: review?(fabrice) → review+
DOMErrors should use InitCaps as naming convention, like Exceptions.

I think NotUserAction might be appropriate here since it's not just user input that is allowed to start an activity.
(In reply to Antonio Manuel Amaya Calvo from comment #9)
> Just out of curiosity (and quite possibly, my own ignorance), why can't you
> just add the nsEventStateManager::IsHandlingUserInput() on the Activity
> constructor in /dom/activities/src/Activity.cpp and return the error there
> instead of calling it from Javascript later on?

There is no particular reason. Actually, I did that mostly because I didn't want to handle the runnable from C++ (which would have require adding a static method, etc.) but I've just seen we have a helper to send async event for DOMRequest.
I just wrote a new patch doing the check in the C++ code. It saves a few cycles.
(In reply to Jonas Sicking (:sicking) from comment #11)
> DOMErrors should use InitCaps as naming convention, like Exceptions.
> 
> I think NotUserAction might be appropriate here since it's not just user
> input that is allowed to start an activity.

Actually, I thought something was wrong by using "FOO_BAR" syntax but I did that because most of our code is currently doing that, unfortunately :(
Attached patch Patch (obsolete) — Splinter Review
Alternative patch in C++. Fabrice, let me know if you would prefer the JS version.
Attachment #669103 - Flags: review?(fabrice)
Is the second patch correct? It's JavaScript still.
Attached patch PatchSplinter Review
Oups, I forgot to qref my patch before uploading.
Thank you for the heads up Carmen.
Attachment #669103 - Attachment is obsolete: true
Attachment #669103 - Flags: review?(fabrice)
Attachment #669121 - Flags: review?(fabrice)
Attachment #669121 - Flags: review?(fabrice) → review+
https://hg.mozilla.org/integration/mozilla-inbound/rev/2a0be10848c3
Status: NEW → ASSIGNED
Flags: in-testsuite?
Whiteboard: [need review]
Target Milestone: --- → mozilla19
I had to backout because Gaia's homescreen is launched without any user interaction at startup trough a Web Activity.

Vivien, can you think of a way to fix that? (except allowing certified apps to start web activities whenever they want)

https://hg.mozilla.org/integration/mozilla-inbound/rev/582b256202d1
I think a better question would be why is the homescreen being launched as a Web Activity instead of just being launched as an app?

And what's going to happen when someone adds

  "activities": {
    "view": {
      "filters": {
        "type": "application/x-application-list"
      },
      "href": "/index.html"
    }
  }

to their app manifest?

What currently happens (I tried it) is that the first time you unlock the device you get a nice black screen asking you to choose between two apps called Homescreen (I called my rogue app the same way) and then you have a 50% chance of choosing the wrong one and having to reboot the device to get rid of it.
Indeed. We should not be using an activity here.
I think it's fine to use an activity, this let people change the homescreen. What we lack is a "remember my choice" in the activity picker.
The current homescreen asks for some permissions that are granted for certified apps only. That means that any drop-in app, even if it's privileged, won't have the full functionality of the current one. 

In any case, if what's wanted is a way for users to replace their homescreen app, a mechanism should be designed that it's user initiated, so the change is the result of a conscious decision by the user, and not something forced upon him by any rogue app.

As the system is now, even if a 'remember my choice' was added the change would not be user initiated, nor the result of a conscious decision by the user. Any application the user installs can try to set itself as homescreen the next time the device boots, without telling the user it's going to do so. The first time the device boots after all, the user will be presented with an option he didn't request.

At least it looks like very bad UX to me... and I'm not even going to enter on any security considerations :)
It's kind of user initiated in the sense that the user will install a new app. And the system app could check which activity is offered and warn or whatever.

Can we focus on finding a solution here instead of just saying how bad the current solution is?
I agree that to support replacing the homescreen app we need a more comprehensive solution than a simple activity.

It's very scary that a couple of simple clicks, "remember this decision" and "select something from an activity picker" can essentially brick the device.

If you install a homescreen that isn't a fully functional homescreen you won't be able to get to the settings app to revert it, or even get to the factory reset function.

Until we have a more comprehensive solution for homescreen, I don't think we should be using an activity here.
Just fyi, this is very possible on android to replace your homescreen this way. Not sure how many people got bricked though.

Also, we still can do a factory reset from the recovery mode of the device usually (eg booting with vol down + power on otoro/unagi) to restore only the standard apps.
I still don't think we should use activities here. It's too easy to get into a state which is too hard to get out of.
So if we don't want to hardcode the name of the homescreen app in the system app, can we just use a setting to store it? (we would need some UI in the settings app to change it, but that can wait)
Using a setting which contains the manifestURL of the homescreen app sounds like the right solution yeah.
Attached patch wip gaia patch (obsolete) — Splinter Review
Tim, feedback welcome on this approach to use a setting to start the homescreen instead of an activity.
Attachment #669412 - Flags: feedback?(timdream+bugs)
Does this also make sure that the trustedUI code for mozpay renders the homescreen without using the activity?

To be on the safe side we should probably remove the activity handler from the homescreen app.
Updated gaia patch to remove all uses of the application/x-application-list activity.
Attachment #669412 - Attachment is obsolete: true
Attachment #669412 - Flags: feedback?(timdream+bugs)
Attachment #669417 - Flags: feedback?(timdream+bugs)
Comment on attachment 669417 [details] [diff] [review]
wip gaia patch v2

>diff --git a/apps/homescreen/js/homescreen.js b/apps/homescreen/js/homescreen.js
>index d7fc340..5b12685 100644
>--- a/apps/homescreen/js/homescreen.js
>+++ b/apps/homescreen/js/homescreen.js
>@@ -24,17 +24,6 @@ const Homescreen = (function() {
>     });
>   }
> 
>-  function onHomescreenActivity() {
>-    if (Homescreen.isInEditMode()) {
>-      Homescreen.setMode('normal');
>-      GridManager.saveState();
>-      DockManager.saveState();
>-      Permissions.hide();
>-    } else {
>-      GridManager.goToPage(1);
>-    }
>-  }
>-

We will need to pass home key press into home screen app and invoke this function.

>   function setLocale() {
>     // set the 'lang' and 'dir' attributes to <html> when the page is translated
>     document.documentElement.lang = navigator.mozL10n.language.code;
>@@ -86,9 +75,6 @@ const Homescreen = (function() {
>           case 'url':
>             BookmarkEditor.init(data);
>             break;
>-          case 'application/x-application-list':
>-            onHomescreenActivity();
>-            break;
>         }
>       });
>   }
>diff --git a/apps/homescreen/manifest.webapp b/apps/homescreen/manifest.webapp
>index d9cf65d..c43c768 100644
>--- a/apps/homescreen/manifest.webapp
>+++ b/apps/homescreen/manifest.webapp
>@@ -1,6 +1,7 @@
> {
>   "name": "Homescreen",
>   "description": "Gaia Homescreen",
>+  "launch_path": "/index.html",

This will make home screen app show up as an icon on the home screen grid. I have no solution to this problem.


>   "developer": {
>     "name": "The Gaia Team",
>     "url": "https://github.com/andreasgal/gaia"
>@@ -70,12 +71,6 @@
>         "type": "url"
>       },
>       "href": "/index.html"
>-    },
>-    "view": {
>-      "filters": {
>-        "type": "application/x-application-list"
>-      },
>-      "href": "/index.html"
>     }
>   }
> }
>diff --git a/apps/system/js/bootstrap.js b/apps/system/js/bootstrap.js
>index 889574f..0b49761 100644
>--- a/apps/system/js/bootstrap.js
>+++ b/apps/system/js/bootstrap.js
>@@ -5,13 +5,21 @@
> 
> function startup() {
>   function launchHomescreen() {
>-    var activity = new MozActivity({
>-      name: 'view',
>-      data: { type: 'application/x-application-list' }
>-    });
>-    activity.onerror = function homescreenLaunchError() {
>-      console.error('Failed to launch home screen with activity.');
>-    };
>+    // get the homescreen manifest URL from settings, and use it to
>+    // launch the app.
>+    var lock = navigator.mozSettings.createLock();
>+    var setting = lock.get('homescreen.manifestURL');
>+    setting.onsuccess = function(event) {
>+      var app = Applications.getByManifestURL(this.result['homescreen.manifestURL']);
>+      if (app) {
>+        WindowManager.ensureHomescreen(app);

Please please don't expose |ensureHomescreen| as a public method of the WindowManager.
Use 

var app = Applications.getByManifestURL(manifestURL);
app.launch();

instead.

>+      } else {
>+        // XXX what can we do if we have no homescreen?
>+      }
>+    }
>+    setting.onerror = function(event) {
>+      // XXX what can we do if we have no homescreen?
>+    }
>   }
> 
>   if (Applications.ready) {
>diff --git a/apps/system/js/permission_manager.js b/apps/system/js/permission_manager.js
>index 55df78c..0456970 100644
>--- a/apps/system/js/permission_manager.js
>+++ b/apps/system/js/permission_manager.js
>@@ -57,8 +57,9 @@ var PermissionManager = (function() {
>       return;
>     }
> 
>-    var name = app.manifest.name;
>-    var locales = app.manifest.locales;
>+    var manifest = app.manifest || app.updateManifest;
>+    var name = manifest.name;
>+    var locales = manifest.locales;
>     var lang = navigator.language;
>     if (locales && locales[lang] && locales[lang].name)
>       name = locales[lang].name;
>diff --git a/apps/system/js/window_manager.js b/apps/system/js/window_manager.js
>index 739043d..ac7bffe 100644
>--- a/apps/system/js/window_manager.js
>+++ b/apps/system/js/window_manager.js
>@@ -52,7 +52,7 @@
> 
> var WindowManager = (function() {
>   // Holds the origin of the home screen, which should be the first
>-  // app we launch through web activity during boot
>+  // app we launch
>   var homescreen = null;
>   var homescreenURL = '';
>   var homescreenManifestURL = '';
>@@ -641,7 +641,13 @@ var WindowManager = (function() {
> 
>   // Ensure the homescreen is loaded and return its frame.  Restarts
>   // the homescreen app if it was killed in the background.
>-  function ensureHomescreen() {
>+  function ensureHomescreen(homeApp) {
>+    if (homeApp) {
>+      homescreenManifestURL = homeApp.manifestURL;
>+      homescreen = homeApp.origin;
>+      homescreenURL = homeApp.origin + (homeApp.manifest.launch_path || ""); // XXX do proper uri resolution
>+    }
>+


Undo these changes as we are not exposing |ensureHomescreen| to do these task.


>     if (!isRunning(homescreen)) {
>       var app = Applications.getByManifestURL(homescreenManifestURL);
>       appendFrame(null, homescreen, homescreenURL,
>@@ -1079,18 +1085,6 @@ var WindowManager = (function() {
>         if (!e.detail.isActivity)
>           return;
> 
>-        // If nothing is opened yet, consider the first application opened
>-        // as the homescreen.
>-        if (!homescreen) {
>-          homescreen = origin;
>-          addWrapperListener();
>-          // Save the entry manifest URL and launch URL so that we can restart
>-          // the homescreen later, if necessary.
>-          homescreenURL = e.detail.url;
>-          homescreenManifestURL = manifestURL;
>-          return;
>-        }
>-


Move this part to 'webapps-launch' chromeEvent handling so |homescreen| will be remembered, instead of removing it.

You can also move the entire |launchHomescreen()| into WindowManager itself, but in order to avoid doing URI resolution ourselves I will still call |app.launch()| and handle the chromeEvent.


>         // XXX: the correct way would be for UtilityTray to close itself
>         // when there is a appwillopen/appopen event.
>         UtilityTray.hide();
>@@ -1290,12 +1284,14 @@ var WindowManager = (function() {
>     } else if (displayedApp !== homescreen) {
>       setDisplayedApp(homescreen);
>     } else {
>-      new MozActivity({
>-        name: 'view',
>-        data: {
>-          type: 'application/x-application-list'
>+      var lock = navigator.mozSettings.createLock();
>+      var setting = lock.get('homescreen.manifestURL');
>+      setting.onsuccess = function(event) {
>+        var app = Applications.getByManifestURL(this.result['homescreen.manifestURL']);
>+        if (app) {
>+          ensureHomescreen(app);
>         }
>-      });
>+      }

The activity fired here is for passing the home screen key press into the home screen app itself.

Running |ensureHomescreen| again does not do that.

>     }
>   });
> 
>@@ -1348,7 +1344,8 @@ var WindowManager = (function() {
>     setDisplayedApp: setDisplayedApp,
>     getCurrentDisplayedApp: function() {
>       return runningApps[displayedApp];
>-    }
>+    },
>+    ensureHomescreen: ensureHomescreen
>   };
> }());
> 
>diff --git a/build/settings.js b/build/settings.js
>index f2b8638..c56f53e 100644
>--- a/build/settings.js
>+++ b/build/settings.js
>@@ -10,6 +10,7 @@ dump("Populate settingsdb in:" + PROFILE_DIR + "\n");
> 
> // Todo: Get a list of settings
> var settings = [
>+ new Setting("homescreen.manifestURL", "app://homescreen.gaiamobile.org/manifest.webapp"),


Same question rises when we were working on another bug: is manifest URL is enough to identify the "app"? What if an home screen app offers 3 home screens in 3 different launch paths?


>  new Setting("alarm.enabled", false),
>  new Setting("accessibility.invert", false),
>  new Setting("accessibility.screenreader", false),
Attachment #669417 - Flags: feedback?(timdream+bugs) → feedback+
Comment on attachment 669417 [details] [diff] [review]
wip gaia patch v2

Vivien, do you have solutions on the two issues I rose in the feedback comment?
Attachment #669417 - Flags: feedback?(21)
I guess that we have the same solution for different use cases. With this activity we tries to indicate to the system: launch homescreen and furthermore from homescreen point of view we are listening for clicks on HOME button as Tim commented correctly. Well, IMHO, I don't have idea how to run the home on startup, via activity, via method, or whatever but we could expose a event or a new Activity with other name in order to know when the home button is clicked. Third apps could listen for this event or activity or not. but..... it's is not important, they cannot do anything with it I guess.
uff sorry for my spelling mistakes, how embarrassing!
Actually, passing the homescreen button click to the homescreen app via a web activity is, also, something that should not be done. And for the same reason. If any other app registers the same web activity, when we press the homescreen the activity picker will come out. Again, not a very good UX, and depending on how absent-minded the user is can cause security issues.

Oh and...


(In reply to Fabrice Desré [:fabrice] from comment #23)
> Can we focus on finding a solution here instead of just saying how bad the
> current solution is?

I'm sorry about that, I wasn't saying how bad the current solution was... I was asking for it to be replaced with what's being implemented now :)
(In reply to Tim Guan-tin Chien [:timdream] (MoCo-TPE) from comment #33)
> Comment on attachment 669417 [details] [diff] [review]
> wip gaia patch v2
> 
> Vivien, do you have solutions on the two issues I rose in the feedback
> comment?

I don't. I would have kept the code with the activity and send a trusted MozChromeEvent at some point in order to let the system app launch the homescreen. Or the chrome could sent the activity itself at startup.

Also the default choice of such activity should be configured to be 'remembered' so if an app will not be able to pop in the user face except if the user has explicitly removed it's default homescreen choice from Settings.
(In reply to Antonio Manuel Amaya Calvo from comment #36)
> Actually, passing the homescreen button click to the homescreen app via a
> web activity is, also, something that should not be done. 

The click is not really passed since it can not be cancelled. So I don't think there is any security risks here.

> And for the same
> reason. If any other app registers the same web activity, when we press the
> homescreen the activity picker will come out. Again, not a very good UX, and
> depending on how absent-minded the user is can cause security issues.
> 

If there is a default choice that is pre-configured that won't happen and it's hard to me to thing about any security risks this will bring.
(In reply to Vivien Nicolas (:vingtetun) from comment #38)
> (In reply to Antonio Manuel Amaya Calvo from comment #36)
> > Actually, passing the homescreen button click to the homescreen app via a
> > web activity is, also, something that should not be done. 
> 
> The click is not really passed since it can not be cancelled. So I don't
> think there is any security risks here.

The click is not passed, but the way the information that the user has pressed the homescreen button is passed to the homescreen via a web activity, if I read the previous comments correctly.

> 
> > And for the same
> > reason. If any other app registers the same web activity, when we press the
> > homescreen the activity picker will come out. Again, not a very good UX, and
> > depending on how absent-minded the user is can cause security issues.
> > 
> 
> If there is a default choice that is pre-configured that won't happen and
> it's hard to me to thing about any security risks this will bring.

If there's a fixed (I think the term 'default' is misleading here, since it cannot be overridden without removing the setting or setting it to something else) choice configured then I agree, it won't be a problem, just a convoluted way to send messages to a fixed app. 

But I have some questions about this...

* Is that mechanism (selecting a fixed target for a web activity) implemented?
* Is the possibility to select a custom homescreen a requisite for V1?
Couldn't the homescreen app simply not display itself? I.e. it's the homescreen app which is listing all the apps, it can simply choose to not skip displaying anything when it sees its own manifestURL
(In reply to Jonas Sicking (:sicking) from comment #40)
> Couldn't the homescreen app simply not display itself? I.e. it's the
> homescreen app which is listing all the apps, it can simply choose to not
> skip displaying anything when it sees its own manifestURL

yep, using getSelf() for that is the way to go.

Tim, would you have time to finish the gaia patch? I'm busy with other pieces I'm more familiar with.
Tim or Vivien, could you open a Gaia bug and mark is a blocking this one?
Whiteboard: [has reviewed patch][blocked by Gaia]
Keywords: dev-doc-needed
Target Milestone: mozilla19 → ---
Priority: -- → P1
(In reply to Mounir Lamouri (:mounir) from comment #42)
> Tim or Vivien, could you open a Gaia bug and mark is a blocking this one?

https://bugzilla.mozilla.org/show_bug.cgi?id=801995
No longer depends on: 801995
Comment on attachment 669417 [details] [diff] [review]
wip gaia patch v2

Review of attachment 669417 [details] [diff] [review]:
-----------------------------------------------------------------

::: apps/homescreen/js/homescreen.js
@@ -33,5 @@
> -    } else {
> -      GridManager.goToPage(1);
> -    }
> -  }
> -

You need have something for that. The dirtiest thing I can imagine that would let you get rid of the activity is to call a
Attachment #669417 - Flags: feedback?(21)
Vivien, how bad would that be to push this patch with bug 804966 not being fixed? I feel like it would be better to push this patch sooner than later to make sure we don't find problems too late.
Flags: needinfo?(21)
This would break the phone for dogfooders that use pin code. Let's see how fast Alexandre can fix the SIM pin ui related bug.
Flags: needinfo?(21) needinfo?(21) → needinfo+
Summary: Web Activites should only be started by a user interaction → Web Activities should only be started by a user interaction
Pushed on m-i:
https://hg.mozilla.org/integration/mozilla-inbound/rev/d7cddf2126ca

and m-a:
https://hg.mozilla.org/releases/mozilla-aurora/rev/7a12a2175083
Whiteboard: [has reviewed patch][blocked by Gaia]
Target Milestone: --- → mozilla19
https://hg.mozilla.org/mozilla-central/rev/d7cddf2126ca
Status: ASSIGNED → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Depends on: 813828
I think we need to back this bug out. There's simply too much stuff breaking because of this change. And there's too many things that want to do asynchronous actions before they can launch an activity.

I think just limiting activities to the foreground app is the best thing we can do for now :(
Depends on: 815561
(In reply to Jonas Sicking (:sicking) from comment #49)

> I think just limiting activities to the foreground app is the best thing we
> can do for now :(
Note that this won't actually fix the original problem this bug intended to mitigate: that activities are launched only when the user is actually interacting/looking at the phone and thus he would be aware of anything untoward/unexpected happening. 

Limiting activities to the foreground app doesn't actually mitigate that risk. A mallicious app can just wait for x seconds without interaction before launching the activity.
That's just a defence-in-depth issue since activities should never do anything harmful without the handler app communicating very clearly to the user what is about to happen.

So not a blocking issue.

But yes, it would be better from a security point of view to have this bug fixed.

Reopening since this was backed out.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Was this backed out on m-c, aurora, and beta?  What are the csets?
It was backed out in bug 815561. Only m-c so far.
Heh, 40 minutes after you wrote that comment it was backed out on beta.
I guess this is no longer a blocker.
Assignee: mounir → nobody
blocking-basecamp: + → ?
Flags: in-testsuite?
blocking-basecamp: ? → -
No longer blocks: 813384
No longer blocks: 814119
Component: DOM: Mozilla Extensions → DOM
Severity: normal → major
Priority: P1 → P2
Web Activities was a Mozilla/B2G only API and has since removed from the code base.
Status: REOPENED → RESOLVED
Closed: 11 years ago5 years ago
Resolution: --- → INCOMPLETE
Component: DOM → DOM: Core & HTML
You need to log in before you can comment on or make changes to this bug.