[HN Gopher] Goodbye InnerHTML, Hello SetHTML: Stronger XSS Prote...
       ___________________________________________________________________
        
       Goodbye InnerHTML, Hello SetHTML: Stronger XSS Protection in
       Firefox 148
        
       Author : todsacerdoti
       Score  : 319 points
       Date   : 2026-02-24 13:04 UTC (9 hours ago)
        
 (HTM) web link (hacks.mozilla.org)
 (TXT) w3m dump (hacks.mozilla.org)
        
       | entuno wrote:
       | This kind of thing always makes me nervous, because you end with
       | a mix of methods where you can (supposedly) pass arbitrary user
       | input to them and they'll safely handle it, and methods where you
       | can't do that without introducing vulnerabilities - but it's not
       | at all clear which is which from the names. Ideally you design
       | that in from the state, so any dangerous functions are very
       | clearly dangerous from the name. But you can't easily do that
       | down the line.
       | 
       | I'm also rather sceptical of things that "sanitise" HTML, both
       | because there's a long history of them having holes, and because
       | it's not immediately clear what that means, and what exactly is
       | considered "safe".
        
         | voxic11 wrote:
         | The idea is you wouldn't mix innerHTML and setHTML, you would
         | eliminate all usage of innerHTML and use the new setHTMLUnsafe
         | if you needed the old functionality.
        
           | post-it wrote:
           | > you would eliminate all usage of innerHTML
           | 
           | The mythical refactor where all deprecated code is replaced
           | with modern code. I'm not sure it has ever happened.
           | 
           | I don't have an alternative of course, adding new methods
           | while keeping the old ones is the only way to edit an append-
           | only standard like the web.
        
             | noduerme wrote:
             | Finally, a good use case for AI.
        
               | josefx wrote:
               | Wouldn't AI be trained on data using innerHTML?
        
               | Aachen wrote:
               | My experience is that they somehow print quite modern
               | code despite things like ES6 being too new to be standard
               | knowledge even for me and I'm not even middle-aged yet
               | 
               | Maybe the last 10 years saw so much more modern code than
               | the last cumulative 40+ years of coding and so modern
               | code is statistically more likely to be output? Or maybe
               | they assign higher weights to more recent commits/sources
               | during training? Not sure but it seems to be good at
               | picking this up. And you can always feed the info into
               | its context window until then
        
               | skeeter2020 wrote:
               | This is not my experience. Claude has been happily
               | generating code over the past week that is full of
               | implicit any and using code that's been deprecated for at
               | least 2 years.
               | 
               | >> Maybe the last 10 years saw so much more modern code
               | than the last cumulative 40+ years of coding and so
               | modern code is statistically more likely to be output?
               | 
               | The rate of change has made defining "modern" even more
               | difficult and the timeframe brief, plus all that new code
               | is based on old code, so it's more like a leaning tower
               | than some sort of solid foundation.
        
               | SahAssar wrote:
               | ES6 is 11 years old. It's not that new.
        
               | chrisweekly wrote:
               | > "ES6 being too new to be standard knowledge"
               | 
               | Huh? It's been a decade.
        
               | charcircuit wrote:
               | Which is why it can easily understand how innerHTML is
               | being used so that it can replace it with the right
               | thing.
        
               | stvltvs wrote:
               | Honest question: Is there a way to get an LLM to stop
               | emitting deprecated code?
        
               | fragmede wrote:
               | Theoretically, if you could train your own, and remove
               | all references to the deprecated code in the training
               | data, it wouldn't be able to emit deprecated code.
               | Realistically that ability is out of reach at the
               | hobbiest level so it will have to remain theoretical for
               | at least a few more iterations of Moore's law.
        
               | Aachen wrote:
               | Yeah, using a kilowatt GPU for string replacement is
               | going to be the killer feature. I probably shouldn't even
               | be joking, people are using it like this already
        
               | charcircuit wrote:
               | When the condition for when you want to replace is hard
               | to properly specify, AI shines for such find and
               | replaces.
        
               | Aachen wrote:
               | This one is literally matching "innerHTML = X" and
               | setting "setHTML(X)" instead. Not some complex data
               | format transformation
               | 
               | But I can see what you mean, even if then it would still
               | be better for it to print the code that does what you
               | want (uses a few Wh) than doing the actual transformation
               | itself (prone to mistakes, injection attacks, and uses
               | however many tokens your input data is)
        
               | charcircuit wrote:
               | That can break the site if you do the find and replace
               | blindly. The goal here is to do the refactor without
               | breaking the site.
        
               | lelanthran wrote:
               | > When the condition for when you want to replace is hard
               | to properly specify, AI shines for such find and
               | replaces.
               | 
               | And, in your opinion, this is one of those cases?
        
               | charcircuit wrote:
               | It is because the new API purposefully blocks things the
               | old API did not.
        
               | littlestymaar wrote:
               | This ship has sailed unfortunately, no later than
               | yesterday I've seen coworkers redact a screenshot using
               | chatGTP.
        
             | Vinnl wrote:
             | I kinda like the way JS evolved into a modern language,
             | where essentially ~everyone uses a linter that e.g.
             | prevents the use of `var`. Sure, it's technically still in
             | the language, but it's almost never used anymore.
             | 
             | (Assuming transpilers have stopped outputting it, which I'm
             | not confident about.)
        
               | thunderfork wrote:
               | Depending on the transpiler and mode of operation, `var`
               | is sometimes emitted.
               | 
               | For example, esbuild will emit var when targeting ESM,
               | for performance and minification reasons. Because ESM has
               | its own inherent scope barrier, this is fine, but it
               | won't apply the same optimizations when targeting (e.g.)
               | IIFE, because it's not fine in that context.
               | 
               | https://github.com/evanw/esbuild/issues/1301
        
               | delaminator wrote:
               | for some values of "everyone" and "never".
        
               | yawaramin wrote:
               | Actually...
               | https://github.com/microsoft/TypeScript/issues/52924
        
               | Vinnl wrote:
               | Ah yeah, I remember that. General point still stands: in
               | terms of the lived experience of developers, `var` is
               | essentially deprecated.
        
               | plorkyeran wrote:
               | I touch JS that uses var heavily on a daily basis and I
               | would be incredibly surprised to find out that I am alone
               | in that.
        
             | thenewnewguy wrote:
             | If you want to adopt this in your project, you can add a
             | linter that explicitly bans innerHTML (and then go fix the
             | issues it finds). Obviously Mozilla cannot magically fix
             | the code of every website on the web but the tools exist
             | for _your_ website.
        
             | bulbar wrote:
             | It for sure happens for drop in replacements.
        
             | littlestymaar wrote:
             | Nobody's talking about old code here.
             | 
             | Having an alternative to innerHTML means you can ban it
             | from new code through linting.
        
           | croes wrote:
           | If I need the old functionality why not stick to innerHTML?
        
             | orf wrote:
             | because the "unsafe" suffix conveys information to the
             | reader, whereas `innherHTML` does not?
        
               | goatlover wrote:
               | Any potential reader should be familiar with innerHTML.
        
               | kennywinker wrote:
               | Right. Like how any potential reader is familiar with the
               | risks of sql injection which is why nothing has ever been
               | hacked that way.
               | 
               | Or how any potential driver is familiar with seat belts
               | which is why everybody wears them and nobody's been
               | thrown from a car since they were invented.
        
               | orf wrote:
               | yes, and bugs shouldn't exist because everyone should be
               | familiar with everything.
        
               | croes wrote:
               | But if some are marked unsafe and others are not it gives
               | a false sense of security if something is not marked
               | unsafe.
        
               | orf wrote:
               | So we shouldn't mark anything as unsafe then? And give no
               | indication whatsoever?
               | 
               | The issue isn't that the word "safe" doesn't appear in
               | safe variants, it's that "unsafe" makes your intentions
               | clear: "I know this is unsafe, but it's fine because of X
               | and Y".
        
               | croes wrote:
               | Maybe we should add the word safe and consider everything
               | else as unsafe
        
               | orf wrote:
               | Like life, things should default to being safe. Unsafe,
               | unexpected behaviours should be exception and thus
               | require an exceptional name.
               | 
               | Legacy and backwards compatibility hampers this, but
               | going forward...
        
             | tbrownaw wrote:
             | Because then your linter won't be able to tell you when
             | you're done migrating the calls that can be migrated.
        
             | philipwhiuk wrote:
             | Because sooner or later it'll be removed.
        
               | croes wrote:
               | And break millions of sites?
        
               | goatlover wrote:
               | No because the web has to remain backwards compatible
               | with older sites. This has always been the case.
        
           | reddalo wrote:
           | You can't rename an existing method. It would break
           | compatibility with existing websites.
        
           | extraduder_ire wrote:
           | I looked up setHTMLUnsafe on MDN, and it looks like its been
           | in every notable browser since last year.
           | 
           | Good idea to ship that one first, when it's easier to
           | implement and is going to be the unsafe fallback going
           | forward.
        
             | onion2k wrote:
             | _I looked up setHTMLUnsafe on MDN, and it looks like its
             | been in every notable browser since last year._
             | 
             | Oddly though, the Sanitizer API that it's built on doesn't
             | appear to be in Safari. https://developer.mozilla.org/en-
             | US/docs/Web/API/Sanitizer
        
         | DoctorOW wrote:
         | They do link the default configuration for "safe":
         | https://wicg.github.io/sanitizer-api/#built-in-safe-default-...
         | 
         | But I agree, my default approach has usually been to only use
         | innerText if it has untrusted content:
         | 
         | So if their demo is this:
         | container.SetHTML(`<h1>Hello, {name}</h1>`);
         | 
         | Mine would be:                   let greetingHeader =
         | container.CreateElement("h1");         greetingHeader.innerText
         | = `Hello, {name}`;
        
           | itishappy wrote:
           | What if I wanted an <h2>?
           | 
           | Edit: I don't mean this flippantly. If I want to render, say,
           | my blog entry on your site, will I need to select every
           | markup element from a dropdown list of custom elements that
           | only accept text a la Wordpress?
        
             | DoctorOW wrote:
             | If it's anything complex I'm doing it server side,
             | personally
        
         | post-it wrote:
         | realSetSafeHTML()
        
         | jncraton wrote:
         | You are right that the concept of "safe" is nebulous, but the
         | goal here is specifically to be XSS-safe [1]. Elements or
         | properties that could allow scripts to execute are removed.
         | This functionality lives in the user agent and prevents adding
         | unsafe elements to the DOM itself, so it should be easier to
         | get correct than a string-to-string sanitizer. The logic of "is
         | the element currently being added to the DOM a <script>" is
         | fundamentally easier to get right than "does this HTML string
         | include a script tag".
         | 
         | [1] https://developer.mozilla.org/en-
         | US/docs/Web/API/Element/set...
        
           | entuno wrote:
           | It's certainly an improvement over people trying to homebrew
           | their own sanitisers. But that distinction of being XSS-safe
           | is a potentially subtle one, and could end up being dangerous
           | if people don't carefully consider whether XSS-safe is good
           | enough when they're handling arbitrary users input like that.
        
           | intrasight wrote:
           | Also has made me nervous for years that there's been no
           | schema against which one can validate HTML. "You want to
           | validate? Paste your URL into the online validation tool."
        
             | Dylan16807 wrote:
             | This help? https://github.com/validator/validator
             | 
             | But for html snippets you can pretty much just check that
             | tags follow a couple simple rules between <> and that
             | they're closed or not closed correctly.
        
               | intrasight wrote:
               | That app does look helpful!
        
         | snowhale wrote:
         | the browser-native Sanitizer API has one advantage the library
         | approaches don't: it uses the same HTML parser the browser uses
         | to render. libraries like DOMPurify parse in a separate context
         | then re-serialize, and historically that round-trip is where
         | most bypasses came from. when the sanitizer and the renderer
         | share the same parser, mutation XSS attacks have nowhere to
         | hide.
        
           | pornel wrote:
           | BTW, HTML allows inline SVG with an XML-flavored syntax that
           | interprets <script/> and <title> differently. It's a goldmine
           | for sanitizer escapes. There are completely bonkers syntax
           | switching and error recovery rules that interact with parsing
           | modes (there's even an edge case where a particular
           | _attribute value_ switches between HTML and XML-ish parsing
           | rules).
           | 
           | Don't even try to allow inline <svg> from untrusted sources!
           | (and then you still must sanitise any svg files you host)
        
             | kccqzy wrote:
             | If you just serve SVGs through <img> tag it'll be much
             | safer. I never understood the appeal of inline <svg>
             | anyways.
        
               | rwj wrote:
               | Inline reduces round trips.
        
               | toast0 wrote:
               | You can use img with a data url?
        
               | lenkite wrote:
               | Inline SVG is stylable with CSS styles in the same HTML
               | page.
        
               | runarberg wrote:
               | Also animatible with the same context (Animation API,
               | etc.) as the parent page, so different SVGs can influence
               | each other's animations.
        
           | cxr wrote:
           | It may be using some of the same deserialization machinery,
           | but "parsing" is a broad term that includes things that the
           | sanitizer is doing and that the browser's ordinary content-
           | processing - rendering path does not.
           | 
           | Even with this being a native API, there are still two
           | parsers that need to be maintained. What a native API
           | achieves is to shift the onus for maintaining synchronicity
           | between the two onto the browser makers. That's not nothing,
           | but it's also not the sort of free lunch that some people
           | naively believe it is.
        
         | noduerme wrote:
         | Some sanitization is better than none? If you're relying on the
         | browser to handle it for you, you're already in a lot of
         | trouble.
        
         | jaffathecake wrote:
         | fwiw, if you serve your page with:
         | 
         | Content-Security-Policy: require-trusted-types-for 'script'
         | 
         | ...then it blocks you from passing regular strings to the
         | methods that don't sanitize.
        
         | Cthulhu_ wrote:
         | Ideally you should be able to set a global property somewhere
         | (as a web developer) that disallows outdated APIs like
         | `innerHTML`, but with the Big Caveat that your website will not
         | work on browsers older than X. But maybe there's web standards
         | for that already, backup content if a browser is considered
         | outdated.
        
           | afavour wrote:
           | I like the idea of that. But I imagine linting rules are a
           | much more immediate answer in a lot of projects.
        
           | staticassertion wrote:
           | Doesn't using TrustedTypes basically do that? I'm not really
           | web-y, someone please correct me if I'm off.
        
             | madeofpalk wrote:
             | Yup, this is basically what TrustedTypes is for!
        
           | cxr wrote:
           | It's not an "outdated API". It's still good for what it was
           | always meant for: parsing trusted, application-generated
           | markup and atomically inserting it into the content tree as a
           | replacement for a given element's existing children.
           | 
           | > set a global property somewhere (as a web developer) that
           | disallows[...] `innerHTML`
           | Object.defineProperty(Element.prototype, "innerHTML", {
           | set: (() => { throw Error("No!") })         });
           | 
           | (Not that you should actually do this--anyone who has to
           | resort to it in their codebase has deeper problems.)
        
         | onion2k wrote:
         | _it 's not at all clear which is which from the names_
         | 
         | There's setHTML and setHTMLUnsafe. That seems about as clear as
         | you can get.
        
           | hahn-kev wrote:
           | But you can use InnerHTML to set HTML and that's not safe.
        
             | onion2k wrote:
             | At this point that API has been around for decades and is
             | probably impossible to deprecate without breaking fairly
             | large amounts of the web. The only option is to introduce a
             | new and better API, and _maybe_ eventually have the browser
             | throw out console warnings if a page still uses the old
             | innerHTML API. I doubt any browser vendor will be gung ho
             | enough to actually remove it for a very long time.
        
           | entuno wrote:
           | If that'd been the design from the start, then sure. But it's
           | not at all obvious that setHTML is safe with arbitrary user
           | input (for a given value of "safe") and innerHTML is
           | dangerous.
        
         | HWR_14 wrote:
         | That's why I only allow user input of alphanumeric ascii
         | characters. No need to worry about sanitation then, and you can
         | just remove all the characters that don't match.
         | 
         | (It's a joke, but it is also 100% XSS, SQL injection, etc. safe
         | and future proof)
        
         | thaumasiotes wrote:
         | > I'm also rather sceptical of things that "sanitise" HTML,
         | both because there's a long history of them having holes, and
         | because it's not immediately clear what that means, and what
         | exactly is considered "safe".
         | 
         | What is safe depends on where the sanitized HTML is going, on
         | what you're doing with it.
         | 
         | It isn't possible to "sanitize HTML" after collecting it so
         | that, when you use it in the future, it will be safe. "Safe" is
         | defined by the use.
         | 
         | But it is possible to sanitize it _before using it_ , when you
         | know what the use will be.
        
         | cxr wrote:
         | > it's not at all clear which is which from the names. Ideally
         | you design that in from the [start]
         | 
         | It was, and there is: setting elementNode.textContent is safe
         | for untrusted inputs, and setting elementNode.innerHTML is
         | unsafe for untrusted inputs. The former will escape everything,
         | and the latter won't escape anything.
         | 
         | You are right that these "sanitizers" are fundamentally
         | confused:
         | 
         | > _" HTML sanitization" is never going to be solved because
         | it's not solvable.P There's no getting around knowing whether
         | or any arbitrary string is legitimate markup from a trusted
         | source or some untrusted input that needs to be treated like
         | text. This is a hard requirement._
         | 
         | <https://news.ycombinator.com/item?id=46222923>
         | 
         | The Web platform folks who are responsible for getting
         | fundamental APIs standardized and implemented natively are in a
         | position to know better, and they should know better. This API
         | should not have made it past proposal stage and should not have
         | been added to browsers.
        
           | Dylan16807 wrote:
           | > There's no getting around knowing whether or any arbitrary
           | string is legitimate markup from a trusted source or some
           | untrusted input that needs to be treated like text. This is a
           | hard requirement.
           | 
           | It is not a hard requirement that untrusted input is "treated
           | like text". And this API lets you customize exactly what
           | tags/attributes are allowed in the untrusted input. That's
           | way better than telling everyone to write their own; it's not
           | trivial.
        
       | simonw wrote:
       | Great to see this start to show up, but it looks like it will be
       | a while before browser support is widely distributed enough to
       | rely on it being present: https://caniuse.com/mdn-
       | api_element_sethtml
        
         | jraph wrote:
         | Indeed, as any browser API, it might be for in a few years
         | (months if happy with the most recent versions), and we may
         | have polyfills in the meantime.
        
           | tuyiown wrote:
           | I wouldn't advise polyfills on this one, it entirely depends
           | on the browser ability to evaluate cross scripting and cross
           | origin rule on a arbitrary snippet. This is not a convenience
           | API.
        
       | antonyh wrote:
       | A rather deceptive title, given that 'innerHTML' isn't going
       | away.
        
         | jandrese wrote:
         | I think the title is trying to convince you to switch from
         | InnerHTML to SetHTML.
        
       | tuyiown wrote:
       | This is nice. The best part is that all aspects of network access
       | are now properly controlled so that security transitioned from a
       | chain of trusted code to a chain of trusted security setup on
       | hosts, with existing workable safe defaults.
        
       | bingemaker wrote:
       | Nice one. Will there be any impact on __dangerouslySetInnerHTML
       | (React)?
        
       | Aachen wrote:
       | So you can still inject <h1> or <br><br><br>... etc into your
       | username, in the given example
       | 
       | Preventing one bug class (script execution) is good, but this
       | still allows arbitrary markup to the page (even <style> CSS
       | rules) if I'm reading the docs correctly. You could give Paypal a
       | fresh look for anyone who opens your profile page, if they use
       | this. Who would ever want this?
        
         | embedding-shape wrote:
         | > So you can still inject <h1> or <br><br><br>... etc into your
         | username, in the given example
         | 
         | How exactly, given that setHTML sanitizes the input? If you
         | don't want to have any HTML tags allowed, seems you can
         | configure that already? https://wicg.github.io/sanitizer-
         | api/#built-in-safe-default-...
        
           | Aachen wrote:
           | > How exactly, given that setHTML sanitizes the input?
           | 
           | The article says that the output is:
           | <h1>Hello my name is</h1>
           | 
           | So it keeps (non-script) html tags (and presumably also
           | attributes) in the input. Idk how you're asking "how" since
           | it's the default behavior
           | 
           | Stripping HTML tags completely has always been possible with
           | the drop-in replacement `textContent`. Making a custom
           | configuration object for that is much more roundabout
        
             | embedding-shape wrote:
             | Yes, because that's the default configuration, if you don't
             | want that, stop using the default configuration? It's still
             | sanitizing away the common XSS holes, hence it's a safer
             | alternative to .innerHTML, and a more flexible alternative
             | to .innerText
        
               | Aachen wrote:
               | Shouldn't use innerText anyway (nonstandard, worse
               | performance, tries to parse the HTML and gives you
               | unexpected behavior if e.g. a style is set that makes an
               | element invisible but still has text inside, doesn't work
               | on all DOM nodes...)
               | 
               | I can see how it's a way of allowing some tags like bold
               | and italic without needing a library or some custom
               | parser, but I didn't understand what the point of this
               | default could be and so why it exists (a sibling comment
               | proposed a plausible answer: hardening on top of another
               | solution)
               | 
               | > Yes, because that's the default configuration, if you
               | don't want that, stop using the default configuration?
               | 
               | "don't use it if it's not what you want" is perhaps the
               | silliest possible answer to the question "what's the use-
               | case for this"
        
               | embedding-shape wrote:
               | > Shouldn't use innerText anyway (nonstandard, worse
               | performance, tries to parse the HTML and gives you
               | unexpected behavior if e.g. a style is set that makes an
               | element invisible but still has text inside, doesn't work
               | on all DOM nodes...)
               | 
               | Maybe you meant .innerHTML? .innerText AFAIK doesn't try
               | to parse HTML (why would it?), but I don't understand
               | what you mean with nonstandard, both .innerHTML and
               | .innerText are part of the standards, and I think they've
               | been for a long time.
               | 
               | > but I didn't understand what the point of this default
               | could be and so why it exists (a sibling comment proposed
               | a plausible answer: hardening on top of another solution)
               | [...] the question "what's the use-case for this"
               | 
               | I guess maybe third time could be the charm: it's for
               | preventing XSS holes that are very common when people use
               | .innerHTML
        
               | Aachen wrote:
               | > maybe third time could be the charm: it's for
               | preventing XSS holes
               | 
               | That information is in the question, so sadly no this
               | still doesn't make sense to me because I don't understand
               | any scenario in which this is what the developer wants.
               | You always still need more code (to filter the right
               | tags) or can just use textContent (separating data and
               | code completely, imo the recommended solution)
               | 
               | > Maybe you meant .innerHTML? .innerText AFAIK doesn't
               | try to parse HTML (why would it?)
               | 
               | No, I didn't mean that, yes it does, and no I don't know
               | why it is this way. If you don't believe me and don't
               | want to check it out for yourself, I'm not sure what more
               | I can say
        
               | lelanthran wrote:
               | > I don't understand any scenario in which this is what
               | the developer wants.
               | 
               | Client-side includes.
        
               | benregenspan wrote:
               | It seems like the goal of the default configuration is
               | preventing script injection while being otherwise very
               | permissive. Basically, "safer than innerHTML, even when
               | used very lazily". But I would expect guidance to evolve
               | saying that it almost never makes sense to use the
               | default and instead to specify a configuration that makes
               | contextual sense for a given field.
               | 
               | The default might be suitable for something like an
               | internal blog where you want to allow people to sometimes
               | go crazy with `<style>` tags etc, just not inject
               | scripts, but I would expect it to almost always make
               | sense to define a specific allowed tag and attribute
               | list, as is usually done with the userland predecessors
               | to this API.
        
         | cogman10 wrote:
         | > Who would ever want this?
         | 
         | The main case I can think of is wanting some forum
         | functionality. Perhaps you want to allow your users to be able
         | to write in markdown. This would provide an extra layer of
         | protection as you could take the HTML generated from the
         | markdown and further lock it down to only an allowed set of
         | elements like `h1`. Just in case someone tried some of the
         | markdown escape hatches that you didn't expect.
        
           | Aachen wrote:
           | > This would provide an extra layer of protection
           | 
           | I think this might be the answer. There's no point to it by
           | itself (either you separate data and code or you don't and
           | let the user do anything to your page), but if you're already
           | using a sanitiser _and_ you can 't use `textContent` because
           | (such as with Markdown) there'll be HTML tags in the output,
           | then this could be extra hardening. Thanks!
        
           | iLoveOncall wrote:
           | You'd never want to store the processed HTML anyway, this is
           | website building 101.
        
             | efilife wrote:
             | I store both, to serve processed HTML faster, and to be
             | able to rebuild it just in case. Is this ok?
        
         | byproxy wrote:
         | > but this still allows arbitrary markup to the page (even
         | <style> CSS rules) if I'm reading the docs correctly.
         | 
         | If that's true, seems like it's still a security risk given
         | what you can do with CSS these days:
         | https://news.ycombinator.com/item?id=47132102
        
           | circuit10 wrote:
           | You can use selectors to gain some information about things
           | like input fields, e.g. https://www.invicti.com/blog/web-
           | security/private-data-stole...
           | 
           | Or I guess you could completely restyle and change the text
           | of UI elements so it looks like the user is doing one thing
           | when they're actually doing something completely different
           | like sending you money
        
             | qingcharles wrote:
             | Back in 2002 (?) I got banned from a certain auction site
             | because I managed to inject HTML into my username that made
             | it so once I had bid the "Bid" button disappeared for all
             | subsequent users.
        
         | itishappy wrote:
         | > If the default configuration of setHTML( ) is too strict (or
         | not strict enough) for a given use case, developers can provide
         | a custom configuration that defines which HTML elements and
         | attributes should be kept or removed.
        
           | Aachen wrote:
           | Injecting markup into someone else's website isn't what I'd
           | call too strict a default configuration
           | 
           | If you mean to convey that it's possible to configure it to
           | filter properly, let me introduce you to `textContent` which
           | is older than Firefox (I'm struggling to find a date it's so
           | old)
        
             | itishappy wrote:
             | That's the whole point of the setHTML.
             | 
             | How would I set a header level using textContent?
        
               | Aachen wrote:
               | The traditional way: separating data and code
               | document.createElement("h1").textContent = `Hello,
               | ${username}!`
               | 
               | If you allow <h1> in the setHTML configuration or use the
               | default, users with the tag in their username also always
               | get it rendered as markup
        
               | matsemann wrote:
               | Which is why you only use it where you want to allow some
               | kind of html..?
        
               | itishappy wrote:
               | It sounds like you're arguing against a specific usecase,
               | rather than the technology itself. If you don't want
               | arbitrary markup in usernames, setHTML would absolutely
               | be the wrong choice, but that's not really a good
               | argument against setHTML.
        
         | jerf wrote:
         | If I'm reading this right,
         | .setHTML("<h1>Hello</h1>", new Sanitizer({}))
         | 
         | will strip all elements out. That's not too difficult.
         | 
         | Plus this is defense-in-depth. Backends will still need to
         | sanitize usernames on some standard anyhow (there's not a lot
         | of systems out there that should take arbitrary Unicode input
         | as usernames), and backends SHOULD (in the RFC sense [1]) still
         | HTML-escape anything they output that they don't want to be raw
         | HTML.
         | 
         | [1]: https://www.rfc-editor.org/rfc/rfc2119
        
           | benmmurphy wrote:
           | i think the use case for setHTML is for user content that
           | contains rich text and to display that safely. so this is not
           | an alternative for escaping text or inserting text into the
           | DOM but rather a method for displaying rich text. for example
           | maybe you have an editor that produces em, and strong tags so
           | now you can just whitelist those tags and use setHTML to
           | safely put that rich text into the DOM without worrying about
           | all the possible HTML parsing edge cases.
        
           | evilpie wrote:
           | You aren't reading it right.                 new
           | Sanitizer({})
           | 
           | This Sanitizer will allow everything by default, but setHTML
           | will still block elements/attributes that can lead to XSS.
           | 
           | You might want something like:                 new
           | Sanitizer({ replaceWithChildrenElements: ["h1"], elements:
           | [], attributes: [] })
           | 
           | This will replace <h1> elements with their children (i.e.
           | text in this case), but disallow all other elements and
           | attributes.
        
         | piccirello wrote:
         | `setHTML` is meant as a replacement for `innerHTML`. In the use
         | case you describe, you would have never wanted `innerHTML`
         | anyway. You'd want `innerText` or `textContent`.
        
           | iLoveOncall wrote:
           | But that's what setHTML isn't at all a replacement for
           | innerHTML.
           | 
           | You still need innerHTML when you want to inject HTML tags in
           | the page, and you could already use innerText when you didn't
           | want to.
           | 
           | Having something in between is seriously useless.
        
             | Dylan16807 wrote:
             | > You still need innerHTML when you want to inject HTML
             | tags in the page
             | 
             | What makes you say this?
        
         | afavour wrote:
         | > Who would ever want this?
         | 
         | Anyone who wants to provide some level of flexibility but
         | within bounds. Say, you want to allow <strong> and <em> in a
         | forum post but not <script>. It's not too difficult to imagine
         | uses.
        
           | goatlover wrote:
           | Forums would already have code that sanitizes user input when
           | it's submitted. Users aren't directly setting html elements.
        
             | afavour wrote:
             | And is that sanitization perfect? Kept up to date?
             | 
             | With a safe API like this one that's tied to the browser's
             | own interpretation of HTML (i.e. it is perfectly placed to
             | know exactly what is and isn't dangerous given it is the
             | one rendering it) wouldn't it be much better to rely on
             | that?
        
         | kccqzy wrote:
         | There's innerText if you don't want markup. Or more verbosely,
         | document.createTextNode followed by whatever.appendChild.
        
         | dheera wrote:
         | > So you can still inject <h1> or <br><br><br>... etc into your
         | username
         | 
         | Are we taking out all the fun of the web? I absolutely loved
         | the <marquee> names people had in the early days of Facebook,
         | it was all harmless fun.
         | 
         | If injection of frontend code takes down your backend, your
         | backend sucks, fix it.
        
         | lelanthran wrote:
         | > Who would ever want this?
         | 
         | Your lack of imagination is disturbing :-)
         | 
         | https://github.com/lelanthran/ZjsComponent
        
       | cogman10 wrote:
       | Seems like this has a bunch of footguns. Particularly if you
       | interact with the Sanitizer api, and particularly if you use the
       | "remove" sanitizer api.
       | 
       | Don't get me wrong, better than nothing, but also really really
       | consider just using "setText" instead and never allow the user to
       | add any sort of HTML too the document.
        
         | evilpie wrote:
         | Using an allowlist based Sanitizer you are definitely less
         | likely to shoot yourself in the foot, but as long as you use
         | setHTML you can't introduce XSS at least.
        
         | GalaxyNova wrote:
         | It's worse than nothing, since inevitably people will use this
         | thinking it's 100% safe when it's not.
        
         | lelanthran wrote:
         | > never allow the user to add any sort of HTML too the
         | document.
         | 
         | What about when the author of the page wants to add large html
         | fragments to the page?
         | 
         | Are you saying that you cannot think of a single use for this,
         | considering how often innerHTML is being used?
        
       | dbvn wrote:
       | at what point can we consider the development of "set this
       | element's text/html" to be done?
        
         | Aachen wrote:
         | When browsers implement a variant that lets you separate data
         | and code perhaps. That's what I expected when reading the
         | headline: setHtml(code, data, data, ...), just like
         | parameterised SQL works: prepare("select rowid from %s where
         | time < %n", tablename, mynumber)
         | 
         | This new method they've cooked up would be called
         | eval(code,options) if html was anything other than a markup
         | language
        
           | itishappy wrote:
           | tablenames cannot be parameterized in SQL
           | 
           | https://stackoverflow.com/questions/78516750/parametrize-
           | tab...
        
       | shadowgovt wrote:
       | Oh, that's nice-to-have. Good work, Mozilla.
       | 
       | It would close the loop better if you could also use policy to
       | switch off innerHTML in a given page, but definitely a step in
       | the right direction for plain-JavaScript applications.
        
       | bryanrasmussen wrote:
       | is there any situation where innerHTML would be preferable? I
       | could suppose it might be more performant and so if you were
       | constructing something that was not open to XSS it might
       | theoretically be better (with the usual caveat that people always
       | make mistakes about this kind of thing)
        
       | kevincloudsec wrote:
       | naming the old behavior setHTMLUnsafe is what did it for me.
       | security features that require developers to opt in don't work.
       | making the unsafe path feel unsafe does.
        
       | shevy-java wrote:
       | Well, the name SetHTML, or let's say:
       | .set_html()
       | 
       | Makes objectively more sense than:
       | .inner_html()         .inner_html =         .set_inner_html()
       | 
       | It is a fairly small thing, but ... really. One day someone
       | should clean up the mess that is JavaScript. Guess it will never
       | happen, but JavaScript has so many strange things ...
       | 
       | I understand that this here is about protection against attacks
       | rather than a better API design, but really - APIs should ideally
       | be as great as possible the moment they are introduced and shown
       | to the public.
        
         | lloydatkinson wrote:
         | To be pedantic that's the DOM API, which is exposed to
         | JavaScript.
         | 
         | The DOM API has always felt like, and still does, it was
         | written by people that have never made an API.
        
           | pier25 wrote:
           | I don't think that's pedantic. Seems like a valid objection
           | to me.
           | 
           | So many issues in the client JS world originate from
           | insufficient or bad browser APIs.
        
             | lloydatkinson wrote:
             | I don't really think it's pedantic it's just that unless
             | you preface a lot of comments on HN these days, you'll get
             | a lot of whataboutism and straw man arguments.
        
       | giancarlostoro wrote:
       | My corporate firewall blocks it due to the "hacks" in the
       | subdomain / url. This is silly.
        
         | ok123456 wrote:
         | That's why the DNS for hackernews is news.ycombinator.com and
         | not hackernews.org
        
       | dogtimeimmortal wrote:
       | Title was a bit rage-baity. And I think you can already do
       | sanitation by writing a function to check input before passing it
       | to innerHTML?
       | 
       | This really just seems like another attempt at reinventing the
       | wheel. Somewhat related, I find it ironic how i cannot browse
       | hacks.mozilla.org in my old version of firefox("Browser not
       | supported"). Also, developer.mozilla.org loads mangled to various
       | degrees in current versions of palemoon, basilisk, and seamonkey
       | 
       | It's like there is some sort of "browser cartel" trying to screw
       | up The Web.
        
         | Retr0id wrote:
         | > you can already do sanitation by writing a function to check
         | input before passing it to innerHTML
         | 
         | This is like saying C is memory safe as long as your code
         | doesn't have any bugs.
         | 
         | More saliently, it does not consider parser differentials.
        
       | dvh wrote:
       | Kids in the '90s:                 SQL("select * from user where
       | name = " + name);
       | 
       | Kids in the '20s:                 div.innerHTML = "Hello " +
       | user.name;
        
         | Legend2440 wrote:
         | Kids in the '30s:                 "Summarize this email:  " +
         | email.contents
         | 
         | Prompt injection is just the same problem on a new technology.
         | We didn't learn anything from the 90s.
        
       | pier25 wrote:
       | Tangential but it's amazing in 2026 browsers still don't ship a
       | native DOM morph/merge API like morphdom or idiomorph.
        
       | pyrolistical wrote:
       | And for those who want a better innerHTML, use insertAdjacentHTML
       | https://developer.mozilla.org/en-US/docs/Web/API/Element/ins...
       | 
       | I don't ever use it with user input, but use it often when
       | building SPA without frameworks
        
       | austin-cheney wrote:
       | Another solution is just use this at the start of your code:
       | delete Element.prototype.innerHTML;
       | 
       | Then assignments to innerHTML do not modify the element's
       | textContent or child node list and assignments to it will not
       | throw an error.
        
       | jjcm wrote:
       | What I really want is a <sandbox> element that can safely run
       | dangerous code, not something that modifies dangerous code.
       | 
       | Iframes have significant restrictions as they can't flow with the
       | DOM. With AI and the increase in dynamic content, there's going
       | to be even more situations where you run untrusted code. I want
       | configurable encapsulation.
        
       ___________________________________________________________________
       (page generated 2026-02-24 23:01 UTC)