Content scripts and websocket

Hi, I’m struggling to find a way to make a websocket connection from the add-on content script. I get the following error in the console, so I’m assuming it has something to do with the CSP configuration.

Content Security Policy: The page’s settings blocked the loading of a resource at wss://localhost:8000/sessions/ (“connect-src”).

I’ve read up the default CSP doc and the manifest setting, but I have a few questions about this

  1. Can I specify connect-src in the CSP for the extension?

  2. Can I specify wss:// as a protocol in the connect-src? The manifest reference suggests allowed protocols are blob:, filesystem:, moz-extension:, and https:

  3. Does the content script follow the CSP defined in the add-on manifest?

I’m looking to understand how to get a websocket connection working on a content script, and any help there would be appreciated. Thank you!

1 Like

The answer should be the same as here:

Thanks Niklas. I have the host added to the permissions array in the manifest. However, it does not work well with the page’s CSP, at least on websocket. I believe the ws/wss protocol is not covered by XHR. Is that correct? Any alternate implementation?

How can it “not work well”? I’d think it either works or it does not work at all …

If connecting from a content script doesn’t work even with the correct host permission, you can

  1. Report it as a bug, which I think it is (or at least a missing feature). But it will probably take a good while until a fix is released, so:
  2. Try to connect from the background page. If that works “pipe” everything through a runtime.Port.
  3. Or modify the CSP on the incoming webRequests of the page you want to work on. I have done it beffore and can tell you that it is a lot of work if you want to get it right.

I meant that adding host permissions does not work with websocket. Ignore the well.

Thank you for the pointers. #2 seems to be most reliable, will try that out and also file a bug.

Same problem here. This problem doesn’t occur in Chrome for example. Need a way to specify the wss://domain.com in the “content_security_policy” of the manifest file.

It works for me from the background script but not from the content script.

The CSP in the manifest.json I’m using is:

"content_security_policy": "default-src 'self'; connect-src wss://echo.websocket.org;"

On Chrome it does work for both the background script and content script.

9.1. Vendor-specific Extensions and Addons

Policy enforced on a resource SHOULD NOT interfere with the operation of user-agent features like addons, extensions, or bookmarklets. These kinds of features generally advance the user’s priority over page authors, as espoused in [HTML-DESIGN].

Moreover, applying CSP to these kinds of features produces a substantial amount of noise in violation reports, significantly reducing their value to developers.

Chrome, for example, excludes the chrome-extension: scheme from CSP checks, and does some work to ensure that extension-driven injections are allowed, regardless of a page’s policy.

Running this on the background script and adding "webRequestBlocking" to "permissions" in manifest.json should allow connecting via web socket from any page that runs the content script.

import browser from 'webextension-polyfill';

function isCSPHeader(headerName) {
  return (headerName === 'CONTENT-SECURITY-POLICY') || (headerName === 'X-WEBKIT-CSP');
}

browser.webRequest.onHeadersReceived.addListener(
  (details) => {
    for (let i = 0; i < details.responseHeaders.length; i += 1) {
      if (isCSPHeader(details.responseHeaders[i].name.toUpperCase())) {
        const csp = 'default-src * \'unsafe-inline\' \'unsafe-eval\' data: blob:; ';
        details.responseHeaders[i].value = csp;
      }
    }
    return {
      responseHeaders: details.responseHeaders
    };
  }, {
    urls: ['<all_urls>'],
    types: ['main_frame'],
  }, ['blocking', 'responseHeaders']
);