summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSoumya Sambu <soumya.sambu@windriver.com>2023-09-14 16:33:48 +0000
committerSteve Sakoman <steve@sakoman.com>2023-09-23 05:26:15 -1000
commitde7443a25dbed19adf14ddbf7dbed430dd567903 (patch)
tree079215c3b609927b1de6078b1f70452806ece64c
parent5c556073ac6e54314aa3fc210db040f3ab55105a (diff)
downloadpoky-de7443a25dbed19adf14ddbf7dbed430dd567903.tar.gz
go: Fix CVE-2023-39319
The html/template package does not apply the proper rules for handling occurrences of "<script", "<!--", and "</script" within JS literals in <script> contexts. This may cause the template parser to improperly consider script contexts to be terminated early, causing actions to be improperly escaped. This could be leveraged to perform an XSS attack. References: https://nvd.nist.gov/vuln/detail/CVE-2023-39319 (From OE-Core rev: afdc322ecff4cfd8478c89a03f7fce748a132b48) Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-devtools/go/go-1.17.13.inc3
-rw-r--r--meta/recipes-devtools/go/go-1.20/CVE-2023-39319.patch254
2 files changed, 256 insertions, 1 deletions
diff --git a/meta/recipes-devtools/go/go-1.17.13.inc b/meta/recipes-devtools/go/go-1.17.13.inc
index 91dd886cd0..c753a26a7e 100644
--- a/meta/recipes-devtools/go/go-1.17.13.inc
+++ b/meta/recipes-devtools/go/go-1.17.13.inc
@@ -1,6 +1,6 @@
1require go-common.inc 1require go-common.inc
2 2
3FILESEXTRAPATHS:prepend := "${FILE_DIRNAME}/go-1.21:${FILE_DIRNAME}/go-1.19:${FILE_DIRNAME}/go-1.18:" 3FILESEXTRAPATHS:prepend := "${FILE_DIRNAME}/go-1.21:${FILE_DIRNAME}/go-1.20:${FILE_DIRNAME}/go-1.19:${FILE_DIRNAME}/go-1.18:"
4 4
5LIC_FILES_CHKSUM = "file://LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707" 5LIC_FILES_CHKSUM = "file://LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707"
6 6
@@ -43,6 +43,7 @@ SRC_URI += "\
43 file://CVE-2023-24531_1.patch \ 43 file://CVE-2023-24531_1.patch \
44 file://CVE-2023-24531_2.patch \ 44 file://CVE-2023-24531_2.patch \
45 file://CVE-2023-29409.patch \ 45 file://CVE-2023-29409.patch \
46 file://CVE-2023-39319.patch \
46" 47"
47SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd" 48SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd"
48 49
diff --git a/meta/recipes-devtools/go/go-1.20/CVE-2023-39319.patch b/meta/recipes-devtools/go/go-1.20/CVE-2023-39319.patch
new file mode 100644
index 0000000000..1554aa975c
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.20/CVE-2023-39319.patch
@@ -0,0 +1,254 @@
1From 2070531d2f53df88e312edace6c8dfc9686ab2f5 Mon Sep 17 00:00:00 2001
2From: Roland Shoemaker <bracewell@google.com>
3Date: Thu Aug 3 12:28:28 2023 -0700
4Subject: [PATCH] html/template: properly handle special tags within the script
5 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
38Reviewed-on: https://go-review.googlesource.com/c/go/+/526099
39TryBot-Result: Gopher Robot <gobot@golang.org>
40Run-TryBot: Cherry Mui <cherryyz@google.com>
41
42CVE: CVE-2023-39319
43
44Upstream-Status: Backport [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5]
45
46Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
47---
48 src/go/build/deps_test.go | 6 ++--
49 src/html/template/context.go | 14 ++++++++++
50 src/html/template/escape.go | 26 ++++++++++++++++++
51 src/html/template/escape_test.go | 47 +++++++++++++++++++++++++++++++-
52 src/html/template/transition.go | 15 ++++++++++
53 5 files changed, 104 insertions(+), 4 deletions(-)
54
55diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
56index dc3bb8c..359a00a 100644
57--- a/src/go/build/deps_test.go
58+++ b/src/go/build/deps_test.go
59@@ -255,15 +255,15 @@ var depsRules = `
60 < text/template
61 < internal/lazytemplate;
62
63- encoding/json, html, text/template
64- < html/template;
65-
66 # regexp
67 FMT
68 < regexp/syntax
69 < regexp
70 < internal/lazyregexp;
71
72+ encoding/json, html, text/template, regexp
73+ < html/template;
74+
75 # suffix array
76 encoding/binary, regexp
77 < index/suffixarray;
78diff --git a/src/html/template/context.go b/src/html/template/context.go
79index 0b65313..f5f44a1 100644
80--- a/src/html/template/context.go
81+++ b/src/html/template/context.go
82@@ -164,6 +164,20 @@ func isInTag(s state) bool {
83 return false
84 }
85
86+// isInScriptLiteral returns true if s is one of the literal states within a
87+// <script> tag, and as such occurances of "<!--", "<script", and "</script"
88+// need to be treated specially.
89+func isInScriptLiteral(s state) bool {
90+ // Ignore the comment states (stateJSBlockCmt, stateJSLineCmt,
91+ // stateJSHTMLOpenCmt, stateJSHTMLCloseCmt) because their content is already
92+ // omitted from the output.
93+ switch s {
94+ case stateJSDqStr, stateJSSqStr, stateJSBqStr, stateJSRegexp:
95+ return true
96+ }
97+ return false
98+}
99+
100 // delim is the delimiter that will end the current HTML attribute.
101 type delim uint8
102
103diff --git a/src/html/template/escape.go b/src/html/template/escape.go
104index bdccc65..1747ec9 100644
105--- a/src/html/template/escape.go
106+++ b/src/html/template/escape.go
107@@ -10,6 +10,7 @@ import (
108 "html"
109 "internal/godebug"
110 "io"
111+ "regexp"
112 "text/template"
113 "text/template/parse"
114 )
115@@ -652,6 +653,26 @@ var delimEnds = [...]string{
116 delimSpaceOrTagEnd: " \t\n\f\r>",
117 }
118
119+var (
120+ // Per WHATWG HTML specification, section 4.12.1.3, there are extremely
121+ // complicated rules for how to handle the set of opening tags <!--,
122+ // <script, and </script when they appear in JS literals (i.e. strings,
123+ // regexs, and comments). The specification suggests a simple solution,
124+ // rather than implementing the arcane ABNF, which involves simply escaping
125+ // the opening bracket with \x3C. We use the below regex for this, since it
126+ // makes doing the case-insensitive find-replace much simpler.
127+ specialScriptTagRE = regexp.MustCompile("(?i)<(script|/script|!--)")
128+ specialScriptTagReplacement = []byte("\\x3C$1")
129+)
130+
131+func containsSpecialScriptTag(s []byte) bool {
132+ return specialScriptTagRE.Match(s)
133+}
134+
135+func escapeSpecialScriptTags(s []byte) []byte {
136+ return specialScriptTagRE.ReplaceAll(s, specialScriptTagReplacement)
137+}
138+
139 var doctypeBytes = []byte("<!DOCTYPE")
140
141 // escapeText escapes a text template node.
142@@ -707,6 +728,11 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
143 b.Write(s[written:cs])
144 written = i1
145 }
146+ if isInScriptLiteral(c.state) && containsSpecialScriptTag(s[i:i1]) {
147+ b.Write(s[written:i])
148+ b.Write(escapeSpecialScriptTags(s[i:i1]))
149+ written = i1
150+ }
151 if i == i1 && c.state == c1.state {
152 panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
153 }
154diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
155index 4f48afe..7853daa 100644
156--- a/src/html/template/escape_test.go
157+++ b/src/html/template/escape_test.go
158@@ -503,6 +503,21 @@ func TestEscape(t *testing.T) {
159 "<script>var a/*b*///c\nd</script>",
160 "<script>var a \nd</script>",
161 },
162+ {
163+ "Special tags in <script> string literals",
164+ `<script>var a = "asd < 123 <!-- 456 < fgh <script jkl < 789 </script"</script>`,
165+ `<script>var a = "asd < 123 \x3C!-- 456 < fgh \x3Cscript jkl < 789 \x3C/script"</script>`,
166+ },
167+ {
168+ "Special tags in <script> string literals (mixed case)",
169+ `<script>var a = "<!-- <ScripT </ScripT"</script>`,
170+ `<script>var a = "\x3C!-- \x3CScripT \x3C/ScripT"</script>`,
171+ },
172+ {
173+ "Special tags in <script> regex literals (mixed case)",
174+ `<script>var a = /<!-- <ScripT </ScripT/</script>`,
175+ `<script>var a = /\x3C!-- \x3CScripT \x3C/ScripT/</script>`,
176+ },
177 {
178 "CSS comments",
179 "<style>p// paragraph\n" +
180@@ -1491,8 +1506,38 @@ func TestEscapeText(t *testing.T) {
181 context{state: stateJS, element: elementScript},
182 },
183 {
184+ // <script and </script tags are escaped, so </script> should not
185+ // cause us to exit the JS state.
186 `<script>document.write("<script>alert(1)</script>");`,
187- context{state: stateText},
188+ context{state: stateJS, element: elementScript},
189+ },
190+ {
191+ `<script>document.write("<script>`,
192+ context{state: stateJSDqStr, element: elementScript},
193+ },
194+ {
195+ `<script>document.write("<script>alert(1)</script>`,
196+ context{state: stateJSDqStr, element: elementScript},
197+ },
198+ {
199+ `<script>document.write("<script>alert(1)<!--`,
200+ context{state: stateJSDqStr, element: elementScript},
201+ },
202+ {
203+ `<script>document.write("<script>alert(1)</Script>");`,
204+ context{state: stateJS, element: elementScript},
205+ },
206+ {
207+ `<script>document.write("<!--");`,
208+ context{state: stateJS, element: elementScript},
209+ },
210+ {
211+ `<script>let a = /</script`,
212+ context{state: stateJSRegexp, element: elementScript},
213+ },
214+ {
215+ `<script>let a = /</script/`,
216+ context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp},
217 },
218 {
219 `<script type="text/template">`,
220diff --git a/src/html/template/transition.go b/src/html/template/transition.go
221index 92eb351..e2660cc 100644
222--- a/src/html/template/transition.go
223+++ b/src/html/template/transition.go
224@@ -212,6 +212,11 @@ var (
225 // element states.
226 func tSpecialTagEnd(c context, s []byte) (context, int) {
227 if c.element != elementNone {
228+ // script end tags ("</script") within script literals are ignored, so that
229+ // we can properly escape them.
230+ if c.element == elementScript && (isInScriptLiteral(c.state) || isComment(c.state)) {
231+ return c, len(s)
232+ }
233 if i := indexTagEnd(s, specialTagEndMarkers[c.element]); i != -1 {
234 return context{}, i
235 }
236@@ -331,6 +336,16 @@ func tJSDelimited(c context, s []byte) (context, int) {
237 inCharset = true
238 case ']':
239 inCharset = false
240+ case '/':
241+ // If "</script" appears in a regex literal, the '/' should not
242+ // close the regex literal, and it will later be escaped to
243+ // "\x3C/script" in escapeText.
244+ if i > 0 && i+7 <= len(s) && bytes.Compare(bytes.ToLower(s[i-1:i+7]), []byte("</script")) == 0 {
245+ i++
246+ } else if !inCharset {
247+ c.state, c.jsCtx = stateJS, jsCtxDivOp
248+ return c, i + 1
249+ }
250 default:
251 // end delimiter
252 if !inCharset {
253--
2542.40.0