Skip to content

html/template: potential XSS caused by wrong JavaScript regexp context tracking after top-level { #80435

Description

@thatnealpatel

Previously, pathological inputs could close an
unescaped / early, allowing for attack-controlled
data to inject arbitrary content, potentially
leading to XSS.

Thanks to Ali Sherif for reporting this issue.

This is CVE-2026-56858 and Go issue https://go.dev/issue/80435.


This was a PUBLIC track issue, tracked in http://b/503512010.

Original report:

In src/html/template/transition.go, tJS() handles a top-level { inside <script> content without updating jsCtx through the normal JavaScript context transition logic.

Because of that, the following / can be treated as if it were in division context instead of regexp context. This makes html/template choose jsValEscaper instead of jsRegexpEscaper for a template action that is actually inside a JavaScript regexp literal.

That distinction matters because jsValEscaper uses JSON-style escaping and does not escape / for regexp safety, while jsRegexpEscaper escapes / as \/.

As a result, attacker-controlled input containing / can terminate the intended regexp literal in the browser parser and inject arbitrary JavaScript.

vulnerable pattern looks like:

<script>if(true){/{{.}}/g.test("x")}</script>

Attacker-controlled input:

./;alert(document.domain);var q=/.

Rendered output:

<script>if(true){/"./;alert(document.domain);var q=/."/g.test("x")}</script>

I tested this manually in a browser and alert(document.domain) executed, confirming XSS.

Control case used during testing:

<script>var x = {{.}}</script>

Using the same dangerous input in the control case did not trigger XSS, because that context correctly uses normal JavaScript value escaping. This helps show the issue is not just “dangerous input in JavaScript”, but a specific context-tracking bug that causes the wrong escaper to be selected after the top-level {.

PoC:

package main

import (
"bytes"
"fmt"
"html/template"
)

func main() {
tmpl := `<script>if(true){/{{.}}/g.test("x")}</script>`
input := `./;alert(document.domain);var q=/.`

t := template.Must(template.New("x").Parse(tmpl))
var buf bytes.Buffer
_ = t.Execute(&buf, input)

fmt.Println(buf.String())
}

Attack scenario

A fully external, unauthenticated attacker can exploit this if they control data passed to html/template.Execute() and the application renders that data in an affected regexp literal sink inside a <script> block after a top-level {.

The attacker gains JavaScript execution in the victim’s browser in the origin of the vulnerable application.

Depending on the target application, this can lead to session theft, account actions as the victim, sensitive data exfiltration, or other same-origin XSS impact.


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixPendingIssues that have a fix which has not yet been reviewed or submitted.Securityrelease-blocker

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions