summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/go/go-1.14/CVE-2023-24538_6.patch
blob: baf400b891a8a8723b4412cb5072c0570bffdd1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
From 16f4882984569f179d73967c9eee679bb9b098c5 Mon Sep 17 00:00:00 2001
From: Roland Shoemaker <bracewell@google.com>
Date: Mon, 20 Mar 2023 11:01:13 -0700
Subject: [PATCH 6/6] html/template: disallow actions in JS template literals

ECMAScript 6 introduced template literals[0][1] which are delimited with
backticks. These need to be escaped in a similar fashion to the
delimiters for other string literals. Additionally template literals can
contain special syntax for string interpolation.

There is no clear way to allow safe insertion of actions within JS
template literals, as handling (JS) string interpolation inside of these
literals is rather complex. As such we've chosen to simply disallow
template actions within these template literals.

A new error code is added for this parsing failure case, errJsTmplLit,
but it is unexported as it is not backwards compatible with other minor
release versions to introduce an API change in a minor release. We will
export this code in the next major release.

The previous behavior (with the cavet that backticks are now escaped
properly) can be re-enabled with GODEBUG=jstmpllitinterp=1.

This change subsumes CL471455.

Thanks to Sohom Datta, Manipal Institute of Technology, for reporting
this issue.

Fixes CVE-2023-24538
For #59234
Fixes #59271

[0] https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-template-literals
[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802457
Reviewed-by: Damien Neil <dneil@google.com>
Run-TryBot: Damien Neil <dneil@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
Reviewed-by: Roland Shoemaker <bracewell@google.com>
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612
Run-TryBot: Roland Shoemaker <bracewell@google.com>
Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c
Reviewed-on: https://go-review.googlesource.com/c/go/+/481987
Auto-Submit: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>

Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b
CVE: CVE-2023-24538
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
---
 src/html/template/context.go      |  2 ++
 src/html/template/error.go        | 13 ++++++++
 src/html/template/escape.go       | 11 +++++++
 src/html/template/escape_test.go  | 66 ++++++++++++++++++++++-----------------
 src/html/template/js.go           |  2 ++
 src/html/template/js_test.go      |  2 +-
 src/html/template/jsctx_string.go |  9 ++++++
 src/html/template/state_string.go | 37 ++++++++++++++++++++--
 src/html/template/transition.go   |  7 ++++-
 9 files changed, 116 insertions(+), 33 deletions(-)

diff --git a/src/html/template/context.go b/src/html/template/context.go
index f7d4849..0b65313 100644
--- a/src/html/template/context.go
+++ b/src/html/template/context.go
@@ -116,6 +116,8 @@ const (
	stateJSDqStr
	// stateJSSqStr occurs inside a JavaScript single quoted string.
	stateJSSqStr
+	// stateJSBqStr occurs inside a JavaScript back quoted string.
+	stateJSBqStr
	// stateJSRegexp occurs inside a JavaScript regexp literal.
	stateJSRegexp
	// stateJSBlockCmt occurs inside a JavaScript /* block comment */.
diff --git a/src/html/template/error.go b/src/html/template/error.go
index 0e52706..fd26b64 100644
--- a/src/html/template/error.go
+++ b/src/html/template/error.go
@@ -211,6 +211,19 @@ const (
	//   pipeline occurs in an unquoted attribute value context, "html" is
	//   disallowed. Avoid using "html" and "urlquery" entirely in new templates.
	ErrPredefinedEscaper
+
+	// errJSTmplLit: "... appears in a JS template literal"
+	// Example:
+	//     <script>var tmpl = `{{.Interp}`</script>
+	// Discussion:
+	//   Package html/template does not support actions inside of JS template
+	//   literals.
+	//
+	// TODO(rolandshoemaker): we cannot add this as an exported error in a minor
+	// release, since it is backwards incompatible with the other minor
+	// releases. As such we need to leave it unexported, and then we'll add it
+	// in the next major release.
+	errJSTmplLit
 )

 func (e *Error) Error() string {
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
index f12dafa..29ca5b3 100644
--- a/src/html/template/escape.go
+++ b/src/html/template/escape.go
@@ -8,6 +8,7 @@ import (
	"bytes"
	"fmt"
	"html"
+	"internal/godebug"
	"io"
	"text/template"
	"text/template/parse"
@@ -203,6 +204,16 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
		c.jsCtx = jsCtxDivOp
	case stateJSDqStr, stateJSSqStr:
		s = append(s, "_html_template_jsstrescaper")
+	case stateJSBqStr:
+		debugAllowActionJSTmpl := godebug.Get("jstmpllitinterp")
+		if debugAllowActionJSTmpl == "1" {
+			s = append(s, "_html_template_jsstrescaper")
+		} else {
+			return context{
+				state: stateError,
+				err:   errorf(errJSTmplLit, n, n.Line, "%s appears in a JS template literal", n),
+			}
+		}
	case stateJSRegexp:
		s = append(s, "_html_template_jsregexpescaper")
	case stateCSS:
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
index fa2b84a..1b150e9 100644
--- a/src/html/template/escape_test.go
+++ b/src/html/template/escape_test.go
@@ -681,35 +681,31 @@ func TestEscape(t *testing.T) {
	}

	for _, test := range tests {
-		tmpl := New(test.name)
-		tmpl = Must(tmpl.Parse(test.input))
-		// Check for bug 6459: Tree field was not set in Parse.
-		if tmpl.Tree != tmpl.text.Tree {
-			t.Errorf("%s: tree not set properly", test.name)
-			continue
-		}
-		b := new(bytes.Buffer)
-		if err := tmpl.Execute(b, data); err != nil {
-			t.Errorf("%s: template execution failed: %s", test.name, err)
-			continue
-		}
-		if w, g := test.output, b.String(); w != g {
-			t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
-			continue
-		}
-		b.Reset()
-		if err := tmpl.Execute(b, pdata); err != nil {
-			t.Errorf("%s: template execution failed for pointer: %s", test.name, err)
-			continue
-		}
-		if w, g := test.output, b.String(); w != g {
-			t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
-			continue
-		}
-		if tmpl.Tree != tmpl.text.Tree {
-			t.Errorf("%s: tree mismatch", test.name)
-			continue
-		}
+		t.Run(test.name, func(t *testing.T) {
+			tmpl := New(test.name)
+			tmpl = Must(tmpl.Parse(test.input))
+			// Check for bug 6459: Tree field was not set in Parse.
+			if tmpl.Tree != tmpl.text.Tree {
+				t.Fatalf("%s: tree not set properly", test.name)
+			}
+			b := new(strings.Builder)
+			if err := tmpl.Execute(b, data); err != nil {
+				t.Fatalf("%s: template execution failed: %s", test.name, err)
+			}
+			if w, g := test.output, b.String(); w != g {
+				t.Fatalf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
+			}
+			b.Reset()
+			if err := tmpl.Execute(b, pdata); err != nil {
+				t.Fatalf("%s: template execution failed for pointer: %s", test.name, err)
+			}
+			if w, g := test.output, b.String(); w != g {
+				t.Fatalf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
+			}
+			if tmpl.Tree != tmpl.text.Tree {
+				t.Fatalf("%s: tree mismatch", test.name)
+			}
+		})
	}
 }

@@ -936,6 +932,10 @@ func TestErrors(t *testing.T) {
			"{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
			"",
		},
+		{
+			"<script>var a = `${a+b}`</script>`",
+			"",
+		},
		// Error cases.
		{
			"{{if .Cond}}<a{{end}}",
@@ -1082,6 +1082,10 @@ func TestErrors(t *testing.T) {
			// html is allowed since it is the last command in the pipeline, but urlquery is not.
			`predefined escaper "urlquery" disallowed in template`,
		},
+		{
+			"<script>var tmpl = `asd {{.}}`;</script>",
+			`{{.}} appears in a JS template literal`,
+		},
	}
	for _, test := range tests {
		buf := new(bytes.Buffer)
@@ -1304,6 +1308,10 @@ func TestEscapeText(t *testing.T) {
			context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
		},
		{
+			"<a onclick=\"`foo",
+			context{state: stateJSBqStr, delim: delimDoubleQuote, attr: attrScript},
+		},
+		{
			`<A ONCLICK="'`,
			context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
		},
diff --git a/src/html/template/js.go b/src/html/template/js.go
index ea9c183..b888eaf 100644
--- a/src/html/template/js.go
+++ b/src/html/template/js.go
@@ -308,6 +308,7 @@ var jsStrReplacementTable = []string{
	// Encode HTML specials as hex so the output can be embedded
	// in HTML attributes without further encoding.
	'"':  `\u0022`,
+	'`':  `\u0060`,
	'&':  `\u0026`,
	'\'': `\u0027`,
	'+':  `\u002b`,
@@ -331,6 +332,7 @@ var jsStrNormReplacementTable = []string{
	'"':  `\u0022`,
	'&':  `\u0026`,
	'\'': `\u0027`,
+	'`':  `\u0060`,
	'+':  `\u002b`,
	'/':  `\/`,
	'<':  `\u003c`,
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
index d7ee47b..7d963ae 100644
--- a/src/html/template/js_test.go
+++ b/src/html/template/js_test.go
@@ -292,7 +292,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
				`0123456789:;\u003c=\u003e?` +
				`@ABCDEFGHIJKLMNO` +
				`PQRSTUVWXYZ[\\]^_` +
-				"`abcdefghijklmno" +
+				"\\u0060abcdefghijklmno" +
				"pqrstuvwxyz{|}~\u007f" +
				"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
		},
diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go
index dd1d87e..2394893 100644
--- a/src/html/template/jsctx_string.go
+++ b/src/html/template/jsctx_string.go
@@ -4,6 +4,15 @@ package template

 import "strconv"

+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[jsCtxRegexp-0]
+	_ = x[jsCtxDivOp-1]
+	_ = x[jsCtxUnknown-2]
+}
+
 const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown"

 var _jsCtx_index = [...]uint8{0, 11, 21, 33}
diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go
index 05104be..6fb1a6e 100644
--- a/src/html/template/state_string.go
+++ b/src/html/template/state_string.go
@@ -4,9 +4,42 @@ package template

 import "strconv"

-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError"
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[stateText-0]
+	_ = x[stateTag-1]
+	_ = x[stateAttrName-2]
+	_ = x[stateAfterName-3]
+	_ = x[stateBeforeValue-4]
+	_ = x[stateHTMLCmt-5]
+	_ = x[stateRCDATA-6]
+	_ = x[stateAttr-7]
+	_ = x[stateURL-8]
+	_ = x[stateSrcset-9]
+	_ = x[stateJS-10]
+	_ = x[stateJSDqStr-11]
+	_ = x[stateJSSqStr-12]
+	_ = x[stateJSBqStr-13]
+	_ = x[stateJSRegexp-14]
+	_ = x[stateJSBlockCmt-15]
+	_ = x[stateJSLineCmt-16]
+	_ = x[stateCSS-17]
+	_ = x[stateCSSDqStr-18]
+	_ = x[stateCSSSqStr-19]
+	_ = x[stateCSSDqURL-20]
+	_ = x[stateCSSSqURL-21]
+	_ = x[stateCSSURL-22]
+	_ = x[stateCSSBlockCmt-23]
+	_ = x[stateCSSLineCmt-24]
+	_ = x[stateError-25]
+	_ = x[stateDead-26]
+}
+
+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"

-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}
+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317}

 func (i state) String() string {
	if i >= state(len(_state_index)-1) {
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
index 06df679..92eb351 100644
--- a/src/html/template/transition.go
+++ b/src/html/template/transition.go
@@ -27,6 +27,7 @@ var transitionFunc = [...]func(context, []byte) (context, int){
	stateJS:          tJS,
	stateJSDqStr:     tJSDelimited,
	stateJSSqStr:     tJSDelimited,
+	stateJSBqStr:     tJSDelimited,
	stateJSRegexp:    tJSDelimited,
	stateJSBlockCmt:  tBlockCmt,
	stateJSLineCmt:   tLineCmt,
@@ -262,7 +263,7 @@ func tURL(c context, s []byte) (context, int) {

 // tJS is the context transition function for the JS state.
 func tJS(c context, s []byte) (context, int) {
-	i := bytes.IndexAny(s, `"'/`)
+	i := bytes.IndexAny(s, "\"`'/")
	if i == -1 {
		// Entire input is non string, comment, regexp tokens.
		c.jsCtx = nextJSCtx(s, c.jsCtx)
@@ -274,6 +275,8 @@ func tJS(c context, s []byte) (context, int) {
		c.state, c.jsCtx = stateJSDqStr, jsCtxRegexp
	case '\'':
		c.state, c.jsCtx = stateJSSqStr, jsCtxRegexp
+	case '`':
+		c.state, c.jsCtx = stateJSBqStr, jsCtxRegexp
	case '/':
		switch {
		case i+1 < len(s) && s[i+1] == '/':
@@ -303,6 +306,8 @@ func tJSDelimited(c context, s []byte) (context, int) {
	switch c.state {
	case stateJSSqStr:
		specials = `\'`
+	case stateJSBqStr:
+		specials = "`\\"
	case stateJSRegexp:
		specials = `\/[]`
	}
--
2.7.4