You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cordova.apache.org by Erik Jan de Wit <ed...@redhat.com> on 2014/07/10 13:48:58 UTC

Re: Android Plugin API

Hi all,

Created an plugin that does this: https://github.com/edewit/aerogear-reflect-cordova the repository also includes a example plugin that uses this. Also have a blog describing this a bit more http://blog.nerdin.ch/2014/07/improved-cordova-android-plugin-api.html

Cheers,
	Erik Jan

On 2 Jun,2014, at 4:20 , Terence M. Bandoian <te...@tmbsw.com> wrote:

> It does get complicated in a hurry. Using the technique in the Google Maps plugin:
> 
> - getDeclaredMethod is documented to NOT return inherited methods which limits exposure
> - the signature passed to getDeclaredMethod also limits exposure
> - annotations, as Andrew suggested, could further limit exposure
> 
> - getDeclaredMethod is documented to return private methods which for me raises questions
> - setAccessible(true) suppresses normal access checking which also raises questions
> 
> - invoke throws InvocationTargetException so exceptions thrown by the invoked method could be publicized
> - invoke applies overriding which is probably a wash
> - invoke returns an object which would have to be handled correctly
> 
> Quite a bit to consider.
> 
> -Terence Bandoian
> 
> 
> On 5/29/2014 11:44 PM, Joe Bowser wrote:
>> On Thu, May 29, 2014 at 9:26 PM, Terence M. Bandoian <te...@tmbsw.com> wrote:
>>> Please correct me if I'm wrong but, as I understand it, the vulnerability
>>> stems from injecting a Java object into the WebView which, in API levels 16
>>> and below, exposed all of the public methods of the object (small 'o')
>>> including the methods inherited from the Object class.
>>> 
>> Yes, you are correct in this case.  You can basically use the object
>> methods to reflect into whatever you want.  Once you have a single
>> class exposed in this manner, you can then reflect into it, and
>> basically you're done.  The reason we don't use reflection is that
>> it's very easy to reflect into things you're not supposed to be in.
>> IMO, It wouldn't be hard to do this if we exposed plugins in the same
>> way.  Right now this is an Android Vulnerability, but if we start
>> using reflection for plugins, we could very quickly end up making a
>> similar Cordova exploit if we're not careful.
>> 
>> This is also why we use the prompt bridge on API levels below 17.
>> 
>>> 
>>> 
>>> On 5/28/2014 9:54 AM, Joe Bowser wrote:
>>>> In case anyone is curious, here's why we minimize reflection:
>>>> 
>>>> 
>>>> https://labs.mwrinfosecurity.com/blog/2013/09/24/webview-addjavascriptinterface-remote-code-execution/
>>>> 
>>>> On Wed, May 28, 2014 at 7:33 AM, Andrew Grieve <ag...@chromium.org>
>>>> wrote:
>>>>> Another reasonable approach would be to use a Map<String, Runnable>, but
>>>>> that can be implemented on top of what is currently exposed. I'm quite
>>>>> wary
>>>>> of Reflection as well.
>>>>> 
>>>>> 
>>>>> On Wed, May 28, 2014 at 10:06 AM, Joe Bowser <bo...@gmail.com> wrote:
>>>>> 
>>>>>> The execute command exists for security reasons.  We don't want any
>>>>>> methods other than execute exposed to Javascript.  I also prefer this
>>>>>> approach because it is less prone to less catastrophic bugs than using
>>>>>> Java reflection.  We try and only use reflection when we have to.
>>>>>> 
>>>>>> On Wed, May 28, 2014 at 5:50 AM, Erik Jan de Wit <ed...@redhat.com>
>>>>>> wrote:
>>>>>>> Hi,
>>>>>>> 
>>>>>>> When one is writing a plugin for android ATM the api that you have to
>>>>>> implement has a execute method that has the action as a string:
>>>>>>> @Override
>>>>>>>      public boolean execute(String action, JSONArray args,
>>>>>> CallbackContext callbackContext) throws JSONException {
>>>>>>>          if ("beep".equals(action)) {
>>>>>>>              this.beep(args.getLong(0));
>>>>>>>              callbackContext.success();
>>>>>>>              return true;
>>>>>>>          }
>>>>>>>          return false;  // Returning false results in a
>>>>>>> "MethodNotFound"
>>>>>> error.
>>>>>>>      }
>>>>>>> When you have multiple actions this method gets very long, if you
>>>>>> compare this with iOS here you don’t need a method like this you could
>>>>>> ‘just’ implement the method directly:
>>>>>>> - (void)beep:(CDVInvokedUrlCommand*)command
>>>>>>>      {
>>>>>>>          CDVPluginResult* pluginResult =;
>>>>>>>          NSString* myarg =mmand.arguments objectAtIndex:0];
>>>>>>> 
>>>>>>>          if (myarg !=) {
>>>>>>>              pluginResult �VPluginResult
>>>>>> resultWithStatus:CDVCommandStatus_OK];
>>>>>>>          } else {
>>>>>>>              pluginResult �VPluginResult
>>>>>> resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was
>>>>>> null"];
>>>>>>>          }
>>>>>>>          [self.commandDelegate sendPluginResult:pluginResult
>>>>>> callbackId:command.callbackId];
>>>>>>>      }
>>>>>>> We could do the same thing for android if we use reflection, making the
>>>>>> API more similar and removing all the string test by the user. What do
>>>>>> you
>>>>>> think?
>>>>>>> Cheers,
>>>>>>>          Erik Jan


Re: Android Plugin API

Posted by Joe Bowser <bo...@gmail.com>.
We won't be breaking plugins. I don't want to move to this for the reasons
stated before. This code also is way less maintainable, and looks like it
could break easily.  I really think that clarity should win out here over
an abuse of java reflection.

On 10 Jul,2014, at 17:15 , Anis KADRI <an...@gmail.com> wrote:

> I want to say that this should be default...but we won't change the API
> again and won't break plugins again.

Both could be supported at the same time, just by using a different base
class

Re: Android Plugin API

Posted by Erik Jan de Wit <ed...@redhat.com>.
On 10 Jul,2014, at 17:15 , Anis KADRI <an...@gmail.com> wrote:

> I want to say that this should be default...but we won't change the API
> again and won't break plugins again.

Both could be supported at the same time, just by using a different base class

Re: Android Plugin API

Posted by Anis KADRI <an...@gmail.com>.
I want to say that this should be default...but we won't change the API
again and won't break plugins again.


On Thu, Jul 10, 2014 at 7:48 AM, Erik Jan de Wit <ed...@redhat.com> wrote:

> Hi all,
>
> Created an plugin that does this:
> https://github.com/edewit/aerogear-reflect-cordova the repository also
> includes a example plugin that uses this. Also have a blog describing this
> a bit more
> http://blog.nerdin.ch/2014/07/improved-cordova-android-plugin-api.html
>
> Cheers,
>         Erik Jan
>
> On 2 Jun,2014, at 4:20 , Terence M. Bandoian <te...@tmbsw.com> wrote:
>
> > It does get complicated in a hurry. Using the technique in the Google
> Maps plugin:
> >
> > - getDeclaredMethod is documented to NOT return inherited methods which
> limits exposure
> > - the signature passed to getDeclaredMethod also limits exposure
> > - annotations, as Andrew suggested, could further limit exposure
> >
> > - getDeclaredMethod is documented to return private methods which for me
> raises questions
> > - setAccessible(true) suppresses normal access checking which also
> raises questions
> >
> > - invoke throws InvocationTargetException so exceptions thrown by the
> invoked method could be publicized
> > - invoke applies overriding which is probably a wash
> > - invoke returns an object which would have to be handled correctly
> >
> > Quite a bit to consider.
> >
> > -Terence Bandoian
> >
> >
> > On 5/29/2014 11:44 PM, Joe Bowser wrote:
> >> On Thu, May 29, 2014 at 9:26 PM, Terence M. Bandoian <te...@tmbsw.com>
> wrote:
> >>> Please correct me if I'm wrong but, as I understand it, the
> vulnerability
> >>> stems from injecting a Java object into the WebView which, in API
> levels 16
> >>> and below, exposed all of the public methods of the object (small 'o')
> >>> including the methods inherited from the Object class.
> >>>
> >> Yes, you are correct in this case.  You can basically use the object
> >> methods to reflect into whatever you want.  Once you have a single
> >> class exposed in this manner, you can then reflect into it, and
> >> basically you're done.  The reason we don't use reflection is that
> >> it's very easy to reflect into things you're not supposed to be in.
> >> IMO, It wouldn't be hard to do this if we exposed plugins in the same
> >> way.  Right now this is an Android Vulnerability, but if we start
> >> using reflection for plugins, we could very quickly end up making a
> >> similar Cordova exploit if we're not careful.
> >>
> >> This is also why we use the prompt bridge on API levels below 17.
> >>
> >>>
> >>>
> >>> On 5/28/2014 9:54 AM, Joe Bowser wrote:
> >>>> In case anyone is curious, here's why we minimize reflection:
> >>>>
> >>>>
> >>>>
> https://labs.mwrinfosecurity.com/blog/2013/09/24/webview-addjavascriptinterface-remote-code-execution/
> >>>>
> >>>> On Wed, May 28, 2014 at 7:33 AM, Andrew Grieve <ag...@chromium.org>
> >>>> wrote:
> >>>>> Another reasonable approach would be to use a Map<String, Runnable>,
> but
> >>>>> that can be implemented on top of what is currently exposed. I'm
> quite
> >>>>> wary
> >>>>> of Reflection as well.
> >>>>>
> >>>>>
> >>>>> On Wed, May 28, 2014 at 10:06 AM, Joe Bowser <bo...@gmail.com>
> wrote:
> >>>>>
> >>>>>> The execute command exists for security reasons.  We don't want any
> >>>>>> methods other than execute exposed to Javascript.  I also prefer
> this
> >>>>>> approach because it is less prone to less catastrophic bugs than
> using
> >>>>>> Java reflection.  We try and only use reflection when we have to.
> >>>>>>
> >>>>>> On Wed, May 28, 2014 at 5:50 AM, Erik Jan de Wit <edewit@redhat.com
> >
> >>>>>> wrote:
> >>>>>>> Hi,
> >>>>>>>
> >>>>>>> When one is writing a plugin for android ATM the api that you have
> to
> >>>>>> implement has a execute method that has the action as a string:
> >>>>>>> @Override
> >>>>>>>      public boolean execute(String action, JSONArray args,
> >>>>>> CallbackContext callbackContext) throws JSONException {
> >>>>>>>          if ("beep".equals(action)) {
> >>>>>>>              this.beep(args.getLong(0));
> >>>>>>>              callbackContext.success();
> >>>>>>>              return true;
> >>>>>>>          }
> >>>>>>>          return false;  // Returning false results in a
> >>>>>>> "MethodNotFound"
> >>>>>> error.
> >>>>>>>      }
> >>>>>>> When you have multiple actions this method gets very long, if you
> >>>>>> compare this with iOS here you don’t need a method like this you
> could
> >>>>>> ‘just’ implement the method directly:
> >>>>>>> - (void)beep:(CDVInvokedUrlCommand*)command
> >>>>>>>      {
> >>>>>>>          CDVPluginResult* pluginResult =;
> >>>>>>>          NSString* myarg =mmand.arguments objectAtIndex:0];
> >>>>>>>
> >>>>>>>          if (myarg !=) {
> >>>>>>>              pluginResult �VPluginResult
> >>>>>> resultWithStatus:CDVCommandStatus_OK];
> >>>>>>>          } else {
> >>>>>>>              pluginResult �VPluginResult
> >>>>>> resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Arg was
> >>>>>> null"];
> >>>>>>>          }
> >>>>>>>          [self.commandDelegate sendPluginResult:pluginResult
> >>>>>> callbackId:command.callbackId];
> >>>>>>>      }
> >>>>>>> We could do the same thing for android if we use reflection,
> making the
> >>>>>> API more similar and removing all the string test by the user. What
> do
> >>>>>> you
> >>>>>> think?
> >>>>>>> Cheers,
> >>>>>>>          Erik Jan
>
>