summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSiddharth Doshi <sdoshi@mvista.com>2023-09-26 09:37:54 +0530
committerSteve Sakoman <steve@sakoman.com>2023-10-04 05:17:51 -1000
commitcbb7afa601bc8595b17cd8386c12f64f07f8220c (patch)
treed9787921c15b1115b0027ff1c207f08aa9efeecd
parentf27e86a4d70ca186bb1a22e8c5ea63805a8822b7 (diff)
downloadpoky-cbb7afa601bc8595b17cd8386c12f64f07f8220c.tar.gz
go: Fix CVE-2023-39318 and CVE-2023-39319
Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c] CVE: CVE-2023-39318 Upstream-Status: Backport from [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5] CVE: CVE-2023-39319 (From OE-Core rev: 8de380d765d8f47a961c6e45eba1cfa4d2feb68f) Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/go/go-1.14.inc2
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch238
-rw-r--r--meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch230
3 files changed, 470 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc
index 784b502f46..be63f64825 100644
--- a/meta/recipes-devtools/go/go-1.14.inc
+++ b/meta/recipes-devtools/go/go-1.14.inc
@@ -77,6 +77,8 @@ SRC_URI += "\
77 file://CVE-2023-24536_1.patch \ 77 file://CVE-2023-24536_1.patch \
78 file://CVE-2023-24536_2.patch \ 78 file://CVE-2023-24536_2.patch \
79 file://CVE-2023-24536_3.patch \ 79 file://CVE-2023-24536_3.patch \
80 file://CVE-2023-39318.patch \
81 file://CVE-2023-39319.patch \
80" 82"
81 83
82SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" 84SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch"
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch
new file mode 100644
index 0000000000..20e70c0485
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch
@@ -0,0 +1,238 @@
1From 023b542edf38e2a1f87fcefb9f75ff2f99401b4c Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <bracewell@google.com>
3Date: Thu, 3 Aug 2023 12:24:13 -0700
4Subject: [PATCH] [release-branch.go1.20] html/template: support HTML-like
5 comments in script contexts
6
7Per Appendix B.1.1 of the ECMAScript specification, support HTML-like
8comments in script contexts. Also per section 12.5, support hashbang
9comments. This brings our parsing in-line with how browsers treat these
10comment types.
11
12Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for
13reporting this issue.
14
15Fixes #62196
16Fixes #62395
17Fixes CVE-2023-39318
18
19Change-Id: Id512702c5de3ae46cf648e268cb10e1eb392a181
20Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976593
21Run-TryBot: Roland Shoemaker <bracewell@google.com>
22Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
23Reviewed-by: Damien Neil <dneil@google.com>
24Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
25Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014620
26Reviewed-on: https://go-review.googlesource.com/c/go/+/526098
27Run-TryBot: Cherry Mui <cherryyz@google.com>
28TryBot-Result: Gopher Robot <gobot@golang.org>
29
30Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c]
31CVE: CVE-2023-39318
32Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
33---
34 src/html/template/context.go | 6 ++-
35 src/html/template/escape.go | 5 +-
36 src/html/template/escape_test.go | 10 ++++
37 src/html/template/state_string.go | 4 +-
38 src/html/template/transition.go | 80 ++++++++++++++++++++-----------
39 5 files changed, 72 insertions(+), 33 deletions(-)
40
41diff --git a/src/html/template/context.go b/src/html/template/context.go
42index 0b65313..4eb7891 100644
43--- a/src/html/template/context.go
44+++ b/src/html/template/context.go
45@@ -124,6 +124,10 @@ const (
46 stateJSBlockCmt
47 // stateJSLineCmt occurs inside a JavaScript // line comment.
48 stateJSLineCmt
49+ // stateJSHTMLOpenCmt occurs inside a JavaScript <!-- HTML-like comment.
50+ stateJSHTMLOpenCmt
51+ // stateJSHTMLCloseCmt occurs inside a JavaScript --> HTML-like comment.
52+ stateJSHTMLCloseCmt
53 // stateCSS occurs inside a <style> element or style attribute.
54 stateCSS
55 // stateCSSDqStr occurs inside a CSS double quoted string.
56@@ -149,7 +153,7 @@ const (
57 // authors & maintainers, not for end-users or machines.
58 func isComment(s state) bool {
59 switch s {
60- case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
61+ case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt, stateCSSBlockCmt, stateCSSLineCmt:
62 return true
63 }
64 return false
65diff --git a/src/html/template/escape.go b/src/html/template/escape.go
66index 435f912..ad2ec69 100644
67--- a/src/html/template/escape.go
68+++ b/src/html/template/escape.go
69@@ -698,9 +698,12 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
70 if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone {
71 // Preserve the portion between written and the comment start.
72 cs := i1 - 2
73- if c1.state == stateHTMLCmt {
74+ if c1.state == stateHTMLCmt || c1.state == stateJSHTMLOpenCmt {
75 // "<!--" instead of "/*" or "//"
76 cs -= 2
77+ } else if c1.state == stateJSHTMLCloseCmt {
78+ // "-->" instead of "/*" or "//"
79+ cs -= 1
80 }
81 b.Write(s[written:cs])
82 written = i1
83diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
84index f550691..5f41e52 100644
85--- a/src/html/template/escape_test.go
86+++ b/src/html/template/escape_test.go
87@@ -503,6 +503,16 @@ func TestEscape(t *testing.T) {
88 "<script>var a/*b*///c\nd</script>",
89 "<script>var a \nd</script>",
90 },
91+ {
92+ "JS HTML-like comments",
93+ "<script>before <!-- beep\nbetween\nbefore-->boop\n</script>",
94+ "<script>before \nbetween\nbefore\n</script>",
95+ },
96+ {
97+ "JS hashbang comment",
98+ "<script>#! beep\n</script>",
99+ "<script>\n</script>",
100+ },
101 {
102 "CSS comments",
103 "<style>p// paragraph\n" +
104diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go
105index 05104be..b5cfe70 100644
106--- a/src/html/template/state_string.go
107+++ b/src/html/template/state_string.go
108@@ -4,9 +4,9 @@ package template
109
110 import "strconv"
111
112-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError"
113+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
114
115-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296}
116+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 214, 233, 241, 254, 267, 280, 293, 304, 320, 335, 345, 354}
117
118 func (i state) String() string {
119 if i >= state(len(_state_index)-1) {
120diff --git a/src/html/template/transition.go b/src/html/template/transition.go
121index 92eb351..12aa4c4 100644
122--- a/src/html/template/transition.go
123+++ b/src/html/template/transition.go
124@@ -14,32 +14,34 @@ import (
125 // the updated context and the number of bytes consumed from the front of the
126 // input.
127 var transitionFunc = [...]func(context, []byte) (context, int){
128- stateText: tText,
129- stateTag: tTag,
130- stateAttrName: tAttrName,
131- stateAfterName: tAfterName,
132- stateBeforeValue: tBeforeValue,
133- stateHTMLCmt: tHTMLCmt,
134- stateRCDATA: tSpecialTagEnd,
135- stateAttr: tAttr,
136- stateURL: tURL,
137- stateSrcset: tURL,
138- stateJS: tJS,
139- stateJSDqStr: tJSDelimited,
140- stateJSSqStr: tJSDelimited,
141- stateJSBqStr: tJSDelimited,
142- stateJSRegexp: tJSDelimited,
143- stateJSBlockCmt: tBlockCmt,
144- stateJSLineCmt: tLineCmt,
145- stateCSS: tCSS,
146- stateCSSDqStr: tCSSStr,
147- stateCSSSqStr: tCSSStr,
148- stateCSSDqURL: tCSSStr,
149- stateCSSSqURL: tCSSStr,
150- stateCSSURL: tCSSStr,
151- stateCSSBlockCmt: tBlockCmt,
152- stateCSSLineCmt: tLineCmt,
153- stateError: tError,
154+ stateText: tText,
155+ stateTag: tTag,
156+ stateAttrName: tAttrName,
157+ stateAfterName: tAfterName,
158+ stateBeforeValue: tBeforeValue,
159+ stateHTMLCmt: tHTMLCmt,
160+ stateRCDATA: tSpecialTagEnd,
161+ stateAttr: tAttr,
162+ stateURL: tURL,
163+ stateSrcset: tURL,
164+ stateJS: tJS,
165+ stateJSDqStr: tJSDelimited,
166+ stateJSSqStr: tJSDelimited,
167+ stateJSBqStr: tJSDelimited,
168+ stateJSRegexp: tJSDelimited,
169+ stateJSBlockCmt: tBlockCmt,
170+ stateJSLineCmt: tLineCmt,
171+ stateJSHTMLOpenCmt: tLineCmt,
172+ stateJSHTMLCloseCmt: tLineCmt,
173+ stateCSS: tCSS,
174+ stateCSSDqStr: tCSSStr,
175+ stateCSSSqStr: tCSSStr,
176+ stateCSSDqURL: tCSSStr,
177+ stateCSSSqURL: tCSSStr,
178+ stateCSSURL: tCSSStr,
179+ stateCSSBlockCmt: tBlockCmt,
180+ stateCSSLineCmt: tLineCmt,
181+ stateError: tError,
182 }
183
184 var commentStart = []byte("<!--")
185@@ -263,7 +265,7 @@ func tURL(c context, s []byte) (context, int) {
186
187 // tJS is the context transition function for the JS state.
188 func tJS(c context, s []byte) (context, int) {
189- i := bytes.IndexAny(s, "\"`'/")
190+ i := bytes.IndexAny(s, "\"`'/<-#")
191 if i == -1 {
192 // Entire input is non string, comment, regexp tokens.
193 c.jsCtx = nextJSCtx(s, c.jsCtx)
194@@ -293,6 +295,26 @@ func tJS(c context, s []byte) (context, int) {
195 err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", s[i:]),
196 }, len(s)
197 }
198+ // ECMAScript supports HTML style comments for legacy reasons, see Appendix
199+ // B.1.1 "HTML-like Comments". The handling of these comments is somewhat
200+ // confusing. Multi-line comments are not supported, i.e. anything on lines
201+ // between the opening and closing tokens is not considered a comment, but
202+ // anything following the opening or closing token, on the same line, is
203+ // ignored. As such we simply treat any line prefixed with "<!--" or "-->"
204+ // as if it were actually prefixed with "//" and move on.
205+ case '<':
206+ if i+3 < len(s) && bytes.Equal(commentStart, s[i:i+4]) {
207+ c.state, i = stateJSHTMLOpenCmt, i+3
208+ }
209+ case '-':
210+ if i+2 < len(s) && bytes.Equal(commentEnd, s[i:i+3]) {
211+ c.state, i = stateJSHTMLCloseCmt, i+2
212+ }
213+ // ECMAScript also supports "hashbang" comment lines, see Section 12.5.
214+ case '#':
215+ if i+1 < len(s) && s[i+1] == '!' {
216+ c.state, i = stateJSLineCmt, i+1
217+ }
218 default:
219 panic("unreachable")
220 }
221@@ -372,12 +394,12 @@ func tBlockCmt(c context, s []byte) (context, int) {
222 return c, i + 2
223 }
224
225-// tLineCmt is the context transition function for //comment states.
226+// tLineCmt is the context transition function for //comment states, and the JS HTML-like comment state.
227 func tLineCmt(c context, s []byte) (context, int) {
228 var lineTerminators string
229 var endState state
230 switch c.state {
231- case stateJSLineCmt:
232+ case stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt:
233 lineTerminators, endState = "\n\r\u2028\u2029", stateJS
234 case stateCSSLineCmt:
235 lineTerminators, endState = "\n\f\r", stateCSS
236--
2372.24.4
238
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch
new file mode 100644
index 0000000000..69106e3e05
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch
@@ -0,0 +1,230 @@
1From 2070531d2f53df88e312edace6c8dfc9686ab2f5 Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <bracewell@google.com>
3Date: Thu, 3 Aug 2023 12:28:28 -0700
4Subject: [PATCH] [release-branch.go1.20] html/template: properly handle
5 special tags within the script context
6
7The HTML specification has incredibly complex rules for how to handle
8"<!--", "<script", and "</script" when they appear within literals in
9the script context. Rather than attempting to apply these restrictions
10(which require a significantly more complex state machine) we apply
11the workaround suggested in section 4.12.1.3 of the HTML specification [1].
12
13More precisely, when "<!--", "<script", and "</script" appear within
14literals (strings and regular expressions, ignoring comments since we
15already elide their content) we replace the "<" with "\x3C". This avoids
16the unintuitive behavior that using these tags within literals can cause,
17by simply preventing the rendered content from triggering it. This may
18break some correct usages of these tags, but on balance is more likely
19to prevent XSS attacks where users are unknowingly either closing or not
20closing the script blocks where they think they are.
21
22Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for
23reporting this issue.
24
25Fixes #62197
26Fixes #62397
27Fixes CVE-2023-39319
28
29[1] https://html.spec.whatwg.org/#restrictions-for-contents-of-script-elements
30
31Change-Id: Iab57b0532694827e3eddf57a7497ba1fab1746dc
32Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976594
33Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
34Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
35Reviewed-by: Damien Neil <dneil@google.com>
36Run-TryBot: Roland Shoemaker <bracewell@google.com>
37Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014621
38TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
39Reviewed-on: https://go-review.googlesource.com/c/go/+/526099
40TryBot-Result: Gopher Robot <gobot@golang.org>
41Run-TryBot: Cherry Mui <cherryyz@google.com>
42
43Upstream-Status: Backport from [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5]
44CVE: CVE-2023-39319
45Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
46---
47 src/html/template/context.go | 14 ++++++++++
48 src/html/template/escape.go | 26 ++++++++++++++++++
49 src/html/template/escape_test.go | 47 +++++++++++++++++++++++++++++++-
50 src/html/template/transition.go | 15 ++++++++++
51 4 files changed, 101 insertions(+), 1 deletion(-)
52
53diff --git a/src/html/template/context.go b/src/html/template/context.go
54index 4eb7891..feb6517 100644
55--- a/src/html/template/context.go
56+++ b/src/html/template/context.go
57@@ -168,6 +168,20 @@ func isInTag(s state) bool {
58 return false
59 }
60
61+// isInScriptLiteral returns true if s is one of the literal states within a
62+// <script> tag, and as such occurances of "<!--", "<script", and "</script"
63+// need to be treated specially.
64+func isInScriptLiteral(s state) bool {
65+ // Ignore the comment states (stateJSBlockCmt, stateJSLineCmt,
66+ // stateJSHTMLOpenCmt, stateJSHTMLCloseCmt) because their content is already
67+ // omitted from the output.
68+ switch s {
69+ case stateJSDqStr, stateJSSqStr, stateJSBqStr, stateJSRegexp:
70+ return true
71+ }
72+ return false
73+}
74+
75 // delim is the delimiter that will end the current HTML attribute.
76 type delim uint8
77
78diff --git a/src/html/template/escape.go b/src/html/template/escape.go
79index ad2ec69..de8cf6f 100644
80--- a/src/html/template/escape.go
81+++ b/src/html/template/escape.go
82@@ -10,6 +10,7 @@ import (
83 "html"
84 "internal/godebug"
85 "io"
86+ "regexp"
87 "text/template"
88 "text/template/parse"
89 )
90@@ -650,6 +651,26 @@ var delimEnds = [...]string{
91 delimSpaceOrTagEnd: " \t\n\f\r>",
92 }
93
94+var (
95+ // Per WHATWG HTML specification, section 4.12.1.3, there are extremely
96+ // complicated rules for how to handle the set of opening tags <!--,
97+ // <script, and </script when they appear in JS literals (i.e. strings,
98+ // regexs, and comments). The specification suggests a simple solution,
99+ // rather than implementing the arcane ABNF, which involves simply escaping
100+ // the opening bracket with \x3C. We use the below regex for this, since it
101+ // makes doing the case-insensitive find-replace much simpler.
102+ specialScriptTagRE = regexp.MustCompile("(?i)<(script|/script|!--)")
103+ specialScriptTagReplacement = []byte("\\x3C$1")
104+)
105+
106+func containsSpecialScriptTag(s []byte) bool {
107+ return specialScriptTagRE.Match(s)
108+}
109+
110+func escapeSpecialScriptTags(s []byte) []byte {
111+ return specialScriptTagRE.ReplaceAll(s, specialScriptTagReplacement)
112+}
113+
114 var doctypeBytes = []byte("<!DOCTYPE")
115
116 // escapeText escapes a text template node.
117@@ -708,6 +729,11 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
118 b.Write(s[written:cs])
119 written = i1
120 }
121+ if isInScriptLiteral(c.state) && containsSpecialScriptTag(s[i:i1]) {
122+ b.Write(s[written:i])
123+ b.Write(escapeSpecialScriptTags(s[i:i1]))
124+ written = i1
125+ }
126 if i == i1 && c.state == c1.state {
127 panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
128 }
129diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
130index 5f41e52..0cacb20 100644
131--- a/src/html/template/escape_test.go
132+++ b/src/html/template/escape_test.go
133@@ -513,6 +513,21 @@ func TestEscape(t *testing.T) {
134 "<script>#! beep\n</script>",
135 "<script>\n</script>",
136 },
137+ {
138+ "Special tags in <script> string literals",
139+ `<script>var a = "asd < 123 <!-- 456 < fgh <script jkl < 789 </script"</script>`,
140+ `<script>var a = "asd < 123 \x3C!-- 456 < fgh \x3Cscript jkl < 789 \x3C/script"</script>`,
141+ },
142+ {
143+ "Special tags in <script> string literals (mixed case)",
144+ `<script>var a = "<!-- <ScripT </ScripT"</script>`,
145+ `<script>var a = "\x3C!-- \x3CScripT \x3C/ScripT"</script>`,
146+ },
147+ {
148+ "Special tags in <script> regex literals (mixed case)",
149+ `<script>var a = /<!-- <ScripT </ScripT/</script>`,
150+ `<script>var a = /\x3C!-- \x3CScripT \x3C/ScripT/</script>`,
151+ },
152 {
153 "CSS comments",
154 "<style>p// paragraph\n" +
155@@ -1501,8 +1516,38 @@ func TestEscapeText(t *testing.T) {
156 context{state: stateJS, element: elementScript},
157 },
158 {
159+ // <script and </script tags are escaped, so </script> should not
160+ // cause us to exit the JS state.
161 `<script>document.write("<script>alert(1)</script>");`,
162- context{state: stateText},
163+ context{state: stateJS, element: elementScript},
164+ },
165+ {
166+ `<script>document.write("<script>`,
167+ context{state: stateJSDqStr, element: elementScript},
168+ },
169+ {
170+ `<script>document.write("<script>alert(1)</script>`,
171+ context{state: stateJSDqStr, element: elementScript},
172+ },
173+ {
174+ `<script>document.write("<script>alert(1)<!--`,
175+ context{state: stateJSDqStr, element: elementScript},
176+ },
177+ {
178+ `<script>document.write("<script>alert(1)</Script>");`,
179+ context{state: stateJS, element: elementScript},
180+ },
181+ {
182+ `<script>document.write("<!--");`,
183+ context{state: stateJS, element: elementScript},
184+ },
185+ {
186+ `<script>let a = /</script`,
187+ context{state: stateJSRegexp, element: elementScript},
188+ },
189+ {
190+ `<script>let a = /</script/`,
191+ context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp},
192 },
193 {
194 `<script type="text/template">`,
195diff --git a/src/html/template/transition.go b/src/html/template/transition.go
196index 12aa4c4..3d2a37c 100644
197--- a/src/html/template/transition.go
198+++ b/src/html/template/transition.go
199@@ -214,6 +214,11 @@ var (
200 // element states.
201 func tSpecialTagEnd(c context, s []byte) (context, int) {
202 if c.element != elementNone {
203+ // script end tags ("</script") within script literals are ignored, so that
204+ // we can properly escape them.
205+ if c.element == elementScript && (isInScriptLiteral(c.state) || isComment(c.state)) {
206+ return c, len(s)
207+ }
208 if i := indexTagEnd(s, specialTagEndMarkers[c.element]); i != -1 {
209 return context{}, i
210 }
211@@ -353,6 +358,16 @@ func tJSDelimited(c context, s []byte) (context, int) {
212 inCharset = true
213 case ']':
214 inCharset = false
215+ case '/':
216+ // If "</script" appears in a regex literal, the '/' should not
217+ // close the regex literal, and it will later be escaped to
218+ // "\x3C/script" in escapeText.
219+ if i > 0 && i+7 <= len(s) && bytes.Compare(bytes.ToLower(s[i-1:i+7]), []byte("</script")) == 0 {
220+ i++
221+ } else if !inCharset {
222+ c.state, c.jsCtx = stateJS, jsCtxDivOp
223+ return c, i + 1
224+ }
225 default:
226 // end delimiter
227 if !inCharset {
228--
2292.24.4
230