What's in a tag name? JavaScript, apparently
Finds that browsers allow surprising characters in HTML tag names, opening new XSS and sanitizer bypass vectors.
Extract
Published: Tuesday, 25 August 2026 at 14:24 UTC Updated: Tuesday, 25 August 2026 at 14:24 UTC I was on my laptop, as I often am when there's rubbish on telly, and found myself wondering what characters are allowed in a tag. I knew they had to begin with "a-zA-Z", but what about after that? I tried placing alert(1) in the tag name and remembered that the browser converts everything to uppercase. Then I wondered whether another property existed that didn't do that. I gave my tag an id attribute and inspected it in DevTools using console.dir(x). Carefully inspecting each property, I saw that "localName" contained a lowercase version of the tag name. This was perfect.After that, it was a simple case of putting the puzzle pieces together. I already knew that you could make any tag focusable using tabindex and that you can chain the onfocus event with itself. You can write a string to the event handler using attributes[0].value, which gets converted into a function and can then be called as a constructor using "new": <alert(1) onfocus="attributes[0].value=localName,new onfocus" autofocus tabindex=1> I'm sure you'll agree that it's pretty shocking, and it works in every browser. It's also a pretty nice way to bypass a WAF. Let's continue the journey. If localName returns a lowercase version of the tag, maybe that means you can use uppercase JavaScript, and yes, you can: <JAVASCRIPT:ALERT(1) onfocus=location=localName autofocus tabindex=1> Then I fuzzed every transformation of the tag name. This showed that alphabetic characters, forward slashes, whitespace, and newlines get transformed. Interestingly, line and paragraph separator characters don't. These are treated like newlines in JavaScript, so you can create bizarre-looking vectors: <null alert(1) onfocus="attributes.onfocus.value=localName,new onfocus" autofocus tabindex=1> If attributes[0].value gets blocked, there are some interesting alternatives: <ALERT(1) onfocus="attributes[0].textContent=localName,new onfocus" autofocus tabindex=1> <ALERT(1) onfocus="attributes[0].nodeValue=localName,new onfocus" autofocus tabindex=1> After that, I started messing around with the HTML. An opening angle bracket can actually be part of the tag name. You can then combine it with the first attribute to produce an XSS vector: <alert<img title=" src onerror=alert(1)> " onfocus=innerHTML=localName+attributes[0].value tabindex=1 autofocus> I messed around with other attributes, like "part", which actually converts …