Google Chrome extension Content Security Policy

I'm working on a Google Chrome extension that should include Facebook's sdk.js but the problem is I can't make the app work because of the Content Security Policy..Here is part of my code :

manifest.json

{
  "manifest_version": 2,

  "name": "<app-name>",
  "description": "<description>",
  "version": "0.1",

  "permissions": [
  "tabs","<all_urls>"
  ],
  "content_scripts": [
  {
    "matches": [
    "http://*/*",
    "https://*/*"
    ],
    "js": ["intercept_connections.js"]
  }
  ],
  "content_security_policy": "script-src 'self' https://connect.facebook.net/en_US/ 'unsafe-eval'; object-src 'self'",
  "background": {
    "scripts" : ["background.js"]
    },
    "browser_action": {
      "default_icon": "icon.png",
      "default_popup": "popup.html"
    }
  }

popup.html

<html>
<head>
    <title>Facebook Mass Sharer</title>


    <script type="text/javascript" src="popup.js"></script>
    <script type="text/javascript" src="FbApi.js"></script>
</head>

<body>
<body>
    <h1>Post to Facebook</h1>
    <button id="postTest">TestMe</button>

  </body>
</body>
</html>

popup.js

var handleClick = function() {
    postToGroup(<groupID>);
}

var initialize = function() {
    var test = document.getElementById('postTest');
    test.addEventListener("click",handleClick);
}

window.addEventListener("load", initialize);

FbApi.js (made by me)

window.onload = function() {
    window.fbAsyncInit = function() {
        FB.init({
          appId      : '<appID>',
          xfbml      : true,
          version    : 'v2.1',
          oauth      : true
        });
      };

      (function(d, s, id){
         var js, fjs = d.getElementsByTagName(s)[0];
         if (d.getElementById(id)) {return;}
         js = d.createElement(s); js.id = id;
         js.src = "https://connect.facebook.net/en_US/sdk.js";
         fjs.parentNode.insertBefore(js, fjs);
       }(document, 'script', 'facebook-jssdk'));
}

var postToGroup = function(groupID) {
    FB.api(
    "/"+groupID+"/feed",
    "POST",
    {
        "message": "This is a test message"
    },
    function (response) {
      if (response && !response.error) {
        /* handle the result */
      }
    }
);
}

The thing is I keep getting this error :

Refused to execute JavaScript URL because it violates the following Content Security Policy directive: "script-src https:// 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

I tried changing the "content_security_policy": "script-src 'self' https://connect.facebook.net/en_US/ 'unsafe-eval'; object-src 'self'" to "content_security_policy": "script-src 'self'; object-src 'self'" or "content_security_policy": "script-src 'self' https://connect.facebook.net/en_US/ 'inline-eval'; object-src 'self'" as suggested in the error but no luck..What should I try?

Answers:

Answer

Google considers eval to be an unsafe, potentially dangerous operation, since it's possible that dynamic content could work its way into the app/extension and run itself, gaining access to a whole world of powerful chrome.* APIs that give one access to filesystems and other resources that are typically off-limits to normal website JavaScript.

Thus, the only way to use these CSP-violating libraries in your extensions and apps is to sandbox them:

We accomplish this by listing specific HTML files inside the extension package as being sandboxed. Whenever a sandboxed page is loaded, it will be moved to a unique origin, and will be denied access to chrome.* APIs. If we load this sandboxed page into our extension via an iframe, we can pass it messages, let it act upon those messages in some way, and wait for it to pass us back a result. This simple messaging mechanism gives us everything we need to safely include eval-driven code in our extension's workflow.

In short, you'll need to create an extra component in your extension, an HTML file which you sandbox, and which loads the offending Facebook code. You'll then add listeners in the sandboxed page that can receive messages from the containing HTML page and then handle the events in the extension. This protects your users from eval, since eval'd code can't access any Chrome API's in the sandbox.

When I was working with sipml5's JavaScript SIP library, I dealt with the same problems. The library used eval quite liberally, so the only way to use the library was in a sandbox. I created a small library for dealing with syncing storage data between the sandbox and the main app, called the Sandbox StorageAPI for Chrome™. In the code, you can see how I'm communicating between the sandbox and the main page:

manifest.json:

"sandbox": {
    "pages": ["sandbox.html" ]
  },

mainpage.html:

<!-- Sandboxed page -->
    <iframe height="800" width="1200" id="sandboxFrame" class="sandbox active" src="sandbox.html" style=""></iframe>

storageAPI.js:

Here, I create a listener in the sandbox that listens for the main, secure part of the app/extension to "register" itself with the sandbox and then store that reference so the sandbox can communicate back to the main module:

window.addEventListener('message', function(event) {
            console.info("Message received from wrapper = " + JSON.stringify(event.data));
            console.info("location = " + window.location.href);
            switch(event.data.command) {

                case 'initStorageAPI':
                    storageAPI.storage = event.data.storage;  result = "It's alive!!!";
                    storageAPI.sandbox.window.onload();
                    console.info("Prepare to postMessage back to the wrapper...");
                    storageAPI.wrapper = event;
                    event.source.postMessage({'command':'initStorageComplete','result': result}, event.origin);

And here's an example where we send data back to the main module:

storageAPI.setItem = function(key, value) {
        if(storageAPI.CHROME_EXTENSION == false) {
            return window.localStorage.setItem(key, value);
        } else {
            storageAPI.storage[key] = value;
            // send back to the wrapper to store
            storageAPI.wrapper.source.postMessage({'command':'writeStorage', 'storage': storageAPI.storage}, storageAPI.wrapper.origin);

        }

    };

In summary, you can use a similar technique to marshall data back and forth from/to the sandbox to/from the main module, connecting the unsecured library with the secure, sensitive part of your extension. See the docs for more details.

Keep in mind that the iframe could even be hidden. You really only need it to be able to run the unsecured code.

Disclaimer: I am the Sandbox StorageAPI for Chrome™ developer. I am not affiliated with Google or Chrome, and all trademarks belong to Google.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.