The Go standard library's html/template package quietly carried a dangerous blind spot for months: it did not treat JavaScript backticks (ES6 template literals) as string delimiters when deciding how to escape injected content, allowing template actions to break out of a quoted JavaScript context and inject arbitrary script. That weakness — tracked as CVE-2023-24538 — forced a blunt but correct response from the Go maintainers: stop allowing Go template actions inside JavaScript template literals. Parsing such templates now fails, and operators and developers must update, audit, or change how they embed server-side values into client-side scripts. (go.dev)
JavaScript gained template literals in ES6: backticks let authors write multi-line strings and embed expressions. They are useful, but they complicate safe server-side templating because template literals change the rules for which characters are string delimiters and which need escaping.
Go’s html/template package is context-aware: it inspects where values are being placed (HTML body, attribute, CSS, JavaScript) and applies contextual escaping rules designed to prevent cross-site scripting (XSS). That contextual escaping depends on correctly identifying the surrounding language constructs. When the html/template parser did not recognize backticks as JavaScript delimiters, the contextual escaping engine could output content that prematurely closed a template literal and appended attacker-controlled JavaScript. The result is a template-capable XSS vector with minimal attacker effort.
The issue was discovered and responsibly disclosed in early 2023. It was tracked as a Go issue and assigned CVE-2023-24538. Because ES6 template literals introduce parsing complexity (nested interpolation, tagged templates, embedded expressions), the Go team chose to forbid Go template actions inside JavaScript template literals rather than attempt a brittle escape strategy. The parser now rejects templates that try to interpolate Go template actions inside backtick-delimited JavaScript literals. (go.dev)
Notably, different vulnerability databases applied different base scores and severities (some high critical scores reported). Operationally, treat any unpatched server-side code that emits user content directly inside a client-side template literal as high priority to remediate.
Linux distributors and cloud vendors issued advisories and backports reflected in their packaging trees: several vendor advisories (Amazon Linux, Red Hat, Ubuntu, and others) flagged packages and shipped patched builds where the vuln impacted vendored Go code or build artifacts. If you deploy software packaged by a vendor, consult that vendor’s advisory for the precise package-level fixes applied.
It also has a positive side-effect for maintainers: by making the parser fail-fast for unsafe templates, teams get early detection in CI rather than discovering XSS in production logs or incident reports.
The long-term lesson for web developers is to prefer explicit and typed methods of moving server-side data into client-side code: serialize complex data to JSON on the server, pass it through a safe channel, and keep template actions out of complex, language-specific quoting constructs. When a language feature is powerful — and template systems try to be convenient — the risk of subtle escape failures increases. A conservative parser that rejects ambiguous constructs and forces safer patterns is the correct defensive choice. (go.dev)
If you maintain Go web applications: treat this as a high-priority code-health and security item. Parse your templates with the patched parser, fix any rejected templates, and bake the detection into your build pipeline so this class of bugs never makes it to production again.
Source: MSRC Security Update Guide - Microsoft Security Response Center
Background / Overview
JavaScript gained template literals in ES6: backticks let authors write multi-line strings and embed expressions. They are useful, but they complicate safe server-side templating because template literals change the rules for which characters are string delimiters and which need escaping.Go’s html/template package is context-aware: it inspects where values are being placed (HTML body, attribute, CSS, JavaScript) and applies contextual escaping rules designed to prevent cross-site scripting (XSS). That contextual escaping depends on correctly identifying the surrounding language constructs. When the html/template parser did not recognize backticks as JavaScript delimiters, the contextual escaping engine could output content that prematurely closed a template literal and appended attacker-controlled JavaScript. The result is a template-capable XSS vector with minimal attacker effort.
The issue was discovered and responsibly disclosed in early 2023. It was tracked as a Go issue and assigned CVE-2023-24538. Because ES6 template literals introduce parsing complexity (nested interpolation, tagged templates, embedded expressions), the Go team chose to forbid Go template actions inside JavaScript template literals rather than attempt a brittle escape strategy. The parser now rejects templates that try to interpolate Go template actions inside backtick-delimited JavaScript literals. (go.dev)
What the bug actually is — a short technical explanation
- The vulnerable pattern: a JavaScript template literal in a Go template that contains a Go template action, for example:
- let s =
Hello, {{.Name}}; - The original html/template escaping logic assumed regular JavaScript single or double quoted strings, or other JavaScript contexts, and did not treat backticks as string delimiters for the purpose of contextual escaping.
- Because backticks were not recognized, the template engine could fail to escape a value that contained a backtick or other sequence that closed the JavaScript literal and injected new JavaScript tokens. A malicious input could therefore close the literal and inject attacker-controlled code into the page.
- The fix: the Go project updated the html/template parser so that Template.Parse rejects templates that contain Go template actions inside JavaScript backtick literals. In practice this means Template.Parse now returns an error for constructs like the example above. There is an internal ErrorCode (value 12) associated with that error, and the code path was backported to supported releases. (go.dev)
How bad is the risk? Impact and exploitability
This is not a theoretical parsing quirk — it’s an XSS vector that maps directly to the most common client-side exploitation path: uncontrolled script execution in victim browsers. The practical attack is straightforward:- An application embeds user-controlled data inside a JavaScript template literal using a Go template action.
- An attacker supplies content that includes a backtick (or other crafted sequence) that terminates the literal, then injects additional script and neutralizes the rest of the original code (for example with a comment).
- The browser executes the attacker's script in the context of the page, leading to session theft, credential exfiltration, or other client-side compromises.
Notably, different vulnerability databases applied different base scores and severities (some high critical scores reported). Operationally, treat any unpatched server-side code that emits user content directly inside a client-side template literal as high priority to remediate.
Affected versions and distribution guidance
The Go vulnerability tracker and the official vulnerability listing map the exposure to specific Go standard library releases. The canonical vulnerability record identifies the affected html/template versions as:- Versions before go1.19.8, and
- Versions from go1.20.0-0 up to but not including go1.20.3
Linux distributors and cloud vendors issued advisories and backports reflected in their packaging trees: several vendor advisories (Amazon Linux, Red Hat, Ubuntu, and others) flagged packages and shipped patched builds where the vuln impacted vendored Go code or build artifacts. If you deploy software packaged by a vendor, consult that vendor’s advisory for the precise package-level fixes applied.
What the Go project changed (design and policy consequences)
Instead of attempting to write a perfect escape routine for JavaScript template literals (which would have had to deal with nested interpolation, tagged templates, and the expressive features of ES6), the Go maintainers took a conservative approach:- The parser now rejects templates with Go template actions inside JavaScript template literals. Template.Parse will return an error for such templates. This is a correct-by-construction decision: it prevents a whole class of subtle bypasses by refusing the pattern entirely. (go.dev)
- A runtime override exists for users who need legacy behavior: a GODEBUG flag (jstmpllitinterp=1) can re-enable the older interpretation. Using that flag causes backticks to be escaped, but it must be used cautiously; the Go team explicitly warned that re-enabling previous behavior can be hazardous. Relying on this flag is a temporary stopgap and not recommended for general use. (go.dev)
- The maintainers also tracked the issue with an internal ErrorCode and planned to make that error code exportable in later releases, to make programmatic detection easier. This indicates an intent to make tooling and CI checks more practical for downstream projects. (go.dev)
Practical detection and triage steps for teams
If you maintain Go web services or libraries that use html/template (directly or indirectly), run this checklist immediately.- Inventory. Identify all code that uses html/template or text/template where the output is sent to browsers. Include compiled binaries and vendorized Go libraries. Use dependency scanning to find any uses of the html/template package.
- Search the codebase for backtick-delimited JavaScript template literals that interpolate Go template actions. Examples to search for:
let x =...{{.or{{.in template source.- If such templates exist, prioritize them: these are direct matches for the vulnerable pattern.
- Test parsing: run Template.Parse with the updated Go toolchain (the patched releases) against your templates in CI. Templates that use the disallowed pattern will fail to parse, giving you an immediate detection mechanism. (go.dev)
- Audit vendor packages and third-party templates. Templates can be embedded in libraries, static assets, or distribution packages. Vendor or package maintainers may have already shipped fixes; verify their version and advisory notes.
- If you cannot immediately update Go or the affected package, consider a temporary build-time or test-time check that rejects code patterns matching the vulnerable interpolation.
Remediation strategies (developer-level fixes)
There are a small set of pragmatic, secure options for code that currently uses the problematic pattern.- Update the Go toolchain and runtime to a patched release. That is the first and clearest step: move to a Go release line that includes the fix (as recorded in the official vulnerability entries). Running Template.Parse with a patched parser will flag occurrences.
- Replace inline interpolation inside backtick literals:
- Prefer JSON serialization for complex data you want available to client-side code.
- Use server-side JSON marshalling, then embed the JSON value safely into the page using an appropriate typed wrapper (for example, generate a JSON string and write it into a double-quoted JS string or assign it via a DOM attribute). The approach reduces the need to put Go template actions inside backticks and preserves contextual escaping. The Go standard library’s json package and html/template’s contextual escaping combine well for this use case.
- Use typed safe wrappers only when values are trusted:
- Go exposes types like template.JS and template.HTML that bypass escaping; these should be used only for trusted content. Avoid wrapping untrusted input in these types. If you must, clearly document and audit the code path that produces the content.
- Prefer safe templating libraries for heavy client-side embedding:
- The google/safehtml family and alternatives are designed to avoid context-sensitive mistakes by producing types that are safe-by-construction. When you need to include raw JavaScript fragments, prefer an approach that separates untrusted data and trusted code.
- CI and pre-merge checks:
- Add a parse-and-fail step in CI that uses a patched Go toolchain to parse templates. Templates that would have been allowed previously but are now rejected will fail this step and force remediation before merges reach production. (go.dev)
Operational mitigations when immediate patching is impossible
Not every environment can upgrade the toolchain overnight. If you cannot immediately update every affected binary or container image, use layered mitigations:- Harden CSP: deploy a restrictive Content Security Policy that reduces the impact of injected scripts. CSP is not a substitute for fixing template code, but it lowers the blast radius for stolen cookies and inline script execution.
- Strict input validation: reduce the risk surface by validating and normalizing user-controlled text before it enters templates. Reject or canonicalize unexpected characters like backticks where they are not required.
- Isolate readers from high-value pages: for pages that must display untrusted content, consider sandboxed iframes or rendering content on the client via API calls from trusted endpoints.
- Runtime feature flags: if your application conditionally parses templates at runtime, disable or restrict any user-supplied template features temporarily.
Detection & monitoring: what to look for in logs and telemetry
- Template.Parse errors: after you switch to a patched toolchain in staging or CI, pay attention to Template.Parse failures. They indicate previously-accepted templates that the fixed parser now disallows.
- Unexpected CSP violations: if you deploy restrictive CSP and see violations originating from certain pages, investigate source templates for suspicious interpolation patterns.
- Web application WAF rules: consider adding signature or pattern-based WAF rules to detect requests that include backticks, sequence that close JS literals, or other suspicious payloads targeted at template interpolation. But be mindful of false positives.
- Runtime anomalies in client telemetry: unexpected JavaScript errors or network calls originating from unknown script contexts commonly point to client-side injection.
Why the Go team’s choice is sound — and what it means for template design
The Go maintainers’ decision — to reject the ambiguous construct — reflects a conservative security posture: when a language construct is complex enough that reliable contextual escaping is brittle, forbid misuse. This policy aligns with other security-first template ecosystems that avoid contextual guessing and prefer explicit, safe construction of language fragments. The restriction forces developers to choose safer patterns (JSON serialization, typed wrappers, separate script generation) rather than relying on escape heuristics that are easy to gtps://go.dev/issue/59234))It also has a positive side-effect for maintainers: by making the parser fail-fast for unsafe templates, teams get early detection in CI rather than discovering XSS in production logs or incident reports.
Wider context and precedents
This CVE is one of several that demonstrate how subtle language features (modern JS syntax, nested templating, front-end frameworks) interact dangerously with server-side templating. Other Go template vulnerabilities and fixes have followed similar patterns: either tighten the contextual analysis or change the API to make unsafe usage impossible. Organizations that mix server-side templating with front-end frameworks should be especially cautious — these environments often hide mismatches in parsing expectations, turning double-escaping or under-escaping into a latent risk. Our internal archive of related forum items and historical coverage shows multiple instances where template parsing assumptions led to production escapes; treat such patterns as recurring operational debt.Action checklist — what teams should do in the next 72 hours
- Inventory all services that embed Go-generated HTML to browsers. Prioritize internet-facing services.
- Update to a patched Go release or vendor package that includes the fix for CVE-2023-24538. Ensure the update is applied to build pipelines and container images.
- Run Template.Parse with the patched toolchain in CI to detect templates that will fail under the new rules. Fix failing templates by removing Go actions from backtick literals or by using a JSON-serialization approach. (go.dev)
- Apply short-term runtime mitigations (CSP, input validation) only if you cannot immediately patch.
- Add a permanent CI check: parse templates with the latest stable Go to detect regressions early.
- Educate the team: document the rule against placing Go template actions inside JavaScript backtick literals and add a code review checklist item to catch regressions.
Final assessment and takeaway
CVE-2023-24538 is an instructive case: modern JavaScript syntax created an invisibly dangerous assumption inside a longstanding server-side templating system. Left unmitigated, the vulnerability makes XSS trivial where developers previously thought escaping would be automatic. The good news is that the fix is straightforward and straightforward to detect once you run templates through the patched parser: either update your toolchain, or rework templates to avoid embedding Go actions inside backtick-delimited JavaScript.The long-term lesson for web developers is to prefer explicit and typed methods of moving server-side data into client-side code: serialize complex data to JSON on the server, pass it through a safe channel, and keep template actions out of complex, language-specific quoting constructs. When a language feature is powerful — and template systems try to be convenient — the risk of subtle escape failures increases. A conservative parser that rejects ambiguous constructs and forces safer patterns is the correct defensive choice. (go.dev)
If you maintain Go web applications: treat this as a high-priority code-health and security item. Parse your templates with the patched parser, fix any rejected templates, and bake the detection into your build pipeline so this class of bugs never makes it to production again.
Source: MSRC Security Update Guide - Microsoft Security Response Center