Cross-Site Scripting via template.JS in Go

High Risk Cross-Site Scripting (XSS)
goxssjavascripttemplatehtml-template

What it is

XSS vulnerabilities occur when template.JS is used with dynamic or user-controlled content in Go templates. template.JS marks strings as safe JavaScript and disables automatic escaping, allowing attackers to inject malicious scripts that execute in users' browsers, steal sessions, manipulate DOM content, and perform unauthorized actions.

package main

import (
    "fmt"
    "html/template"
    "net/http"
)

func dashboard(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    
    // VULNERABLE: template.JS disables escaping for JavaScript
    jsCode := template.JS(fmt.Sprintf("var user = '%s';", username))
    
    tmpl := `<html><head><script>
        {{.JSCode}}
        alert('Welcome ' + user);
    </script></head></html>`
    
    t := template.Must(template.New("dash").Parse(tmpl))
    t.Execute(w, struct{ JSCode template.JS }{jsCode})
}

// Attack: username = "'; alert('XSS'); var dummy='"
// Result: var user = ''; alert('XSS'); var dummy='';
// This executes the attacker's JavaScript
package main

import (
    "encoding/json"
    "html/template"
    "net/http"
)

func dashboard(w http.ResponseWriter, r *http.Request) {
    username := r.URL.Query().Get("username")
    
    // SECURE: marshal data as JSON
    userData, _ := json.Marshal(map[string]string{
        "username": username,
    })
    
    tmpl := `<html><head><script>
        // Parse JSON data safely
        var userData = JSON.parse({{.UserData}});
        alert('Welcome ' + userData.username);
    </script></head></html>`
    
    t := template.Must(template.New("dash").Parse(tmpl))
    data := struct{ UserData string }{string(userData)}
    t.Execute(w, data)
}

💡 Why This Fix Works

The vulnerable code uses template.JS with fmt.Sprintf(), bypassing JavaScript escaping and allowing attackers to inject malicious scripts. The secure version marshals data as JSON and lets the template engine automatically escape it, then parses it safely in JavaScript.

Why it happens

Passing user-controlled data to template.JS which bypasses JavaScript escaping.

Root causes

Using template.JS with User Input

Passing user-controlled data to template.JS which bypasses JavaScript escaping.

String Formatting Before template.JS

Using fmt.Sprintf() to format strings before passing to template.JS.

Dynamic JavaScript Generation

Building JavaScript code dynamically with user input instead of using JSON data.

Fixes

1

Use JSON for Data Passing

Marshal data as JSON and parse it in JavaScript instead of using template.JS.

2

Use Data Attributes

Pass configuration via HTML data attributes and read with external JavaScript.

3

Only Use template.JS with Constants

If template.JS is necessary, only use with hardcoded, trusted constants.

Detect This Vulnerability in Your Code

Sourcery automatically identifies cross-site scripting via template.js in go and many other security issues in your codebase.