Granted host_permissions do not exempt background-script fetch()/XHR requests from CORS enforcement (MV3)
Categories
(WebExtensions :: General, defect)
Tracking
(firefox152 affected, firefox153 affected, firefox154 affected)
People
(Reporter: msdosfx, Unassigned)
References
(Blocks 1 open bug)
Details
Attachments
(2 files)
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36
Steps to reproduce:
Loaded a Manifest V3 MailExtension (Thunderbird 152.0) with a background script declared as {"scripts": ["background/index.js"], "type": "module"}. Granted the extension access to a LAN origin (http://192.168.50.3:5000/*, a Synology NAS's CalDAV service, which sends no CORS headers) three separate ways, retesting fresh after each: (1) via browser.permissions.request({origins: [...]}) triggered from a real button click, accepted via the resulting prompt, and confirmed granted via browser.permissions.getAll(); (2) via a static host_permissions entry in manifest.json, with the extension fully removed and reloaded; (3) confirmed the corresponding toggle is switched on under the add-on's own Permissions tab in about:addons. After each, from the background script, issued a fetch("http://192.168.50.3:5000/.well-known/caldav", {method: "OPTIONS"}) — both by hand in the background page's console (via about:debugging's Inspect) and from real extension code (a CalDAV client library making the equivalent request, triggered by clicking "Test connection" in the extension's own UI).
Actual results:
In all three permission-grant configurations, the request is blocked by CORS: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource... (Reason: CORS header 'Access-Control-Allow-Origin' missing), followed by NetworkError when attempting to fetch resource. This is identical to the behavior with no permission granted at all — granting the host permission through any of the three mechanisms made no observable difference. Full console output for each test is in the attached log file.
Expected results:
Per the documented behavior of host_permissions ("fetch access to those origins without cross-origin restrictions"), a background script's request to an origin it has been granted host permission for should bypass CORS enforcement and receive a readable response, regardless of whether the target server itself sends CORS headers. That's the entire point of the permission — it's meant to let a privileged extension context talk to servers (like self-hosted CalDAV/WebDAV services) that were never built with browser CORS support in mind.
Updated•1 month ago
|
Updated•1 month ago
|
Comment 1•1 month ago
|
||
Can you try your test add-on in Firefox? What is the result there?
(In reply to John Bieling from comment #1)
Can you try your test add-on in Firefox? What is the result there?
Yes — reproduced in Firefox using the same test add-on. Loaded it as a temporary extension in Firefox (same manifest, same optional_host_permissions for the LAN origin), granted the host permission via the runtime browser.permissions.request() prompt (accepted), and clicked "Test connection" in the extension's own UI, which triggers a fetch() to http://192.168.50.3:5000/.
Result is identical to what I saw in Thunderbird 152.0:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://192.168.50.3:5000/. (Reason: CORS header 'Access-Control-Allow-Origin' missing). Status code: 200.
followed by the same NetworkError when attempting to fetch resource at the app level, and the same pattern of blocked OPTIONS/GET requests to /.well-known/caldav and /caldav/.
So this isn't Thunderbird-specific — it reproduces in Firefox too with an identically-configured extension, which suggests it's shared Gecko/WebExtensions CORS-enforcement behavior rather than something particular to the Thunderbird MailExtension environment.
Comment 3•1 month ago
|
||
I could imagine that the whole CORS mechanism may not work with IP's. Does your local network have a DNS server, so you could try this with using a .local domain for your Synology NAS?
Updated•1 month ago
|
(In reply to John Bieling from comment #3)
I could imagine that the whole CORS mechanism may not work with IP's. Does your local network have a DNS server, so you could try this with using a .local domain for your Synology NAS?
Tested with a .local hostname instead of the IP — same result. Found the NAS's mDNS hostname (DS1520.local) via DSM's Control Panel → Network → General, confirmed it resolves (http://ds1520.local:5000 loads the DSM login page fine in a normal tab), then changed the extension's CalDAV account URL to http://ds1520.local:5000 and granted host permission for that origin.
Result is identical to the IP case:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://ds1520.local:5000/.well-known/caldav. (Reason: CORS header 'Access-Control-Allow-Origin' missing). Status code: 401.
Same pattern across /.well-known/caldav, /caldav/, and /. So this isn't specific to IP-literal origins — a granted host permission for a .local hostname is blocked by CORS exactly the same way. For what it's worth, I also tested with our unrelated native Electron build of this same app, which talks to the same NAS over .local and succeeds — but that's a plain Node HTTP client outside any browser context, so it isn't a useful data point for the CORS behavior here; only the extension-based test is relevant.
Worth noting: Thunderbird's own built-in Calendar, Tasks, and Contacts (native CalDAV/CardDAV, not a WebExtension) sync against this same Synology NAS by IP address (192.168.50.3) with no issue at all — so the server, network, and IP itself aren't the problem. That's outside the CORS-enforced code path entirely, so it's not proof CORS should bypass IP origins, but it does show this bug is specifically blocking WebExtensions from doing something Thunderbird's own native code already does fine against the identical endpoint.
Comment 5•1 month ago
|
||
Hello,
I’m trying to reproduce the issue and would be grateful if you could attach the test extension you used, so I can try out the steps.
Thank you!
Attached is a minimal, standalone test extension (cors-host-permissions-repro.zip) that reproduces this independently of the product I'm building. I stripped it down to just a popup and a background script — no CalDAV client, no application code — since I didn't want to publish my full commercial add-on's source on a public bug. A README.txt inside walks through setup and steps.
I tested it myself before attaching, using a plain local server with no CORS headers (python -m http.server 8765, no product/NAS involved):
- Loaded the extension as a temporary add-on in Firefox.
- Entered
http://127.0.0.1:8765and clicked "Request host permission" — accepted the prompt. - Confirmed the grant via
browser.permissions.getAll():permissions.getAll() origins: ["http://127.0.0.1:8765/*"] - Clicked the button that has the background script
fetch()that same origin. Result:{ "ok": false, "url": "http://127.0.0.1:8765/", "error": "TypeError: NetworkError when attempting to fetch resource." }
Same failure as originally reported, now on a minimal extension and a plain local HTTP server — no LAN NAS, no IP-vs-hostname variable, no product-specific code in the picture. Should be a straightforward one-click repro on your end: load manifest.json as a temporary add-on, start any no-CORS HTTP server, and follow the README.
Comment 7•1 month ago
•
|
||
Hello and thank you for the test extension and the detailed steps to reproduce the issue !
I’ve reproduced the bug on the latest Nightly (154.0a1/20260712214442), Beta (153.0b11/20260710112311) and Release (152.0.5/20260706120035) under Windows 11 and Ubuntu 25.10.
Performing the STR logs the following in the extension popup:
{
"ok": false,
"url": "http://127.0.0.1:8765/",
"error": "TypeError: NetworkError when attempting to fetch resource.",
"note": "This NetworkError, despite a granted host permission for this origin, is the behavior reported in bug 2052000."
}
The browser console also shows a Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://127.0.0.1:8765/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200. error.
Comment 8•22 days ago
|
||
The severity field is not set for this bug.
:robwu, could you have a look please?
For more information, please visit BugBot documentation.
Comment 9•18 days ago
|
||
Do you get the expected behavior if you exclude the port in the permission request?
This is likely a duplicate of bug 1362809.
| Reporter | ||
Comment 10•13 days ago
|
||
(In reply to Rob Wu [:robwu] from comment #9)
Do you get the expected behavior if you exclude the port in the permission request?
This is likely a duplicate of bug 1362809.
Confirmed — excluding the port is the fix, and this does look like a duplicate of bug 1362809.
I reduced it to a minimal MV3 test extension (no product code): it grants a host permission via permissions.request(), then does a background-script fetch() to a local no-CORS server (python -m http.server 8765). Two runs against the same server:
Grant http://127.0.0.1:8765/* (with port), fetch http://127.0.0.1:8765/ → permissions.request() resolves true, permissions.getAll() lists the origin, but the fetch is rejected with a NetworkError (CORS-blocked), as if nothing were granted.
Remove that grant, grant http://127.0.0.1/* (no port), fetch the same http://127.0.0.1:8765/ → fetch succeeds, HTTP 200, body readable — CORS bypassed as documented.
So the grant only takes effect when the match pattern has no port. A pattern with an explicit port is accepted and reported as active by both permissions.getAll() and the about:addons Permissions tab, but silently matches nothing — consistent with 1362809. From an extension author's side the misleading part is exactly that: both surfaces show the port-qualified origin as granted, so there's no hint it isn't applying.
My original report used http://192.168.50.3:5000/; dropping the port to http://192.168.50.3/ resolves it there too. Happy to have this closed as a duplicate of 1362809.
Updated•13 days ago
|
Description
•