Modifying/Removing CSP header V3

I’d like to modify the CSP header in browser.webRequest.onHeadersReceived in my v3 add-on.
For example, I’d like to turn a CSP value of “connect-src ‘self’” into “connect-src localhost:1234 ‘self’” so that I can interact with a local agent over http (to facilitate communication that can not be done via the regular native messaging host).

Some code like this:

browser.webRequest.onHeadersReceived.addListener(
  (details: browser.webRequest._OnHeadersReceivedDetails) => {
    details.responseHeaders?.forEach((header) => {
      if (header.name.toLowerCase() === "content-security-policy") {
        const oldValue = header.value;
        header.value = oldValue?.replace('connect-src', 'connect-src http://localhost:1234');
      }
    });
    return details;
  },
  { urls: ["<all_urls>"] },
  ["blocking", "responseHeaders"]
);

merely results in a combined, equally restrictive, policy

{
    "name": "Content-Security-Policy",
    "value": "connect-src 'self', connect-src http://localhost:1234 'self'"
}

and outright removing the header has no affect at all.
However the documentation - webRequest.onHeadersReceived - Mozilla | MDN
explicitly mentions being able to remove the original CSP header.

Merged modifications always lean towards being more restrictive, though an extension may remove the original CSP header.

I’m also finding that in firefox my content script is beholden to the CSP, while in chrome this is not the case (only code executed in the ‘main world’ javascript context is affected by the CSP).

Is there anything that can be done in firefox to achieve my desired outcome (slightly loosening the csp to call a port on localhost)?
Would moving to a v2 extension work?

That’s the old API, these days one should use:

But for your very specific use case I’m not sure how exactly would that work.
The documentation is also missing some useful examples, but I guess ChatGPT could help with that.

Here some example of my own usage for removing x-frame-options header:

  await browser.declarativeNetRequest.updateSessionRules({
    removeRuleIds: [ruleId],
    addRules: [{
      id: ruleId,
      condition: {
        tabIds: targetTabId,
        resourceTypes: ['sub_frame'],
      },
      action: {
        type: 'modifyHeaders',
        responseHeaders: [
          {header: 'x-frame-options', operation: 'remove'}
        ],
      },
    }],
  })

NOTE that relaxing security for all pages loaded in all tabs is likely a security issue, so make sure to limit your API to only those page / tabs where needed.

Also check the browser compatibility table to make sure Firefox supports what you need as there is A LOT of special features.

See also:

Thanks,
Using declarative net request to remove the header definitely works.