Skip to content

escape <!-- and --> for inline-script safety - #4511

Open
MsfPablo wants to merge 1 commit into
evanw:mainfrom
MsfPablo:escape-html-comment-inline-script
Open

MsfPablo wants to merge 1 commit into
evanw:mainfrom
MsfPablo:escape-html-comment-inline-script

Conversation

@MsfPablo

@MsfPablo MsfPablo commented Aug 8, 2026

Copy link
Copy Markdown

Fixes #4494.

When the inline-script feature is enabled (the default), esbuild escapes </script in string/template literals so output can be embedded in an inline <script> tag. The HTML parser's script data state machine also treats <!-- and --> specially:

So a string like "<!-- <script>" defeats the existing </script escaping. This PR escapes <!-- as \x3c!-- and --> as --\x3e in string/template content, mirroring Terser's inline_script option, which the issue references:

console.log("<!--", "<script>", "-->");
// Becomes:
console.log("\x3c!--","<script>","--\x3e");

<script> (without a slash) is left alone, matching Terser: it is only dangerous after a <!--, which is now escaped, and in the normal script-data state <script does nothing special.

Tagged template literals such as String.raw\...`emit raw content verbatim, so the printer cannot escape them in place. The existing</scriptlowering (which turns such templates into__templatecalls) is extended to also cover, letting the same escapes apply to the raw content while preserving the runtime value (the \x3c/\x3eescapes are source-level only; the string value stays, same as the existing </script` handling).

Both the printer escaping and the tagged-template lowering are gated on compat.InlineScript being supported, consistent with the existing </script behavior.

// in
console.log("<!-- <script>", "-->");
// out
console.log("\x3c!-- <script>", "--\x3e");
// in
x = String.raw`<!--`;
// out
import { __template } from "<runtime>";
var _a;
x = String.raw(_a || (_a = __template(["\x3c!--"])));

go build ./... and go test ./... pass; gofmt and go vet clean on the touched files.

When the inline-script feature is enabled, esbuild escapes </script in
string/template literals so the output can be embedded in an inline
<script> tag. The HTML parser's script data state machine also treats
<!-- and --> specially: <!-- enters the "script data escaped" state,
where a later <script sequence enters the "script data double escaped"
state and prevents </script> from ending the element. Escape <!-- and
--> as \x3c/\x3e in string content to keep it inert, mirroring Terser's
inline_script option.

Tagged template literals (e.g. String.raw`...`) emit raw content
verbatim, so the printer cannot escape them in place; lower such
templates into __template calls first, extending the existing
</script lowering to also cover <!-- and -->.

Fixes evanw#4494
@MsfPablo

Copy link
Copy Markdown
Author

Following up here. I realise escaping behaviour is a deliberate design area in esbuild, so if this isn't a change you want I'm happy to close it — mainly looking for a yes/no.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

inline-script should escape HTML comment brackets in string/regex/template literals

1 participant