From 74b22a5e918b8d3a71748880e9ab2179f6486c71 Mon Sep 17 00:00:00 2001 From: Shubham Kulkarni Date: Sat, 30 Sep 2023 00:50:33 +0530 Subject: go: Update fix for CVE-2023-24538 & CVE-2023-39318 Add missing files in fix for CVE-2023-24538 & CVE-2023-39318 Upstream Link - CVE-2023-24538: https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b CVE-2023-39318: https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c (From OE-Core rev: 0d8f7062d4fb5525f34427b1a7304f165bee0cfc) Signed-off-by: Shubham Kulkarni Signed-off-by: Steve Sakoman --- meta/recipes-devtools/go/go-1.17.13.inc | 3 +- .../go/go-1.18/CVE-2023-24538.patch | 208 ------- .../go/go-1.18/CVE-2023-24538_1.patch | 597 +++++++++++++++++++++ .../go/go-1.18/CVE-2023-24538_2.patch | 371 +++++++++++++ .../go/go-1.21/CVE-2023-39318.patch | 44 +- 5 files changed, 1004 insertions(+), 219 deletions(-) delete mode 100644 meta/recipes-devtools/go/go-1.18/CVE-2023-24538.patch create mode 100644 meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch create mode 100644 meta/recipes-devtools/go/go-1.18/CVE-2023-24538_2.patch diff --git a/meta/recipes-devtools/go/go-1.17.13.inc b/meta/recipes-devtools/go/go-1.17.13.inc index ed2645bc12..461819d80f 100644 --- a/meta/recipes-devtools/go/go-1.17.13.inc +++ b/meta/recipes-devtools/go/go-1.17.13.inc @@ -29,7 +29,8 @@ SRC_URI += "\ file://CVE-2022-41722.patch \ file://CVE-2023-24537.patch \ file://CVE-2023-24534.patch \ - file://CVE-2023-24538.patch \ + file://CVE-2023-24538_1.patch \ + file://CVE-2023-24538_2.patch \ file://CVE-2023-24540.patch \ file://CVE-2023-24539.patch \ file://CVE-2023-29404.patch \ diff --git a/meta/recipes-devtools/go/go-1.18/CVE-2023-24538.patch b/meta/recipes-devtools/go/go-1.18/CVE-2023-24538.patch deleted file mode 100644 index 502486befc..0000000000 --- a/meta/recipes-devtools/go/go-1.18/CVE-2023-24538.patch +++ /dev/null @@ -1,208 +0,0 @@ -From 07cc3b8711a8efbb5885f56dd90d854049ad2f7d Mon Sep 17 00:00:00 2001 -From: Roland Shoemaker -Date: Mon, 20 Mar 2023 11:01:13 -0700 -Subject: [PATCH] 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 -Run-TryBot: Damien Neil -Reviewed-by: Julie Qiu -Reviewed-by: Roland Shoemaker -Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612 -Run-TryBot: Roland Shoemaker -Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c -Reviewed-on: https://go-review.googlesource.com/c/go/+/481987 -Auto-Submit: Michael Knyszek -TryBot-Result: Gopher Robot -Run-TryBot: Michael Knyszek -Reviewed-by: Matthew Dempsky - -Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b -CVE: CVE-2023-24538 -Signed-off-by: Shubham Kulkarni ---- - src/html/template/context.go | 2 ++ - src/html/template/error.go | 13 +++++++++++++ - src/html/template/escape.go | 11 +++++++++++ - src/html/template/js.go | 2 ++ - src/html/template/jsctx_string.go | 9 +++++++++ - src/html/template/transition.go | 7 ++++++- - 6 files changed, 43 insertions(+), 1 deletion(-) - -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: -+ // -+ // 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 8739735..ca078f4 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" -@@ -205,6 +206,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/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/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/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 diff --git a/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch b/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch new file mode 100644 index 0000000000..bb0a416f46 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_1.patch @@ -0,0 +1,597 @@ +From b1e4e8ec7e946ff2d3bb37ac99c5468ceb49c362 Mon Sep 17 00:00:00 2001 +From: Russ Cox +Date: Thu, 20 May 2021 12:46:33 -0400 +Subject: [PATCH 1/2] html/template, text/template: implement break and + continue for range loops + +Break and continue for range loops was accepted as a proposal in June 2017. +It was implemented in CL 66410 (Oct 2017) +but then rolled back in CL 92155 (Feb 2018) +because html/template changes had not been implemented. + +This CL reimplements break and continue in text/template +and then adds support for them in html/template as well. + +Fixes #20531. + +Change-Id: I05330482a976f1c078b4b49c2287bd9031bb7616 +Reviewed-on: https://go-review.googlesource.com/c/go/+/321491 +Trust: Russ Cox +Run-TryBot: Russ Cox +TryBot-Result: Go Bot +Reviewed-by: Rob Pike + +Upstream-Status: Backport from https://github.com/golang/go/commit/d0dd26a88c019d54f22463daae81e785f5867565 +CVE: CVE-2023-24538 +Signed-off-by: Shubham Kulkarni +--- + src/html/template/context.go | 4 ++ + src/html/template/escape.go | 71 ++++++++++++++++++++++++++++++++++- + src/html/template/escape_test.go | 24 ++++++++++++ + src/html/template/exec_test.go | 2 + + src/text/template/doc.go | 8 ++++ + src/text/template/exec.go | 24 +++++++++++- + src/text/template/exec_test.go | 2 + + src/text/template/parse/lex.go | 13 ++++++- + src/text/template/parse/lex_test.go | 2 + + src/text/template/parse/node.go | 36 ++++++++++++++++++ + src/text/template/parse/parse.go | 42 ++++++++++++++++++++- + src/text/template/parse/parse_test.go | 8 ++++ + 12 files changed, 232 insertions(+), 4 deletions(-) + +diff --git a/src/html/template/context.go b/src/html/template/context.go +index f7d4849..aaa7d08 100644 +--- a/src/html/template/context.go ++++ b/src/html/template/context.go +@@ -6,6 +6,7 @@ package template + + import ( + "fmt" ++ "text/template/parse" + ) + + // context describes the state an HTML parser must be in when it reaches the +@@ -22,6 +23,7 @@ type context struct { + jsCtx jsCtx + attr attr + element element ++ n parse.Node // for range break/continue + err *Error + } + +@@ -141,6 +143,8 @@ const ( + // stateError is an infectious error state outside any valid + // HTML/CSS/JS construct. + stateError ++ // stateDead marks unreachable code after a {{break}} or {{continue}}. ++ stateDead + ) + + // isComment is true for any state that contains content meant for template +diff --git a/src/html/template/escape.go b/src/html/template/escape.go +index 8739735..6dea79c 100644 +--- a/src/html/template/escape.go ++++ b/src/html/template/escape.go +@@ -97,6 +97,15 @@ type escaper struct { + actionNodeEdits map[*parse.ActionNode][]string + templateNodeEdits map[*parse.TemplateNode]string + textNodeEdits map[*parse.TextNode][]byte ++ // rangeContext holds context about the current range loop. ++ rangeContext *rangeContext ++} ++ ++// rangeContext holds information about the current range loop. ++type rangeContext struct { ++ outer *rangeContext // outer loop ++ breaks []context // context at each break action ++ continues []context // context at each continue action + } + + // makeEscaper creates a blank escaper for the given set. +@@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper { + map[*parse.ActionNode][]string{}, + map[*parse.TemplateNode]string{}, + map[*parse.TextNode][]byte{}, ++ nil, + } + } + +@@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context { + switch n := n.(type) { + case *parse.ActionNode: + return e.escapeAction(c, n) ++ case *parse.BreakNode: ++ c.n = n ++ e.rangeContext.breaks = append(e.rangeContext.breaks, c) ++ return context{state: stateDead} + case *parse.CommentNode: + return c ++ case *parse.ContinueNode: ++ c.n = n ++ e.rangeContext.continues = append(e.rangeContext.breaks, c) ++ return context{state: stateDead} + case *parse.IfNode: + return e.escapeBranch(c, &n.BranchNode, "if") + case *parse.ListNode: +@@ -427,6 +445,12 @@ func join(a, b context, node parse.Node, nodeName string) context { + if b.state == stateError { + return b + } ++ if a.state == stateDead { ++ return b ++ } ++ if b.state == stateDead { ++ return a ++ } + if a.eq(b) { + return a + } +@@ -466,14 +490,27 @@ func join(a, b context, node parse.Node, nodeName string) context { + + // escapeBranch escapes a branch template node: "if", "range" and "with". + func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context { ++ if nodeName == "range" { ++ e.rangeContext = &rangeContext{outer: e.rangeContext} ++ } + c0 := e.escapeList(c, n.List) +- if nodeName == "range" && c0.state != stateError { ++ if nodeName == "range" { ++ if c0.state != stateError { ++ c0 = joinRange(c0, e.rangeContext) ++ } ++ e.rangeContext = e.rangeContext.outer ++ if c0.state == stateError { ++ return c0 ++ } ++ + // The "true" branch of a "range" node can execute multiple times. + // We check that executing n.List once results in the same context + // as executing n.List twice. ++ e.rangeContext = &rangeContext{outer: e.rangeContext} + c1, _ := e.escapeListConditionally(c0, n.List, nil) + c0 = join(c0, c1, n, nodeName) + if c0.state == stateError { ++ e.rangeContext = e.rangeContext.outer + // Make clear that this is a problem on loop re-entry + // since developers tend to overlook that branch when + // debugging templates. +@@ -481,11 +518,39 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) + c0.err.Description = "on range loop re-entry: " + c0.err.Description + return c0 + } ++ c0 = joinRange(c0, e.rangeContext) ++ e.rangeContext = e.rangeContext.outer ++ if c0.state == stateError { ++ return c0 ++ } + } + c1 := e.escapeList(c, n.ElseList) + return join(c0, c1, n, nodeName) + } + ++func joinRange(c0 context, rc *rangeContext) context { ++ // Merge contexts at break and continue statements into overall body context. ++ // In theory we could treat breaks differently from continues, but for now it is ++ // enough to treat them both as going back to the start of the loop (which may then stop). ++ for _, c := range rc.breaks { ++ c0 = join(c0, c, c.n, "range") ++ if c0.state == stateError { ++ c0.err.Line = c.n.(*parse.BreakNode).Line ++ c0.err.Description = "at range loop break: " + c0.err.Description ++ return c0 ++ } ++ } ++ for _, c := range rc.continues { ++ c0 = join(c0, c, c.n, "range") ++ if c0.state == stateError { ++ c0.err.Line = c.n.(*parse.ContinueNode).Line ++ c0.err.Description = "at range loop continue: " + c0.err.Description ++ return c0 ++ } ++ } ++ return c0 ++} ++ + // escapeList escapes a list template node. + func (e *escaper) escapeList(c context, n *parse.ListNode) context { + if n == nil { +@@ -493,6 +558,9 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context { + } + for _, m := range n.Nodes { + c = e.escape(c, m) ++ if c.state == stateDead { ++ break ++ } + } + return c + } +@@ -503,6 +571,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context { + // which is the same as whether e was updated. + func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) { + e1 := makeEscaper(e.ns) ++ e1.rangeContext = e.rangeContext + // Make type inferences available to f. + for k, v := range e.output { + e1.output[k] = v +diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go +index fbc84a7..3b0aa8c 100644 +--- a/src/html/template/escape_test.go ++++ b/src/html/template/escape_test.go +@@ -920,6 +920,22 @@ func TestErrors(t *testing.T) { + "", + "", + }, ++ { ++ "{{range .Items}}{{end}}", ++ "", ++ }, ++ { ++ "{{range .Items}}{{continue}}{{end}}", ++ "", ++ }, ++ { ++ "{{range .Items}}{{break}}{{end}}", ++ "", ++ }, ++ { ++ "{{range .Items}}{{if .X}}{{break}}{{end}}{{end}}", ++ "", ++ }, + // Error cases. + { + "{{if .Cond}}{{end}}", ++ "z:1:29: at range loop break: {{range}} branches end in different contexts", ++ }, ++ { ++ "{{range .Items}}{{end}}", ++ "z:1:29: at range loop continue: {{range}} branches end in different contexts", ++ }, ++ { + " 1 { + s.setTopVar(2, index) + } ++ defer s.pop(mark) ++ defer func() { ++ // Consume panic(walkContinue) ++ if r := recover(); r != nil && r != walkContinue { ++ panic(r) ++ } ++ }() + s.walk(elem, r.List) +- s.pop(mark) + } + switch val.Kind() { + case reflect.Array, reflect.Slice: +diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go +index ef52164..586af55 100644 +--- a/src/text/template/exec_test.go ++++ b/src/text/template/exec_test.go +@@ -564,6 +564,8 @@ var execTests = []execTest{ + {"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true}, + {"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, + {"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, ++ {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true}, ++ {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true}, + {"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true}, + {"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true}, + {"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true}, +diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go +index 6784071..95e3377 100644 +--- a/src/text/template/parse/lex.go ++++ b/src/text/template/parse/lex.go +@@ -62,6 +62,8 @@ const ( + // Keywords appear after all the rest. + itemKeyword // used only to delimit the keywords + itemBlock // block keyword ++ itemBreak // break keyword ++ itemContinue // continue keyword + itemDot // the cursor, spelled '.' + itemDefine // define keyword + itemElse // else keyword +@@ -76,6 +78,8 @@ const ( + var key = map[string]itemType{ + ".": itemDot, + "block": itemBlock, ++ "break": itemBreak, ++ "continue": itemContinue, + "define": itemDefine, + "else": itemElse, + "end": itemEnd, +@@ -119,6 +123,8 @@ type lexer struct { + parenDepth int // nesting depth of ( ) exprs + line int // 1+number of newlines seen + startLine int // start line of this item ++ breakOK bool // break keyword allowed ++ continueOK bool // continue keyword allowed + } + + // next returns the next rune in the input. +@@ -461,7 +467,12 @@ Loop: + } + switch { + case key[word] > itemKeyword: +- l.emit(key[word]) ++ item := key[word] ++ if item == itemBreak && !l.breakOK || item == itemContinue && !l.continueOK { ++ l.emit(itemIdentifier) ++ } else { ++ l.emit(item) ++ } + case word[0] == '.': + l.emit(itemField) + case word == "true", word == "false": +diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go +index 6510eed..df6aabf 100644 +--- a/src/text/template/parse/lex_test.go ++++ b/src/text/template/parse/lex_test.go +@@ -35,6 +35,8 @@ var itemName = map[itemType]string{ + // keywords + itemDot: ".", + itemBlock: "block", ++ itemBreak: "break", ++ itemContinue: "continue", + itemDefine: "define", + itemElse: "else", + itemIf: "if", +diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go +index 177482f..4726822 100644 +--- a/src/text/template/parse/node.go ++++ b/src/text/template/parse/node.go +@@ -71,6 +71,8 @@ const ( + NodeVariable // A $ variable. + NodeWith // A with action. + NodeComment // A comment. ++ NodeBreak // A break action. ++ NodeContinue // A continue action. + ) + + // Nodes. +@@ -907,6 +909,40 @@ func (i *IfNode) Copy() Node { + return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) + } + ++// BreakNode represents a {{break}} action. ++type BreakNode struct { ++ tr *Tree ++ NodeType ++ Pos ++ Line int ++} ++ ++func (t *Tree) newBreak(pos Pos, line int) *BreakNode { ++ return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line} ++} ++ ++func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) } ++func (b *BreakNode) String() string { return "{{break}}" } ++func (b *BreakNode) tree() *Tree { return b.tr } ++func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") } ++ ++// ContinueNode represents a {{continue}} action. ++type ContinueNode struct { ++ tr *Tree ++ NodeType ++ Pos ++ Line int ++} ++ ++func (t *Tree) newContinue(pos Pos, line int) *ContinueNode { ++ return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line} ++} ++ ++func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) } ++func (c *ContinueNode) String() string { return "{{continue}}" } ++func (c *ContinueNode) tree() *Tree { return c.tr } ++func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") } ++ + // RangeNode represents a {{range}} action and its commands. + type RangeNode struct { + BranchNode +diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go +index 1a63961..d92bed5 100644 +--- a/src/text/template/parse/parse.go ++++ b/src/text/template/parse/parse.go +@@ -31,6 +31,7 @@ type Tree struct { + vars []string // variables defined at the moment. + treeSet map[string]*Tree + actionLine int // line of left delim starting action ++ rangeDepth int + mode Mode + } + +@@ -224,6 +225,8 @@ func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet ma + t.vars = []string{"$"} + t.funcs = funcs + t.treeSet = treeSet ++ lex.breakOK = !t.hasFunction("break") ++ lex.continueOK = !t.hasFunction("continue") + } + + // stopParse terminates parsing. +@@ -386,6 +389,10 @@ func (t *Tree) action() (n Node) { + switch token := t.nextNonSpace(); token.typ { + case itemBlock: + return t.blockControl() ++ case itemBreak: ++ return t.breakControl(token.pos, token.line) ++ case itemContinue: ++ return t.continueControl(token.pos, token.line) + case itemElse: + return t.elseControl() + case itemEnd: +@@ -405,6 +412,32 @@ func (t *Tree) action() (n Node) { + return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim)) + } + ++// Break: ++// {{break}} ++// Break keyword is past. ++func (t *Tree) breakControl(pos Pos, line int) Node { ++ if token := t.next(); token.typ != itemRightDelim { ++ t.unexpected(token, "in {{break}}") ++ } ++ if t.rangeDepth == 0 { ++ t.errorf("{{break}} outside {{range}}") ++ } ++ return t.newBreak(pos, line) ++} ++ ++// Continue: ++// {{continue}} ++// Continue keyword is past. ++func (t *Tree) continueControl(pos Pos, line int) Node { ++ if token := t.next(); token.typ != itemRightDelim { ++ t.unexpected(token, "in {{continue}}") ++ } ++ if t.rangeDepth == 0 { ++ t.errorf("{{continue}} outside {{range}}") ++ } ++ return t.newContinue(pos, line) ++} ++ + // Pipeline: + // declarations? command ('|' command)* + func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) { +@@ -480,8 +513,14 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) { + func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { + defer t.popVars(len(t.vars)) + pipe = t.pipeline(context, itemRightDelim) ++ if context == "range" { ++ t.rangeDepth++ ++ } + var next Node + list, next = t.itemList() ++ if context == "range" { ++ t.rangeDepth-- ++ } + switch next.Type() { + case nodeEnd: //done + case nodeElse: +@@ -523,7 +562,8 @@ func (t *Tree) ifControl() Node { + // {{range pipeline}} itemList {{else}} itemList {{end}} + // Range keyword is past. + func (t *Tree) rangeControl() Node { +- return t.newRange(t.parseControl(false, "range")) ++ r := t.newRange(t.parseControl(false, "range")) ++ return r + } + + // With: +diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go +index 9b1be27..c3679a0 100644 +--- a/src/text/template/parse/parse_test.go ++++ b/src/text/template/parse/parse_test.go +@@ -230,6 +230,10 @@ var parseTests = []parseTest{ + `{{range $x := .SI}}{{.}}{{end}}`}, + {"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError, + `{{range $x, $y := .SI}}{{.}}{{end}}`}, ++ {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError, ++ `{{range .SI}}{{.}}{{break}}{{end}}`}, ++ {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError, ++ `{{range .SI}}{{.}}{{continue}}{{end}}`}, + {"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError, + `{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`}, + {"template", "{{template `x`}}", noError, +@@ -279,6 +283,10 @@ var parseTests = []parseTest{ + {"adjacent args", "{{printf 3`x`}}", hasError, ""}, + {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, + {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, ++ {"break outside range", "{{range .}}{{end}} {{break}}", hasError, ""}, ++ {"continue outside range", "{{range .}}{{end}} {{continue}}", hasError, ""}, ++ {"break in range else", "{{range .}}{{else}}{{break}}{{end}}", hasError, ""}, ++ {"continue in range else", "{{range .}}{{else}}{{continue}}{{end}}", hasError, ""}, + // Other kinds of assignments and operators aren't available yet. + {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, + {"bug0b", "{{$x += 1}}{{$x}}", hasError, ""}, +-- +2.7.4 diff --git a/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_2.patch b/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_2.patch new file mode 100644 index 0000000000..f94f0f55c7 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.18/CVE-2023-24538_2.patch @@ -0,0 +1,371 @@ +From 07cc3b8711a8efbb5885f56dd90d854049ad2f7d Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker +Date: Mon, 20 Mar 2023 11:01:13 -0700 +Subject: [PATCH 2/2] 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 +Run-TryBot: Damien Neil +Reviewed-by: Julie Qiu +Reviewed-by: Roland Shoemaker +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802612 +Run-TryBot: Roland Shoemaker +Change-Id: Ic7f10595615f2b2740d9c85ad7ef40dc0e78c04c +Reviewed-on: https://go-review.googlesource.com/c/go/+/481987 +Auto-Submit: Michael Knyszek +TryBot-Result: Gopher Robot +Run-TryBot: Michael Knyszek +Reviewed-by: Matthew Dempsky + +Upstream-Status: Backport from https://github.com/golang/go/commit/b1e3ecfa06b67014429a197ec5e134ce4303ad9b +CVE: CVE-2023-24538 +Signed-off-by: Shubham Kulkarni +--- + 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: ++ // ++ // 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 8739735..ca078f4 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" +@@ -205,6 +206,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 3b0aa8c..a695b17 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}}{{if .X}}{{break}}{{end}}{{end}}", + "", + }, ++ { ++ "`", ++ "", ++ }, + // Error cases. + { + "{{if .Cond}}var tmpl = `asd {{.}}`;", ++ `{{.}} 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}, + }, + { ++ "= 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 diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch b/meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch index 85c6ec97c8..503a4a288a 100644 --- a/meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch @@ -32,11 +32,11 @@ CVE: CVE-2023-39318 Signed-off-by: Siddharth Doshi --- src/html/template/context.go | 6 ++- - src/html/template/escape.go | 5 +- - src/html/template/escape_test.go | 10 ++++ - src/html/template/state_string.go | 4 +- - src/html/template/transition.go | 80 ++++++++++++++++++++----------- - 5 files changed, 72 insertions(+), 33 deletions(-) + src/html/template/escape.go | 5 ++- + src/html/template/escape_test.go | 10 +++++ + src/html/template/state_string.go | 26 +++++++------ + src/html/template/transition.go | 80 +++++++++++++++++++++++++-------------- + 5 files changed, 84 insertions(+), 43 deletions(-) diff --git a/src/html/template/context.go b/src/html/template/context.go index f5f44a1..feb6517 100644 @@ -105,14 +105,38 @@ diff --git a/src/html/template/state_string.go b/src/html/template/state_string. index 05104be..b5cfe70 100644 --- a/src/html/template/state_string.go +++ b/src/html/template/state_string.go -@@ -4,9 +4,9 @@ package template - - import "strconv" +@@ -25,21 +25,23 @@ func _() { + _ = 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] ++ _ = x[stateJSHTMLOpenCmt-17] ++ _ = x[stateJSHTMLCloseCmt-18] ++ _ = x[stateCSS-19] ++ _ = x[stateCSSDqStr-20] ++ _ = x[stateCSSSqStr-21] ++ _ = x[stateCSSDqURL-22] ++ _ = x[stateCSSSqURL-23] ++ _ = x[stateCSSURL-24] ++ _ = x[stateCSSBlockCmt-25] ++ _ = x[stateCSSLineCmt-26] ++ _ = x[stateError-27] ++ _ = x[stateDead-28] + } --const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError" +-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" +const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" --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} +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} func (i state) String() string { -- cgit v1.2.3-54-g00ecf