Exploiting Zoom whiteboard via the clipboard. Nice find from @spaceraccoon

https://spaceraccoon.dev/analyzing-clipboardevent-listeners-stored-xss/

I Hope This Sticks: Analyzing ClipboardEvent Listeners for Stored XSS

When is copy-paste payloads not self-XSS? When it’s stored XSS. Recently, I reviewed a Zoom’s code to uncover an interesting attack vector.

@gaz @spaceraccoon Nice writeup!

Looking at this with a browser's hat on, I wonder what we could do to make the `convertToText()` mechanism go away. The new `Element.setHTML()` method aims to handle element nodes being added to the DOM. It doesn't do much for attributes... I wonder if that's a pattern common-enough for there to be something here we should be generalizing?

@gaz @spaceraccoon That said, it's a somewhat straightforward wrapper in userland:

```
let temp = document.createElement('div');
temp.setHTML(data);
el.attribute = temp.innerHTML;
```

/cc @freddy

@mikewest @gaz @spaceraccoon @freddy from what I understand, the issue was not due to transporting HTML in an attribute, but rather that later regexes were used to transform that HTML into text-ish form. That processed text later skipped sanitization via DOMPurify (and it would just as likely skip setHTML?). Perhaps DOMPurify wasn't used because it would destroy some of the legitimate payload, it's not clear, but to me it looks like an 'unchecked flows to raw sinks are possible' problem, and not a 'sanitizer is missing' problem. The authors knew untrusted HTML needed sanitizing, they just didn't sanitize there for some reason.

@koto @gaz @spaceraccoon @freddy My read of the last step was that they _did_ use `convertToText()` as a sanitizer for input to the `dangerouslySetInnerHTML` sink, but that their implementation was a regex that didn't handle multi-line strings. :)

Obviously removing the use of the dangerous sink would be better. But given that developers gonna develop, I wonder whether there's anything we can learn from this for the Sanitizer API.

@mikewest @koto @spaceraccoon @freddy Yep Mike is correct because they used "." without /m it wouldn't match new lines.

@gaz @mikewest @spaceraccoon @freddy hm, so they assumed the regexes are strict enough and skipped sanitizing with a full-blown sanitizer afterwards? There was definitely an attempt to sanitize.

DOMPurify was available and used in this codebase, IIUC. If DOMPurify.sanitize(s) was not called, is it more likely that a 2-liner using setHTML would?