chariotsolutions / phonegap-nfc

PhoneGap NFC Plugin
MIT License
706 stars 559 forks source link

Not Working In CSP configuration : Using Deprecated Apis #196

Closed jmorille closed 6 years ago

jmorille commented 8 years ago

In CSP activate like with CCA 0.7.1 during processing NFC message with the plugins

  <plugin name="com.chariotsolutions.nfc.plugin" spec="^0.6.2"/>

the call of the plugin function (and when passing the nfc tags)

 nfc.addNdefListener(onNfcEvent,onSuccess, onFailure );

the error is raised

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "default-src file: data: chrome-extension: https://ssl.gstatic.com".
processMessage @ cordova.js:1070
processMessages @ cordova.js:1104
pollOnce @ cordova.js:973
pollOnceFromOnlineEvent

After an discution with Cordova team https://issues.apache.org/jira/browse/CB-9277, the plugins use an deprecated Apis that cause the error whe calling the Apis https://github.com/apache/cordova-android/blob/4bf705a3d39b34400388265381a9975b246e3779/framework/src/org/apache/cordova/CordovaWebView.java#L92

 /**
     * Send JavaScript statement back to JavaScript.
     *
     * Deprecated (https://issues.apache.org/jira/browse/CB-6851)
     * Instead of executing snippets of JS, you should use the exec bridge
     * to create a Java->JS communication channel.
     * To do this:
     * 1. Within plugin.xml (to have your JS run before deviceready):
     *    <js-module><runs/></js-module>
     * 2. Within your .js (call exec on start-up):
     *    require('cordova/channel').onCordovaReady.subscribe(function() {
     *      require('cordova/exec')(win, null, 'Plugin', 'method', []);
     *      function win(message) {
     *        ... process message from java here ...
     *      }
     *    });
     * 3. Within your .java:
     *    PluginResult dataResult = new PluginResult(PluginResult.Status.OK, CODE);
     *    dataResult.setKeepCallback(true);
     *    savedCallbackContext.sendPluginResult(dataResult);
     */
    @Deprecated
    void sendJavascript(String statememt);

whitch result the javascript method that do the eval

function processMessage(message) {
    var firstChar = message.charAt(0);
    if (firstChar == 'J') {
        // This is deprecated on the .java side. It doesn't work with CSP enabled.
        eval(message.slice(1));
jmorille commented 8 years ago

The cordova ticket for more detail : https://github.com/chariotsolutions/phonegap-nfc/issues/196 The CCA ticket : https://github.com/MobileChromeApps/mobile-chrome-apps/issues/584

jmorille commented 8 years ago

The deprecated Apis is used to FireEvent

   private void fireNdefEvent(String type, Ndef ndef, Parcelable[] messages) {

        JSONObject jsonObject = buildNdefJSON(ndef, messages);
        String tag = jsonObject.toString();

        String command = MessageFormat.format(javaScriptEventTemplate, type, tag);
        Log.v(TAG, command);
        this.webView.sendJavascript(command);

    }

    private void fireNdefFormatableEvent (Tag tag) {

        String command = MessageFormat.format(javaScriptEventTemplate, NDEF_FORMATABLE, Util.tagToJSON(tag));
        Log.v(TAG, command);
        this.webView.sendJavascript(command);
    }

    private void fireTagEvent (Tag tag) {

        String command = MessageFormat.format(javaScriptEventTemplate, TAG_DEFAULT, Util.tagToJSON(tag));
        Log.v(TAG, command);
        this.webView.sendJavascript(command);
    }
 String javaScriptEventTemplate =
        "var e = document.createEvent(''Events'');\n" +
        "e.initEvent(''{0}'');\n" +
        "e.tag = {1};\n" +
        "document.dispatchEvent(e);";
don commented 8 years ago

What's CSP and CCA?

Unfortunately that JavaScript event is pretty important for this plugin to work.

I don't want to change the plugin API, so the solution is probably to set up another javascript function with a long running callback. The plugin can then use this callback to send data and the event can be dispatched from JavaScript. The plugin initialization make the initial call so it doesn't effect existing apps.

jmorille commented 8 years ago

CCA = Chrome App Mobile (cf https://github.com/MobileChromeApps/mobile-chrome-apps) It is a Cordova Wrapping of Cordova with the Chrome engine. The Default configuration use CSP in order to protect against hacking (Always a good practice for JS developpement)

CSP : Content Security Policy (a W3C spec http://www.w3.org/TR/CSP/) that offer some level of protection againt XSS attack (cf https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policy)

In this case, the CSP block the using of eval function that is a source of this bug.

As I see in the code, the js code is used to just send an event and it is always the same way.

The work around, like the javadoc say, it to create

     * Instead of executing snippets of JS, you should use the exec bridge
     * to create a Java->JS communication channel.

To be honest, I don't know what does it mean (I starting not so long with cordova). In any case, this method it a deprecated method, and in consequence it should be removed in futur cordova version.

More than the security issue, it is a correction that could permit to work in an futur version of cordova.

The goal, if I understand well, is to send an event with the json data ?? nothing more ?

don commented 8 years ago

It's easy to send data via the exec bridge. It's more difficult to do this without changing the API of the plugin. This is an old plugin that has been through many versions of Cordova. Some APIs would be different if I was writing this today, but keeping the API stable is a priority so it doesn't break people's code. Removing the deprecated call is a good idea, but a fair amount of work so it's likely that this won't get updated until the deprecated method is removed from Cordova.

martin-rueegg commented 7 years ago

Hi!

I have created a patch that replaces the use of JavaScript within Java entirely. I have not yet created a PR, as @don was worrying about the change in the API. I do not see, that the API is changed in any way, but maybe someone might want to review my changes. I'm happy to create a PR if that's helping.

The patch also changes the way the JS part is initialized, which obsoletes the usage of the deviceready Event.

I've successfully tested it on an Android 4.2.2 device using the latest cordova and it also resolves #249. But other please test it as well on other devices.

Furthermore it lays out the bases, that the usage of event bubbling can be replaced altogether and callbacks used. You can find that branch here. It will still be backwards compatible for the case, that someone relies on the bubbling itself, rather on the own provided callback. I can create a PR for this as well, if that helps discussing the solution.

Martin.

don commented 7 years ago

@martin-rueegg thanks for creating a patch. A pull request will make it easier for me to review this.