25
votes

Is it possible to use CORS when writing a google chrome extension?

I saw this, http://developer.chrome.com/extensions/contentSecurityPolicy.html

And I tried inserting this into the manifest, "content_security_policy": "script-src 'self' https://twitter.com/; object-src 'self'",

but an ajax requestion fails with XMLHttpRequest cannot load https://twitter.com/. Origin chrome-extension://olimhkjfpndfhdopbneamnekfalckinc is not allowed by Access-Control-Allow-Origin.

4
Content Security Policy and Cross-Origin Resource Sharing are totally different. The CSP is a way for the client to block things that would otherwise be allowed by the same-origin policy, like loading <script>s. CORS is a way for the server to allows things that would otherwsie be blocked (i.e., cross-domain Ajax). - apsillers

4 Answers

37
votes

To enable cross-origin Ajax from your extension to Twitter, you simply need to list Twitter as a host permission in your manifest:

...
"permissions": [
    "*://*.twitter.com/*"
],
...
29
votes

Explanation

As of Chrome 73, cross site requests are blocked. (Even if you have the hosts in your permissions property.) Some sites seem to get through unblocked but I'm sure eventually most won't.

You have to make the request in a background page...


Setup

Add a section to your manifest.json to point to the background page.

"background": {
  "scripts": ["bg_page.js"],
  "persistent": false
}

Create the background page: bg_page.js

chrome.runtime.onMessage.addListener(
    function(url, sender, onSuccess) {
        fetch(url)
            .then(response => response.text())
            .then(responseText => onSuccess(responseText))
        
        return true;  // Will respond asynchronously.
    }
);

Then use it in main.js

chrome.runtime.sendMessage( //goes to bg_page.js
      url,
      data => dataProcessFunction(data)
); 

Additional Tips

If you do anything like console.log() from the bg page, click the "inspect views background page" link in your extension's square in chrome's extensions manager and you'll get a separate dev tools window for everything that happens with your background page.

If you want to see the docs for yourself, check out step 2 of the Recommended Developer Actions section here: https://www.chromium.org/Home/chromium-security/extension-content-script-fetches#TOC-2.-Avoid-Cross-Origin-Fetches-in-Content-Scripts

0
votes

Add the header Access-Control-Allow-Origin: * to the response of your remote server if possible. Here is a tutorial for various backend servers: add CORS support to backend server

0
votes

UPDATE FOR MANIFEST V3


I figured out a way to do this. But be extremely careful with your url filtering and other safeguards.

As of Manifest v3 you can set rules for modifying incoming and outgoing calls. Include the name of the site you are requesting in a new section of the manifest called "host_permissions". You can see how to implement rules in the manifest here: https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#manifest

There is a rule action called "allowAllRequests" using this rule allows you to make cross-origin-requests, bypassing preflight checks. You can read more about the rules here: https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/#type-RuleActionType

To use this rule you will need to include the "resourceTypes" key in "condition" and a value of "main_frame" or "sub_frame"

Also make sure to completely uninstall your extension and reinstall it for these changes to take effect.


Example manifest from google:

{
  "name": "My extension",
  ...

  "declarative_net_request" : {
    "rule_resources" : [{
      "id": "ruleset_1",
      "enabled": true,
      "path": "rules_1.json"
    }, {
      "id": "ruleset_2",
      "enabled": false,
      "path": "rules_2.json"
    }]
  },
  "permissions": [
    "declarativeNetRequest",
    "declarativeNetRequestFeedback",
    "*://example.com/*"
  ],
  "host_permissions": [
    "http://www.blogger.com/",
    "*://*/*"
  ],
  ...
}

Example Rule.json:

[
    {
        "id": 1,
        "priority": 1,
        "action": { "type": "allowAllRequests" },
        "condition": {
            "urlFilter": "again make sure you are very specific with this.",
            "resourceTypes": ["main_frame"]
        }
    }
]