Creating websocket throws in reply compose windows
Categories
(Thunderbird :: Add-Ons: Extensions API, defect)
Tracking
(Not tracked)
People
(Reporter: code, Assigned: code)
References
()
Details
Attachments
(2 files)
10.00 KB,
application/x-tar
|
Details | |
828 bytes,
patch
|
Details | Diff | Splinter Review |
User Agent: Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0
Steps to reproduce:
Loaded my reproducer and tried to reply to an email.
Actual results:
An extension was thrown and the message "after websocket" was not printed in the addon debugging console.
Expected results:
The message "after websocket" should have been printed, like it happens when creating a new message instead of replying to an existing message.
Woops, just noticed a typo: s/An extension was thrown/An exception was thrown/.
Here are the logs I get when running a debug build from the console and just clicking on "write":
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: '!aNode', file /home/me/prog/mozilla-central/editor/libeditor/EditorBase.cpp:3030
[70772, Main Thread] WARNING: NS_ENSURE_TRUE(name) failed: file /home/me/prog/mozilla-central/dom/base/nsDOMAttributeMap.cpp:306
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: NS_ENSURE_TRUE(frame) failed: file /home/me/prog/mozilla-central/dom/events/ContentEventHandler.cpp:1300
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: Failed to retarget HTML data delivery to the parser thread.: file /home/me/prog/mozilla-central/parser/html/nsHtml5StreamParser.cpp:1132
JavaScript error: , line 0: uncaught exception: Object
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
And here are the logs when replying and the exception is thrown:
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: NS_ENSURE_TRUE(!doc->IsResourceDoc() && ((!doc->IsLoadedAsData() && aOwner-IsInComposedDoc()) || doc->IsStaticDocument())) failed: file /home/me/prog/mozilla-central/dom/base/nsFrameLoader.cpp:412
[70772, Main Thread] WARNING: NS_ENSURE_TRUE(!doc->IsResourceDoc() && ((!doc->IsLoadedAsData() && aOwner-IsInComposedDoc()) || doc->IsStaticDocument())) failed: file /home/me/prog/mozilla-central/dom/base/nsFrameLoader.cpp:412
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: HTMLEditor::GetAbsolutelyPositionedSelectionContainer() reached <html> element: file /home/me/prog/mozilla-central/editor/libeditor/HTMLAbsPositionEditor.cpp:94
[70772, Main Thread] WARNING: NS_ENSURE_TRUE(frame) failed: file /home/me/prog/mozilla-central/dom/events/ContentEventHandler.cpp:1300
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: Failed to retarget HTML data delivery to the parser thread.: file /home/me/prog/mozilla-central/parser/html/nsHtml5StreamParser.cpp:1132
JavaScript error: , line 0: uncaught exception: Object
[70772, Main Thread] WARNING: NS_ENSURE_SUCCESS_VOID(rv) failed with result 0x80004001 (NS_ERROR_NOT_IMPLEMENTED): file /home/me/prog/mozilla-central/caps/OriginAttributes.cpp:114
[70772, Main Thread] WARNING: Failed to retarget HTML data delivery to the parser thread.: file /home/me/prog/mozilla-central/parser/html/nsHtml5StreamParser.cpp:1132
[70772, Main Thread] WARNING: 'aRv.Failed()', file /home/me/prog/mozilla-central/dom/websocket/WebSocket.cpp:1245
JavaScript error: moz-extension://ef27a09b-7b25-4ab3-b0c9-9d59979cf46e/frame.js, line 2: :
[70772, Main Thread] WARNING: 'aRv.Failed()', file /home/me/prog/mozilla-central/dom/websocket/WebSocket.cpp:1245
JavaScript error: moz-extension://ef27a09b-7b25-4ab3-b0c9-9d59979cf46e/frame.js, line 2: :
The following diff is enough to make the problem disappear:
diff -r 0e95e169ef40 dom/websocket/WebSocket.cpp
--- a/dom/websocket/WebSocket.cpp Fri Nov 06 13:50:52 2020 +0000
+++ b/dom/websocket/WebSocket.cpp Sat Nov 07 12:54:04 2020 +0100
@@ -1545,10 +1545,6 @@
nsContentUtils::GetContentPolicy());
NS_ENSURE_SUCCESS(rv, rv);
- if (NS_CP_REJECTED(shouldLoad)) {
- // Disallowed by content policy
- return NS_ERROR_CONTENT_BLOCKED;
- }
}
// If the HTTPS-Only mode is enabled, we need to upgrade the websocket
So I guess the problem is that the websocket has a different CSP between a "new message" window and a "reply" window? This doesn't make much sense as in both cases the websocket is created within an iframe that belongs to the webextension…
This patches fixes this bug. @mkmelin I see that you've worked on code adjacent to what I'm adding. Are you able to review and merge my patch? If not, do you know who I should ask?
Comment 5•4 years ago
|
||
Why is it only a problem in the compose window?
Updated•4 years ago
|
Comment 6•4 years ago
|
||
For review, please set the "review?" flag on the attachment to my email address.
It's only a problem in a compose window created for replying to an email ("new email" windows are fine), so I imagine this is because the document created from the reply is considered untrusted and its CSP incorrectly "bleeds" into the web-extension's.
Comment 8•4 years ago
|
||
The relevant code for compose is here: https://searchfox.org/comm-central/rev/93bfded213518bb71f465d6fa17baaafafb9a846/mailnews/base/src/nsMsgContentPolicy.cpp#714
But what are you trying to do? Why would a compose window use websocket?
The relevant code for compose is here: https://searchfox.org/comm-central/rev/93bfded213518bb71f465d6fa17baaafafb9a846/mailnews/base/src/nsMsgContentPolicy.cpp#714
Do you mean that you think my patch is fixing this issue in the wrong place? My patch just adds the "moz-extension" scheme to the list of safe locations that nsMsgContentPolicy::IsSafeRequestingLocation knows about. It is my understanding that moz-extension locations are treated as safe in Firefox (because they act on behalf of the user).
But what are you trying to do? Why would a compose window use websocket?
In my opinion this doesn't really matter, WebSockets should just work. But if you're curious, I've ported Firenvim ( https://addons.thunderbird.net/en-US/thunderbird/addon/firenvim/ ), an extension that communicates with a Neovim server running on the user's computer.
Comment 10•4 years ago
|
||
Well, it seems at least it would do unexpected things. If I understand it correctly, the compose area is replaced by the add-on? And any loads to anywhere (including file system) would therefore be allowed from it? Seems pretty bad...
Assignee | ||
Comment 11•4 years ago
|
||
If I understand it correctly, the compose area is replaced by the add-on?
Here is what my extension does:
- The background script launches a neovim server.
- When a compose window is opened, an iframe whose source document is in extension storage is wrapped inside a shadow dom element and inserted in the compose window's document.
- This iframe uses a websocket to connect to the neovim server, possibly reading files and starting programs.
- At some point, the user decides to sync the Neovim server's content. The iframe is removed from the compose window's document and text the user wrote is added to the compose window's document.
And any loads to anywhere (including file system) would therefore be allowed from it? Seems pretty bad...
Yes, Firenvim can read anything the Neovim server can read. Note that WebSockets have nothing to do with this, reading files from the file system is already possible thanks to the native messaging API. Also note that Firenvim is used by at least two firefox devs, so connecting to a Neovim server with websockets is considered by at least multiple firefox devs.
Assignee | ||
Comment 12•4 years ago
|
||
is considered by at least multiple firefox devs.
I meant "is considered ok by".
Comment 13•4 years ago
|
||
Hmm, maybe file system access is still blocked elsewhere. The point is the quoted content is arbitrary, and could link to e.g. something in the file system to trick, and maybe steal it by including it in the reply. So giving that content a free pass to do anything seems like a problem.
Comment 14•4 years ago
|
||
And even for http links, it would let spammers be notified you read the mail you may be just forwarding to spam filtering (like some people do).
Assignee | ||
Comment 15•4 years ago
|
||
Hmm, maybe file system access is still blocked elsewhere.
I don't understand what you mean. Could you expand on that?
The point is the quoted content is arbitrary, and could link to e.g. something in the file system to trick, and maybe steal it by including it in the reply.
So is the content of web pages in web browsers. Yet multiple Firefox devs think Firenvim for Firefox is fine.
So giving that content a free pass to do anything seems like a problem.
You've already given that content a free pass to do anything when you allowed webextensions to have both compose and native permissions.
And even for http links, it would let spammers be notified you read the mail you may be just forwarding to spam filtering (like some people do).
I don't understand what you mean. The moz-extension storage is strictly controlled by a webextension. Allowing webextensions to open WebSockets does not let the original document open WebSockets as the original document does not have a moz-extension url scheme.
Comment 16•4 years ago
|
||
The (well, one) reason it's blocked for replies is that if you had say a spam with linked images in it, and you didn't load those when viewing the message, those should not get automatically loaded just because you forward the message. What I'm saying is if your add-on gives free pass to all such images (etc.) even that is bad.
Assignee | ||
Comment 17•4 years ago
|
||
Specific case of Firenvim: My add-on doesn't load any resources from the email's content. It just takes the text and puts it in an iframe, so there's no risk here.
More general case of all add-ons: I believe add-ons should be free to load any remote resources, even the ones belonging to spammers. This is because one of the fundamental assumptions behind webextensions is that the webextension is on the user's side. I'm not inventing this, see this quote:
We usually assume that the add-on is acting on the user's behalf (the user chose to install it) so it'd be nice if add-ons could bypass CSP.
Taken right from this comment, from a Firefox developer: https://bugzilla.mozilla.org/show_bug.cgi?id=1267027#c3 . I understand that Thunderbird is not Firefox anymore but given the large shared codebases and philosophies, I think it doesn't make sense to diverge from Firefox here.
Comment 18•4 years ago
|
||
(In reply to glacambre from comment #17)
Specific case of Firenvim: My add-on doesn't load any resources from the email's content. It just takes the text and puts it in an iframe, so there's no risk here.
"Just putting it in an iframe", and with your patch give all resources in there the rights to load, means exactly that those spam images would load, no?
Assignee | ||
Comment 19•4 years ago
|
||
"Just putting it in an iframe", and with your patch give all resources in there the rights to load, means exactly that those spam images would load, no?
No, sorry, I wasn't clear enough. Putting it in an iframe actually means taking the .textContent of the compose window, sending it to a neovim server and then having the server send back an individual view of each character, which are then each inserted in their own span element (there can only be a single character per span). Note that the use of HTML here will soon stop, I've written a new renderer which draws each of the characters individually on a canvas instead.
But as I've said, what Firenvim does should not matter. Webextensions should be free to load any content because they act on the behalf of the user.
Assignee | ||
Comment 20•4 years ago
|
||
Posted yesterday on matrix:
glacambre:
Basically the problem is that you can't create a websocket in a webextension iframe in a (reply) compose window. AFAIU mkmelin thinks you shouldn't be able to do that and I disagree.
mkmelin:
I don't think that is what I'm saying, but there's a reason loading is blocked there, and your patch would seem to break that protection
Mkmelin: Oh, sorry for the misunderstanding then. So the problem is only with the implementation itself and not allowing webextensions to open websockets? Could you tell me what technical solutions you envision to allow moz-extension frames to create arbitrary HTTP and WebSocket connections in a reply compose window? I'm willing to (attempt) to implement it :).
Comment 21•4 years ago
|
||
Comment on attachment 9186470 [details] [diff] [review] fix-extension-csp.patch Review of attachment 9186470 [details] [diff] [review]: ----------------------------------------------------------------- Per previous comments, I don't think this is completely right. I think we'd need to check the permissions the specific add-on has obtained, at least. We should also see how we can add a test for this. (About the patch, please always use a commit message as "Bug 1675589 - why the change was done" John might be able to give some input on what should happen here.
Assignee | ||
Comment 22•4 years ago
|
||
I think we'd need to check the permissions the specific add-on has obtained
Which permissions would you like to check? I'm not seeing anything particularily relevant in https://thunderbird-webextensions.readthedocs.io/en/68/index.html, are there other "hidden" permissions that Thunderbird could use in order to figure out whether an extension is allowed to make a web request?
We should also see how we can add a test for this.
I'll try to figure out how to do this.
Thanks a lot for taking the time to review my patch/answer my questions! :)
Comment 23•4 years ago
•
|
||
I think Magnus is afraid of blocked content being made available again. But this is not the case. If the email contains content that is blocked by Thunderbird (for example remote tracking images), the WebExtension methods in the compose window (like getComposeDetails) also do not see that content, because it is not there. So whatever a WebExtension might be doing, if the user has not unblocked that content, the WebExtension is not able to see the problematic content in the compose window and thus cannot manually cause tracking or alike.
The only difference between "new" and "reply" is that the WebExtension gains (partially) access to the content of a users email. Usually this is controlled by the messagesRead permission, but for me it is ok to not be required here.
The compose permission is giving compose scripts host permission. The add-on could load whatever it wants into the composer, for example remote kitten pictures:
await browser.tabs.executeScript(tab.id, {
code: `
document.execCommand("insertText", false, "Text to insert into message");
document.execCommand("InsertImage", false, "http://placekitten.com/200/300");
`
})
The websocket should work as well. There is no fundamental difference.
The only thing one can think about is to separate the permissions as such as that "compose" is just giving access to the compose area, but remote content would still need a dedicated host permission. But I believe Geoff did not want to go down that route (impl. imap:// url support for host permission and adapt <all_urls> to work in compose scripts).
Comment 24•3 years ago
|
||
Sorry for taking so long.
The patch is changing code in mozilla-central (Firefox code which is also used by Thunderbird) and not in comm-central (Thunderbird specific code). You say this works in Firefox? Then this is not the correct way to fix it. I think we need to "fix" this on our side. I will have a closer look.
Updated•3 years ago
|
Comment 25•3 years ago
|
||
mailnews is comm-central...
Comment 26•3 years ago
|
||
Oh, I was looking at the changes in WebSocket.cpp. I keep investigating.
Assignee | ||
Comment 27•3 years ago
|
||
tbsync, mkmelin, thank you both for still interacting with this bug. I'm still hoping I'll find the time to write a test. Here's what I got so far:
diff -r 8819c8ab92f6 mailnews/base/test/unit/test_bug1675589.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mailnews/base/test/unit/test_bug1675589.js Wed Dec 02 04:07:11 2020 +0100
@@ -0,0 +1,32 @@
+/* -*- mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+/*
+ * Test for bug 1675589.
+ */
+function run_test() {
+ const policy = Cc["@mozilla.org/messenger/content-policy;1"].getService(
+ Ci.nsIMsgContentPolicy
+ );
+
+ const context_uri = Services.io.newURI("moz-extension://00000000-0000-0000-0000-000000000000/index.html");
+ const principal = Services.scriptSecurityManager.createContentPrincipal(context_uri, {});
+
+ const resource_uri = Services.io.newURI("ws://127.0.0.1:12345");
+ const channel = NetUtil.newChannel({
+ uri: resource_uri,
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_WEBSOCKET,
+ });
+
+ const decision = content_policy.shouldLoad(
+ resource_url,
+ channel.loadInfo
+ );
+
+ Assert.equal(
+ decision,
+ Ci.nsIContentPolicy.ACCEPT,
+ "moz-extensions should load webextensions"
+ );
+}
diff -r 8819c8ab92f6 mailnews/base/test/unit/xpcshell.ini
--- a/mailnews/base/test/unit/xpcshell.ini Sat Nov 07 16:42:00 2020 +0100
+++ b/mailnews/base/test/unit/xpcshell.ini Wed Dec 02 04:07:11 2020 +0100
@@ -9,6 +9,7 @@
[test_accountMigration.js]
[test_acctRepair.js]
[test_bccInDatabase.js]
+[test_bug1675589.js]
[test_bug428427.js]
[test_bug434810.js]
[test_bug471682.js]
Unfortunately this fails with:
Unexpected Results
------------------
FAIL comm/mailnews/base/test/unit/test_bug1675589.js - xpcshell return code: 0
ERROR NS_ERROR_NOT_IMPLEMENTED: Component returned failure code: 0x80004001 (NS_ERROR_NOT_IMPLEMENTED) [nsIIOService.newChannelFromURI]
NetUtil_newChannel@resource://gre/modules/NetUtil.jsm:311:31
run_test@/home/me/prog/thunderbird/obj-x86_64-pc-linux-gnu/_tests/xpcshell/comm/mailnews/base/test/unit/test_bug1675589.js:15:27
_execute_test@/home/me/prog/thunderbird/testing/xpcshell/head.js:562:7
@-e:1:1
I'm in way over my head so figuring out the correct sequence of characters will take me some time :)
Assignee | ||
Comment 28•3 years ago
|
||
Now I've got this, which looks correct to my unexperienced eyes, but which doesn't fail when my patch isn't present (so it's not correct :( ):
/* -*- mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Test for bug 1675589.
*/
function run_test() {
const content_policy = Cc["@mozilla.org/messenger/content-policy;1"].getService(
Ci.nsIContentPolicy
);
const msg_content_policy = content_policy.QueryInterface(
Ci.nsIMsgContentPolicy
);
const context_uri = Services.io.newURI("moz-extension://00000000-0000-0000-0000-000000000000/index.html");
const principal = Services.scriptSecurityManager.createContentPrincipal(context_uri, {});
const resource_uri = Services.io.newURI("ws://127.0.0.1:12345");
const channel = Cc["@mozilla.org/network/protocol;1?name=ws"].createInstance(
Ci.nsIWebSocketChannel
);
channel.initLoadInfo(
null, // aLoadingNode
principal,
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
Ci.nsIContentPolicy.TYPE_WEBSOCKET,
);
const decision = msg_content_policy.shouldLoad(
resource_uri,
channel.loadInfo,
null,
null
);
Assert.equal(
decision,
Ci.nsIContentPolicy.ACCEPT,
"moz-extensions should load webextensions"
);
}
Comment 29•1 year ago
|
||
Hi glacambre,
I am going through old bugs which went stale. This and Bug 1675589 are probably not fixable, they both fail due to the iframe.
What I tried instead is the following compose script:
// I do not have a local websocket server, so I used this free test tool at
// https://www.piesocket.com/websocket-tester
const socket = new WebSocket("wss://demo.piesocket.com/v3/channel_123?api_key=<redacted>¬ify_self");
// Connection opened
socket.addEventListener('open', (event) => {
socket.send('Hello Server!');
});
// Listen for messages
socket.addEventListener('message', (event) => {
console.log('Message from server ', event.data);
const newContent = document.createTextNode(event.data);
document.body.appendChild(newContent);
});
And this works in "new" composers and "reply" composers: it fills the composer with responses from the server. This means websocket connections work in compose scripts, but not within an iframe.
What you could do is to add a div at the top of the composer and then update the content of that div (or replace the entire body).
Does that work for you?
The iframe in the compose window is not something we want to support at the moment, so I will close this as wontfix.
Assignee | ||
Comment 30•1 year ago
|
||
Hi TbSync, thanks a lot for coming back to this bug after all this time.
What you could do is to add a div at the top of the composer and then update the content of that div (or replace the entire body). Does that work for you?
I had to give up on the iframe a long time ago due to https://bugzilla.mozilla.org/show_bug.cgi?id=1707449 , and IIRC indeed managed to do without for a while :).
Please feel free to mark RESOLVED+WONTFIX any/all of the thunderbird bugs/requests I made before 2023, I don't need these improvements anymore due to being blocked by https://bugzilla.mozilla.org/show_bug.cgi?id=1746468 .
Description
•