XSS on https://account-api.protonmail.com (/challenge/v4/html)

While being a “sandbox” domain its same-site and is used for UI elements on the login page so would be good for phishing :)

window.addEventListener("message", (function(e) {
  if (e.source === window.parent) {
  var t = e.data, n = t && t.type, i = t && t.payload;
  if (t && n && i && ("load" === n && (document.querySelector("#styles-root").innerHTML = i.stylesRoot || "",
  document.querySelector("#icons-root").innerHTML = i.iconsRoot || "",

Because of the window.parent an iframe has to be used to get the XSS but that was possible since it had no embedding protection, now fixed with CSP frame-ancestors.

frame.contentWindow.postMessage({type: 'load', payload: {iconsRoot: '<img src="https://http.cat/200">'}}, '*');

XSS on https://secure.protonmail.com

The message listener had a same-site check but accepted any origin that contained protonmail.

function Pn(t) {
  t = 0 < arguments.length && void 0 !== t ? t : "";
  return /localhost:\d{4}$/.test(t) || /protonmail/.test(t)

Not possible on the proton.me domain.

Leaking email contents (XS-Search)

An android icon was cached based on the result of a search and the application does not use the Cross-Origin-Opener-Policy or have a Vary Sec-Fetch-Site header.
This allowed for a XS-Search attack and was eventually fixed by removing the image.
Cache partitioning does not prevent this because of the same-site XSS and because the asset is same-site as the application so the partitioned cache bypass applies.
“Search message content” is opt-in per https://proton.me/support/search so it may be limited to the email subject for some users, such attack would also need an idle user or a popunder/tabunder to not be suspicious due to the top-level navigations.