29 comments

[ 2.0 ms ] story [ 50.0 ms ] thread
So is this basically a safe version of innerHTML?
We enabled this by default in Firefox Nightly (only) this week.
Thank you for the effort to bring this to life together with Freddy!
> This feature is not Baseline because it does not work in some of the most widely-used browsers.

This is interesting, but it appears to be in its early days as none of the major browsers seem to support it.. yet.

Really happy to see it, after 25 years (https://www.bugcrowd.com/glossary/cross-site-scripting-xss/) of surviving without it. It always struck me as an obvious missing part of the DOM API, and I still don't know why it took this long time.

But mostly I'm just happy that it's finally here, I do appreciate all the hard work people been doing to get this live.

I think DOM api people just really wanted everyone to use .appendChild() methods.

I think there is an interesting lesson here about how security is partially an ergonomic problem.

Great functionality, terrible name.
The name seems ideal to me.
Neat. I think once this is adopted by HTMX (or similar libraries) you don't need to sanitize on the server side anymore?
I interpreted your question as "do I now no longer need to escape user-generated data in the HTML sent by the server in response to requests by HTMX?" The short answer is no, you still need to escape it:

- HTMX adds extra significance to HTML attributes which aren't accounted for by the built-in sanitizer

- HTMX can't add a custom sanitizer because it wouldn't be able to distinguish between intentional and malicious uses of those attributes

- Even if the HTMX client library sanitized all of the HTML from the server, you can't guarantee that all requests to the server will come from HTMX: browsers can navigate to your "back-end" URLs directly. While you can protect yourself from this using HTTP headers, that's not something I'd feel comfortable relying on since it would be easy to not notice when you've accidentally gotten it wrong.

The HTMX website has a longer explainer on how to protect yourself from XSS when using the library:

https://htmx.org/essays/web-security-basics-with-htmx/

> It then removes any HTML entities that aren't allowed by the sanitizer configuration, and further removes any XSS-unsafe elements or attributes — whether or not they are allowed by the sanitizer configuration.

Emphasis mine. I do not understand this design choice. If I explicitly allow `script` tag, why should it be stripped?

If the method was called setXSSSafeSubsetOfHTML sure I guess, but feels weird for setHTML to have impossible-to-override filter.

The API design could be better. Document fragments are designed to be reused. It should accept an optional fragment key which accepts a document fragment.If not a fragment, throw, if has children, empty contents first.
As someone who has dealt with more than my fair share of content injection vulnerabilities over the years this is great to see at last. It’s kinda crazy that this only coming now while other, more cumbersome solutions like CSP have been around for years.
Maybe it is then time for having something that is beyond "use strict" at the beginning auf a JavaScript document as one option to use the statement.

I think a config object in which you define for script options like sanitization and other script configuration might be helpful.

After all, there almost always need to be backward compatibility be ensured, and this might work. I am no spec guy, it is just an idea. React makes use of "use client/server", so this would be more central and explicit.

ESM modules are already in strict mode by default.
A somewhat related spec, at the page level rather than the module level, are Content Security Policies, which let a page disable various unsafe browser features for a page: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP

One of my favorite features in there is trusted types enforcement: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Typ...

Lets you create your own API for what code is allowed to create arbitrary, potentially unsafe HTML at runtime, so you can allow secure templating systems but disallow code that just concats strings together naively.

So `.setHTML("<script>...</script>")` does not set HTML?
I don't like this. This could be implemented as a JS library. I believe browsers should provide the minimal API so that they are smaller and easier to create. As for safe alternative to innerHTML, it is called innerText.
Is “XSS-unsafe” precisely defined anywhere? I assume it means “any access to the JS interpreter”, but assuming in this context seems decidedly unsafe.
So this is the easier, built in successor to

    innerHTML = trustedTypes.createPolicy('myPolicy', {
  createHTML: (input) => DOMPurify.sanitize(input, {RETURN_TRUSTED_TYPE: true})
}).createHTML()

?

This is goood news for me. Finally! A safer and more predictable alternative to innerHTML.
Since 5 years everybody says Jquery us no longer necessary. But really baisc function like this take a long time for replacing Jquery
>Verbose I/O element

Parsing > "DocumentFragment"

Returns proc. exit status [0]/[1] for browser HTML incompatability.

That's really good to hear.

I've found LLMs will happily generate XSS vulnerable code, which will make things worse for a while until they can be trained better.

In fact, I found it really difficult to get claude-code to use templating libraries and not want to default to hand-written templating with XSS vulnerabilties and injecting content directly, even after going through options with it.

There's also a difference between escaping and sanitisation which can be tricky to handle and track, and it can even be dangerous to try to mix different approaches or sanitizers.

Having a safe backstop in the form of setHTML() to use will be a fantastic addition to narrow the scope of ways to get it wrong.

This whole page renders extremely poorly on mobile, beyond usable I'd say.
I don't really get it. Why this is needed on client? If all we want is to prevent XSS attacks, wouldn't it be more effective to sanitize on server? Am I missing something?