diff options
Diffstat (limited to 'meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch')
-rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch | 230 |
1 files changed, 230 insertions, 0 deletions
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 | |||