Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set iframe height in javascript #747

Merged
merged 3 commits into from
Jun 4, 2024
Merged

Conversation

jorsn
Copy link
Member

@jorsn jorsn commented May 28, 2024

This does not allow the document (message content) to execute javascript, as can be verified with the following email, which displays "no scripts" by default, but "scripts active" when js is active.

Date: Tue, 1 January 1970 00:00:00 +0000
From:
Subject:
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="=-TUjG03Ah9OcLtWE56Ucm"

--=-TUjG03Ah9OcLtWE56Ucm
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<div id=3D"jscontent">
  no scripts
</div>

<script>
  d =3D document.getElementById('jscontent');
  d.innerHTML =3D "scripts active";
</script>

--=-TUjG03Ah9OcLtWE56Ucm
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html>
<html xmlns=3D"http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset=3D"utf-8" />
  <meta name=3D"generator" content=3D"pandoc" />
  <meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=3D=
1.0, user-scalable=3Dyes" />
  <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
</head>
<body>
<div id=3D"jscontent">
<p>no scripts</p>
</div>
<script>
  d =3D document.getElementById('jscontent');
  d.innerHTML =3D "scripts active";
</script>
</body>
</html>

--=-TUjG03Ah9OcLtWE56Ucm--

@jorsn jorsn changed the title set iframe size in javascript set iframe height in javascript May 28, 2024
@ibuclaw
Copy link
Contributor

ibuclaw commented May 28, 2024

I was curious to give this a whirl. The iframe height is set too high a value.

image

Only after toggling between text/plain and text/html does the height correctly readjust itself.

@jorsn
Copy link
Member Author

jorsn commented May 28, 2024

Strange, for me it display fine.

Do you display plain or html by default?
Do you have a sample email for which this happens?

@ibuclaw
Copy link
Contributor

ibuclaw commented May 29, 2024

Strange, for me it display fine.

    "thread_view": {
        "open_html_part_external": "false",
        "preferred_type": "plain",
        "preferred_html_only": "true",
        "allow_remote_when_encrypted": "false",
        "open_external_link": "xdg-open",
        "default_save_directory": "~",
        "indent_messages": "false",
        "gravatar": {
            "enable": "true"
        },
        "mark_unread_delay": "0.5",
        "expand_flagged": "false"
    },

Do you display plain or html by default? Do you have a sample email for which this happens?

i.e: All mail I get from GitHub notifications.

Ah! I've just checked again, and it only affects messages that are collapsed when opening in the thread (i.e: unread tag removed). Expanding them is what triggers the above screenshot.

(I'm also running with a variant of #745 as astroid no longer compiles on my system, so it might only be a GTK4 thing).

@gauteh
Copy link
Member

gauteh commented May 29, 2024

Hi. I don't have much capacity to test, if some of you want to help maintain I can add you.

@jorsn
Copy link
Member Author

jorsn commented May 29, 2024

Alright, I can now also reproduce it. It has nothing to do with #745 nor with the astroid settings.

Sometimes, it is fixed by adding loading=lazy to the iframe. But this does not always work.

Is there any way to inspect the final html/css/js page? EDIT: I found the webinspector hidden behind cmake -DDEBUG_WEBKIT=ON.
Debugging seems next to impossible without. Somehow it seems that no event besides load fires at all, for anything that happens to the page. In principle, resize events for all elements of class email should work in principle, since as far as I read, display: none sets the dimensions to zero.

@jorsn
Copy link
Member Author

jorsn commented May 29, 2024

@ibuclaw I pushed a new commit that should fix the over-sizing. Does it work for you now?

@ibuclaw
Copy link
Contributor

ibuclaw commented May 31, 2024

@ibuclaw I pushed a new commit that should fix the over-sizing. Does it work for you now?

Looks good to me.

@jorsn jorsn mentioned this pull request May 31, 2024
@jorsn
Copy link
Member Author

jorsn commented May 31, 2024

Great! Are there any objections agains the enabling of javascript? It is disabled in the iframe.

Otherwise, we can merge this, can't we?

@gauteh
Copy link
Member

gauteh commented May 31, 2024

The only one is that a sender could add javascript in the subject or in the email address. But I think that is possible to sanitize. As mentioned previously, I would be happy to give someone else access to merge.

@jorsn
Copy link
Member Author

jorsn commented Jun 1, 2024

The only one is that a sender could add javascript in the subject or in the email address. But I think that is possible to sanitize.

Isn't this already sanitized by Glib::Markup::escape_text(...)? I checked every call to webkit_dom_element_set_inner_html, and I am pretty confident that every occurrence is sanitized in that way if it appears outside .body_iframes in the end and contains data (in contrast to user-supplied config and program-generated things like error messages).

As mentioned previously, I would be happy to give someone else access to merge.

I could take it, and help a bit with maintenance, especially when something breaks for me, but I can't tell you how much and for how long: I'm only willing to do it as long as I continue to use astroid, and I cannot give any guarantees on my time budget for this.
Further, I don't have any substantial C++, web or browser development experience, just general programming now and then.

@gauteh
Copy link
Member

gauteh commented Jun 1, 2024

Thanks, i appreciate that and am happy for what you can and have time to do.

@gauteh
Copy link
Member

gauteh commented Jun 1, 2024

I think sanitize should do the trick 👍

@jorsn
Copy link
Member Author

jorsn commented Jun 2, 2024

What is 'sanitize'? The JS method? How would you invoke it safely, here?

I think, if escape_text() is insufficient, the way to go is to set innerText instead of innerHTML. For this, one has to generate the header table without the data first, and then populate with the data.

@gauteh
Copy link
Member

gauteh commented Jun 2, 2024

Innertext is safer yes. I was using sanitize and escape interchangeably.

@jorsn
Copy link
Member Author

jorsn commented Jun 3, 2024

I changed the setting of headers and preview to use innerText.
I think, this can be merged, but I don't seem to have write access to the repo, even if I am in the organization, now. At least, I cannot accept PRs.

@gauteh
Copy link
Member

gauteh commented Jun 3, 2024

Looks good. I tried adding you to this repo as well, please try again.

@jorsn jorsn force-pushed the fix-msg-height/js branch 3 times, most recently from 977f0fe to eef74fd Compare June 4, 2024 22:07
jorsn and others added 3 commits June 5, 2024 00:10
This does not allow the email html document (message content) to execute
javascript, as can be verified with the following email,
which displays "no scripts" by default, but "scripts active" when js is
active.

```
Date: Tue, 1 January 1970 00:00:00 +0000
From:
Subject:
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary="=-TUjG03Ah9OcLtWE56Ucm"

--=-TUjG03Ah9OcLtWE56Ucm
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<div id=3D"jscontent">
  no scripts
</div>

<script>
  d =3D document.getElementById('jscontent');
  d.innerHTML =3D "scripts active";
</script>

--=-TUjG03Ah9OcLtWE56Ucm
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html>
<html xmlns=3D"http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset=3D"utf-8" />
  <meta name=3D"generator" content=3D"pandoc" />
  <meta name=3D"viewport" content=3D"width=3Ddevice-width, initial-scale=3D=
1.0, user-scalable=3Dyes" />
  <style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
</head>
<body>
<div id=3D"jscontent">
<p>no scripts</p>
</div>
<script>
  d =3D document.getElementById('jscontent');
  d.innerHTML =3D "scripts active";
</script>
</body>
</html>

--=-TUjG03Ah9OcLtWE56Ucm--
```
this possibly makes it harder to inject arbitrary html/scripts via email headers or tags
@jorsn jorsn merged commit 6af16fd into astroidmail:master Jun 4, 2024
2 of 4 checks passed
@ibuclaw
Copy link
Contributor

ibuclaw commented Jun 4, 2024

@jorsn thanks for tackling this!

Comment on lines +60 to +69
<!--
sandbox="allow-same-origin" is required, otherwise the scrollHeight of the
embedded document cannot be accessed.
Do not add 'allow-scripts' to the sandbox attribute. This would allow the
document to execute scripts itself.
-->
<iframe class="body_iframe" sandbox="allow-same-origin" onload="handleLoadIframe(this);" srcdoc=""></iframe>
</div>

<script>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a WebKitSettings:enable-javascript-markup setting you can use to remove any doubt; that allows the application to run JavaScript but blocks the document from doing so. That would block the use of <script> here, but you could probably run it manually using webkit_web_view_evaluate_javascript().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems good for (faith in) security. But I did not see how I can reference this (the iframe) in cpp in a way such that I can pass it to the javascript function handleLoadIframe.

At least, I expect it to be a bit complicated.

Are your doubts strong, currently?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I did not see how I can reference this (the iframe) in cpp in a way such that I can pass it to the javascript function handleLoadIframe.

The best solution is the reverse: don't try to access it from C++. Move work that touches the DOM into JavaScript instead. The C++ DOM APIs are all deprecated and already gone in the webkitgtk-6.0 API version, so they'll be a problem for you in the future.

Are your doubts strong, currently?

No. I think the sandbox attribute is good enough because you're careful to encode everything so it can't break out. I think you're good so long as you use Glib::Markup::escape_text on all the untrusted input, and your comment says you do that.

This guideline suggests you don't use inner HTML at all, but if all untrusted input is properly encoded then I'm not sure why not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Yes, in the long run, this schould be done, maybe one can just pass the emails as json to the javascript somehow, (anyway, it seems you can only pass data to js by injecting js strings, so it would be good to have dead json) and then use js to build the whole threadview. But this one is even more work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants