diff options
author | Siddharth Doshi <sdoshi@mvista.com> | 2023-09-26 09:37:54 +0530 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2023-10-04 05:17:51 -1000 |
commit | cbb7afa601bc8595b17cd8386c12f64f07f8220c (patch) | |
tree | d9787921c15b1115b0027ff1c207f08aa9efeecd | |
parent | f27e86a4d70ca186bb1a22e8c5ea63805a8822b7 (diff) | |
download | poky-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.inc | 2 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch | 238 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch | 230 |
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 | ||
82 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" | 84 | SRC_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 @@ | |||
1 | From 023b542edf38e2a1f87fcefb9f75ff2f99401b4c Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 3 Aug 2023 12:24:13 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] html/template: support HTML-like | ||
5 | comments in script contexts | ||
6 | |||
7 | Per Appendix B.1.1 of the ECMAScript specification, support HTML-like | ||
8 | comments in script contexts. Also per section 12.5, support hashbang | ||
9 | comments. This brings our parsing in-line with how browsers treat these | ||
10 | comment types. | ||
11 | |||
12 | Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for | ||
13 | reporting this issue. | ||
14 | |||
15 | Fixes #62196 | ||
16 | Fixes #62395 | ||
17 | Fixes CVE-2023-39318 | ||
18 | |||
19 | Change-Id: Id512702c5de3ae46cf648e268cb10e1eb392a181 | ||
20 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976593 | ||
21 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
22 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
23 | Reviewed-by: Damien Neil <dneil@google.com> | ||
24 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
25 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014620 | ||
26 | Reviewed-on: https://go-review.googlesource.com/c/go/+/526098 | ||
27 | Run-TryBot: Cherry Mui <cherryyz@google.com> | ||
28 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
29 | |||
30 | Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c] | ||
31 | CVE: CVE-2023-39318 | ||
32 | Signed-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 | |||
41 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
42 | index 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 | ||
65 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
66 | index 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 | ||
83 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
84 | index 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" + | ||
104 | diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go | ||
105 | index 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) { | ||
120 | diff --git a/src/html/template/transition.go b/src/html/template/transition.go | ||
121 | index 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 | -- | ||
237 | 2.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 @@ | |||
1 | From 2070531d2f53df88e312edace6c8dfc9686ab2f5 Mon Sep 17 00:00:00 2001 | ||
2 | From: Roland Shoemaker <bracewell@google.com> | ||
3 | Date: Thu, 3 Aug 2023 12:28:28 -0700 | ||
4 | Subject: [PATCH] [release-branch.go1.20] html/template: properly handle | ||
5 | special tags within the script context | ||
6 | |||
7 | The HTML specification has incredibly complex rules for how to handle | ||
8 | "<!--", "<script", and "</script" when they appear within literals in | ||
9 | the script context. Rather than attempting to apply these restrictions | ||
10 | (which require a significantly more complex state machine) we apply | ||
11 | the workaround suggested in section 4.12.1.3 of the HTML specification [1]. | ||
12 | |||
13 | More precisely, when "<!--", "<script", and "</script" appear within | ||
14 | literals (strings and regular expressions, ignoring comments since we | ||
15 | already elide their content) we replace the "<" with "\x3C". This avoids | ||
16 | the unintuitive behavior that using these tags within literals can cause, | ||
17 | by simply preventing the rendered content from triggering it. This may | ||
18 | break some correct usages of these tags, but on balance is more likely | ||
19 | to prevent XSS attacks where users are unknowingly either closing or not | ||
20 | closing the script blocks where they think they are. | ||
21 | |||
22 | Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for | ||
23 | reporting this issue. | ||
24 | |||
25 | Fixes #62197 | ||
26 | Fixes #62397 | ||
27 | Fixes CVE-2023-39319 | ||
28 | |||
29 | [1] https://html.spec.whatwg.org/#restrictions-for-contents-of-script-elements | ||
30 | |||
31 | Change-Id: Iab57b0532694827e3eddf57a7497ba1fab1746dc | ||
32 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976594 | ||
33 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
34 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
35 | Reviewed-by: Damien Neil <dneil@google.com> | ||
36 | Run-TryBot: Roland Shoemaker <bracewell@google.com> | ||
37 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014621 | ||
38 | TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com> | ||
39 | Reviewed-on: https://go-review.googlesource.com/c/go/+/526099 | ||
40 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
41 | Run-TryBot: Cherry Mui <cherryyz@google.com> | ||
42 | |||
43 | Upstream-Status: Backport from [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5] | ||
44 | CVE: CVE-2023-39319 | ||
45 | Signed-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 | |||
53 | diff --git a/src/html/template/context.go b/src/html/template/context.go | ||
54 | index 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 | |||
78 | diff --git a/src/html/template/escape.go b/src/html/template/escape.go | ||
79 | index 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 | } | ||
129 | diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go | ||
130 | index 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">`, | ||
195 | diff --git a/src/html/template/transition.go b/src/html/template/transition.go | ||
196 | index 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 | -- | ||
229 | 2.24.4 | ||
230 | |||