diff options
Diffstat (limited to 'meta/recipes-devtools/go/go-1.21')
-rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2023-24531_1.patch | 252 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2023-24531_2.patch | 47 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch | 262 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2023-45289.patch | 121 | ||||
-rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2023-45290.patch | 270 |
5 files changed, 952 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_1.patch b/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_1.patch new file mode 100644 index 0000000000..5f6d7e16a8 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_1.patch @@ -0,0 +1,252 @@ +From 0f717b5f7d32bb660c01ec0366bd53c9b4c5ab5d Mon Sep 17 00:00:00 2001 +From: Michael Matloob <matloob@golang.org> +Date: Mon, 24 Apr 2023 16:57:28 -0400 +Subject: [PATCH 1/2] cmd/go: sanitize go env outputs + +go env, without any arguments, outputs the environment variables in +the form of a script that can be run on the host OS. On Unix, single +quote the strings and place single quotes themselves outside the +single quoted strings. On windows use the set "var=val" syntax with +the quote starting before the variable. + +Fixes #58508 + +Change-Id: Iecd379a4af7285ea9b2024f0202250c74fd9a2bd +Reviewed-on: https://go-review.googlesource.com/c/go/+/488375 +TryBot-Result: Gopher Robot <gobot@golang.org> +Reviewed-by: Michael Matloob <matloob@golang.org> +Reviewed-by: Damien Neil <dneil@google.com> +Run-TryBot: Michael Matloob <matloob@golang.org> +Reviewed-by: Bryan Mills <bcmills@google.com> +Reviewed-by: Quim Muntal <quimmuntal@gmail.com> + +CVE: CVE-2023-24531 +Upstream-Status: Backport [f379e78951a405e7e99a60fb231eeedbf976c108] + +Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> +--- + src/cmd/go/internal/envcmd/env.go | 60 ++++++++++++- + src/cmd/go/internal/envcmd/env_test.go | 94 +++++++++++++++++++++ + src/cmd/go/testdata/script/env_sanitize.txt | 5 ++ + 3 files changed, 157 insertions(+), 2 deletions(-) + create mode 100644 src/cmd/go/internal/envcmd/env_test.go + create mode 100644 src/cmd/go/testdata/script/env_sanitize.txt + +diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go +index 43b94e7..0ce8843 100644 +--- a/src/cmd/go/internal/envcmd/env.go ++++ b/src/cmd/go/internal/envcmd/env.go +@@ -6,6 +6,7 @@ + package envcmd + + import ( ++ "bytes" + "context" + "encoding/json" + "fmt" +@@ -17,6 +18,7 @@ import ( + "runtime" + "sort" + "strings" ++ "unicode" + "unicode/utf8" + + "cmd/go/internal/base" +@@ -379,9 +381,12 @@ func checkBuildConfig(add map[string]string, del map[string]bool) error { + func PrintEnv(w io.Writer, env []cfg.EnvVar) { + for _, e := range env { + if e.Name != "TERM" { ++ if runtime.GOOS != "plan9" && bytes.Contains([]byte(e.Value), []byte{0}) { ++ base.Fatalf("go: internal error: encountered null byte in environment variable %s on non-plan9 platform", e.Name) ++ } + switch runtime.GOOS { + default: +- fmt.Fprintf(w, "%s=\"%s\"\n", e.Name, e.Value) ++ fmt.Fprintf(w, "%s=%s\n", e.Name, shellQuote(e.Value)) + case "plan9": + if strings.IndexByte(e.Value, '\x00') < 0 { + fmt.Fprintf(w, "%s='%s'\n", e.Name, strings.ReplaceAll(e.Value, "'", "''")) +@@ -392,17 +397,68 @@ func PrintEnv(w io.Writer, env []cfg.EnvVar) { + if x > 0 { + fmt.Fprintf(w, " ") + } ++ // TODO(#59979): Does this need to be quoted like above? + fmt.Fprintf(w, "%s", s) + } + fmt.Fprintf(w, ")\n") + } + case "windows": +- fmt.Fprintf(w, "set %s=%s\n", e.Name, e.Value) ++ if hasNonGraphic(e.Value) { ++ base.Errorf("go: stripping unprintable or unescapable characters from %%%q%%", e.Name) ++ } ++ fmt.Fprintf(w, "set %s=%s\n", e.Name, batchEscape(e.Value)) + } + } + } + } + ++func hasNonGraphic(s string) bool { ++ for _, c := range []byte(s) { ++ if c == '\r' || c == '\n' || (!unicode.IsGraphic(rune(c)) && !unicode.IsSpace(rune(c))) { ++ return true ++ } ++ } ++ return false ++} ++ ++func shellQuote(s string) string { ++ var b bytes.Buffer ++ b.WriteByte('\'') ++ for _, x := range []byte(s) { ++ if x == '\'' { ++ // Close the single quoted string, add an escaped single quote, ++ // and start another single quoted string. ++ b.WriteString(`'\''`) ++ } else { ++ b.WriteByte(x) ++ } ++ } ++ b.WriteByte('\'') ++ return b.String() ++} ++ ++func batchEscape(s string) string { ++ var b bytes.Buffer ++ for _, x := range []byte(s) { ++ if x == '\r' || x == '\n' || (!unicode.IsGraphic(rune(x)) && !unicode.IsSpace(rune(x))) { ++ b.WriteRune(unicode.ReplacementChar) ++ continue ++ } ++ switch x { ++ case '%': ++ b.WriteString("%%") ++ case '<', '>', '|', '&', '^': ++ // These are special characters that need to be escaped with ^. See ++ // https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/set_1. ++ b.WriteByte('^') ++ b.WriteByte(x) ++ default: ++ b.WriteByte(x) ++ } ++ } ++ return b.String() ++} ++ + func printEnvAsJSON(env []cfg.EnvVar) { + m := make(map[string]string) + for _, e := range env { +diff --git a/src/cmd/go/internal/envcmd/env_test.go b/src/cmd/go/internal/envcmd/env_test.go +new file mode 100644 +index 0000000..32d99fd +--- /dev/null ++++ b/src/cmd/go/internal/envcmd/env_test.go +@@ -0,0 +1,94 @@ ++// Copyright 2022 The Go Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style ++// license that can be found in the LICENSE file. ++ ++//go:build unix || windows ++ ++package envcmd ++ ++import ( ++ "bytes" ++ "cmd/go/internal/cfg" ++ "fmt" ++ "internal/testenv" ++ "os" ++ "os/exec" ++ "path/filepath" ++ "runtime" ++ "testing" ++ "unicode" ++) ++ ++func FuzzPrintEnvEscape(f *testing.F) { ++ f.Add(`$(echo 'cc"'; echo 'OOPS="oops')`) ++ f.Add("$(echo shell expansion 1>&2)") ++ f.Add("''") ++ f.Add(`C:\"Program Files"\`) ++ f.Add(`\\"Quoted Host"\\share`) ++ f.Add("\xfb") ++ f.Add("0") ++ f.Add("") ++ f.Add("''''''''") ++ f.Add("\r") ++ f.Add("\n") ++ f.Add("E,%") ++ f.Fuzz(func(t *testing.T, s string) { ++ t.Parallel() ++ ++ for _, c := range []byte(s) { ++ if c == 0 { ++ t.Skipf("skipping %q: contains a null byte. Null bytes can't occur in the environment"+ ++ " outside of Plan 9, which has different code path than Windows and Unix that this test"+ ++ " isn't testing.", s) ++ } ++ if c > unicode.MaxASCII { ++ t.Skipf("skipping %#q: contains a non-ASCII character %q", s, c) ++ } ++ if !unicode.IsGraphic(rune(c)) && !unicode.IsSpace(rune(c)) { ++ t.Skipf("skipping %#q: contains non-graphic character %q", s, c) ++ } ++ if runtime.GOOS == "windows" && c == '\r' || c == '\n' { ++ t.Skipf("skipping %#q on Windows: contains unescapable character %q", s, c) ++ } ++ } ++ ++ var b bytes.Buffer ++ if runtime.GOOS == "windows" { ++ b.WriteString("@echo off\n") ++ } ++ PrintEnv(&b, []cfg.EnvVar{{Name: "var", Value: s}}) ++ var want string ++ if runtime.GOOS == "windows" { ++ fmt.Fprintf(&b, "echo \"%%var%%\"\n") ++ want += "\"" + s + "\"\r\n" ++ } else { ++ fmt.Fprintf(&b, "printf '%%s\\n' \"$var\"\n") ++ want += s + "\n" ++ } ++ scriptfilename := "script.sh" ++ if runtime.GOOS == "windows" { ++ scriptfilename = "script.bat" ++ } ++ scriptfile := filepath.Join(t.TempDir(), scriptfilename) ++ if err := os.WriteFile(scriptfile, b.Bytes(), 0777); err != nil { ++ t.Fatal(err) ++ } ++ t.Log(b.String()) ++ var cmd *exec.Cmd ++ if runtime.GOOS == "windows" { ++ cmd = testenv.Command(t, "cmd.exe", "/C", scriptfile) ++ } else { ++ cmd = testenv.Command(t, "sh", "-c", scriptfile) ++ } ++ out, err := cmd.Output() ++ t.Log(string(out)) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if string(out) != want { ++ t.Fatalf("output of running PrintEnv script and echoing variable: got: %q, want: %q", ++ string(out), want) ++ } ++ }) ++} +diff --git a/src/cmd/go/testdata/script/env_sanitize.txt b/src/cmd/go/testdata/script/env_sanitize.txt +new file mode 100644 +index 0000000..cc4d23a +--- /dev/null ++++ b/src/cmd/go/testdata/script/env_sanitize.txt +@@ -0,0 +1,5 @@ ++env GOFLAGS='$(echo ''cc"''; echo ''OOPS="oops'')' ++go env ++[GOOS:darwin] stdout 'GOFLAGS=''\$\(echo ''\\''''cc"''\\''''; echo ''\\''''OOPS="oops''\\''''\)''' ++[GOOS:linux] stdout 'GOFLAGS=''\$\(echo ''\\''''cc"''\\''''; echo ''\\''''OOPS="oops''\\''''\)''' ++[GOOS:windows] stdout 'set GOFLAGS=\$\(echo ''cc"''; echo ''OOPS="oops''\)' +-- +2.35.5 + diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_2.patch b/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_2.patch new file mode 100644 index 0000000000..eecc04c2e3 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-24531_2.patch @@ -0,0 +1,47 @@ +From b2624f973692ca093348395c2418d1c422f2a162 Mon Sep 17 00:00:00 2001 +From: miller <millerresearch@gmail.com> +Date: Mon, 8 May 2023 16:56:21 +0100 +Subject: [PATCH 2/2] cmd/go: quote entries in list-valued variables for go env + in plan9 + +When 'go env' without an argument prints environment variables as +a script which can be executed by the shell, variables with a +list value in Plan 9 (such as GOPATH) need to be printed with each +element enclosed in single quotes in case it contains characters +significant to the Plan 9 shell (such as ' ' or '='). + +For #58508 + +Change-Id: Ia30f51307cc6d07a7e3ada6bf9d60bf9951982ff +Reviewed-on: https://go-review.googlesource.com/c/go/+/493535 +Run-TryBot: Cherry Mui <cherryyz@google.com> +Reviewed-by: Cherry Mui <cherryyz@google.com> +Reviewed-by: Russ Cox <rsc@golang.org> +TryBot-Result: Gopher Robot <gobot@golang.org> +Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> + +CVE: CVE-2023-24531 +Upstream-Status: Backport [05cc9e55876874462a4726ca0101c970838c80e5] + +Signed-off-by: Sakib Sajal <sakib.sajal@windriver.com> +--- + src/cmd/go/internal/envcmd/env.go | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go +index 0ce8843..b48d0bd 100644 +--- a/src/cmd/go/internal/envcmd/env.go ++++ b/src/cmd/go/internal/envcmd/env.go +@@ -397,8 +397,7 @@ func PrintEnv(w io.Writer, env []cfg.EnvVar) { + if x > 0 { + fmt.Fprintf(w, " ") + } +- // TODO(#59979): Does this need to be quoted like above? +- fmt.Fprintf(w, "%s", s) ++ fmt.Fprintf(w, "'%s'", strings.ReplaceAll(s, "'", "''")) + } + fmt.Fprintf(w, ")\n") + } +-- +2.35.5 + 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 new file mode 100644 index 0000000000..503a4a288a --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-39318.patch @@ -0,0 +1,262 @@ +From 023b542edf38e2a1f87fcefb9f75ff2f99401b4c Mon Sep 17 00:00:00 2001 +From: Roland Shoemaker <bracewell@google.com> +Date: Thu, 3 Aug 2023 12:24:13 -0700 +Subject: [PATCH] [release-branch.go1.20] html/template: support HTML-like + comments in script contexts + +Per Appendix B.1.1 of the ECMAScript specification, support HTML-like +comments in script contexts. Also per section 12.5, support hashbang +comments. This brings our parsing in-line with how browsers treat these +comment types. + +Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for +reporting this issue. + +Fixes #62196 +Fixes #62395 +Fixes CVE-2023-39318 + +Change-Id: Id512702c5de3ae46cf648e268cb10e1eb392a181 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976593 +Run-TryBot: Roland Shoemaker <bracewell@google.com> +Reviewed-by: Tatiana Bradley <tatianabradley@google.com> +Reviewed-by: Damien Neil <dneil@google.com> +Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014620 +Reviewed-on: https://go-review.googlesource.com/c/go/+/526098 +Run-TryBot: Cherry Mui <cherryyz@google.com> +TryBot-Result: Gopher Robot <gobot@golang.org> + +Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c] +CVE: CVE-2023-39318 +Signed-off-by: Siddharth Doshi <sdoshi@mvista.com> +--- + 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 | 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 +--- a/src/html/template/context.go ++++ b/src/html/template/context.go +@@ -124,6 +124,10 @@ const ( + stateJSBlockCmt + // stateJSLineCmt occurs inside a JavaScript // line comment. + stateJSLineCmt ++ // stateJSHTMLOpenCmt occurs inside a JavaScript <!-- HTML-like comment. ++ stateJSHTMLOpenCmt ++ // stateJSHTMLCloseCmt occurs inside a JavaScript --> HTML-like comment. ++ stateJSHTMLCloseCmt + // stateCSS occurs inside a <style> element or style attribute. + stateCSS + // stateCSSDqStr occurs inside a CSS double quoted string. +@@ -149,7 +153,7 @@ const ( + // authors & maintainers, not for end-users or machines. + func isComment(s state) bool { + switch s { +- case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt: ++ case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt, stateCSSBlockCmt, stateCSSLineCmt: + return true + } + return false +diff --git a/src/html/template/escape.go b/src/html/template/escape.go +index 1747ec9..b0085ce 100644 +--- a/src/html/template/escape.go ++++ b/src/html/template/escape.go +@@ -721,9 +721,12 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context { + if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone { + // Preserve the portion between written and the comment start. + cs := i1 - 2 +- if c1.state == stateHTMLCmt { ++ if c1.state == stateHTMLCmt || c1.state == stateJSHTMLOpenCmt { + // "<!--" instead of "/*" or "//" + cs -= 2 ++ } else if c1.state == stateJSHTMLCloseCmt { ++ // "-->" instead of "/*" or "//" ++ cs -= 1 + } + b.Write(s[written:cs]) + written = i1 +diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go +index 7853daa..bff38c6 100644 +--- a/src/html/template/escape_test.go ++++ b/src/html/template/escape_test.go +@@ -503,6 +503,16 @@ func TestEscape(t *testing.T) { + "<script>var a/*b*///c\nd</script>", + "<script>var a \nd</script>", + }, ++ { ++ "JS HTML-like comments", ++ "<script>before <!-- beep\nbetween\nbefore-->boop\n</script>", ++ "<script>before \nbetween\nbefore\n</script>", ++ }, ++ { ++ "JS hashbang comment", ++ "<script>#! beep\n</script>", ++ "<script>\n</script>", ++ }, + { + "Special tags in <script> string literals", + `<script>var a = "asd < 123 <!-- 456 < fgh <script jkl < 789 </script"</script>`, +diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go +index 05104be..b5cfe70 100644 +--- a/src/html/template/state_string.go ++++ b/src/html/template/state_string.go +@@ -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 = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" ++const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead" + +-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 { + if i >= state(len(_state_index)-1) { +diff --git a/src/html/template/transition.go b/src/html/template/transition.go +index e2660cc..3d2a37c 100644 +--- a/src/html/template/transition.go ++++ b/src/html/template/transition.go +@@ -14,32 +14,34 @@ import ( + // the updated context and the number of bytes consumed from the front of the + // input. + var transitionFunc = [...]func(context, []byte) (context, int){ +- stateText: tText, +- stateTag: tTag, +- stateAttrName: tAttrName, +- stateAfterName: tAfterName, +- stateBeforeValue: tBeforeValue, +- stateHTMLCmt: tHTMLCmt, +- stateRCDATA: tSpecialTagEnd, +- stateAttr: tAttr, +- stateURL: tURL, +- stateSrcset: tURL, +- stateJS: tJS, +- stateJSDqStr: tJSDelimited, +- stateJSSqStr: tJSDelimited, +- stateJSBqStr: tJSDelimited, +- stateJSRegexp: tJSDelimited, +- stateJSBlockCmt: tBlockCmt, +- stateJSLineCmt: tLineCmt, +- stateCSS: tCSS, +- stateCSSDqStr: tCSSStr, +- stateCSSSqStr: tCSSStr, +- stateCSSDqURL: tCSSStr, +- stateCSSSqURL: tCSSStr, +- stateCSSURL: tCSSStr, +- stateCSSBlockCmt: tBlockCmt, +- stateCSSLineCmt: tLineCmt, +- stateError: tError, ++ stateText: tText, ++ stateTag: tTag, ++ stateAttrName: tAttrName, ++ stateAfterName: tAfterName, ++ stateBeforeValue: tBeforeValue, ++ stateHTMLCmt: tHTMLCmt, ++ stateRCDATA: tSpecialTagEnd, ++ stateAttr: tAttr, ++ stateURL: tURL, ++ stateSrcset: tURL, ++ stateJS: tJS, ++ stateJSDqStr: tJSDelimited, ++ stateJSSqStr: tJSDelimited, ++ stateJSBqStr: tJSDelimited, ++ stateJSRegexp: tJSDelimited, ++ stateJSBlockCmt: tBlockCmt, ++ stateJSLineCmt: tLineCmt, ++ stateJSHTMLOpenCmt: tLineCmt, ++ stateJSHTMLCloseCmt: tLineCmt, ++ stateCSS: tCSS, ++ stateCSSDqStr: tCSSStr, ++ stateCSSSqStr: tCSSStr, ++ stateCSSDqURL: tCSSStr, ++ stateCSSSqURL: tCSSStr, ++ stateCSSURL: tCSSStr, ++ stateCSSBlockCmt: tBlockCmt, ++ stateCSSLineCmt: tLineCmt, ++ stateError: tError, + } + + var commentStart = []byte("<!--") +@@ -268,7 +270,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) +@@ -298,6 +300,26 @@ func tJS(c context, s []byte) (context, int) { + err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", s[i:]), + }, len(s) + } ++ // ECMAScript supports HTML style comments for legacy reasons, see Appendix ++ // B.1.1 "HTML-like Comments". The handling of these comments is somewhat ++ // confusing. Multi-line comments are not supported, i.e. anything on lines ++ // between the opening and closing tokens is not considered a comment, but ++ // anything following the opening or closing token, on the same line, is ++ // ignored. As such we simply treat any line prefixed with "<!--" or "-->" ++ // as if it were actually prefixed with "//" and move on. ++ case '<': ++ if i+3 < len(s) && bytes.Equal(commentStart, s[i:i+4]) { ++ c.state, i = stateJSHTMLOpenCmt, i+3 ++ } ++ case '-': ++ if i+2 < len(s) && bytes.Equal(commentEnd, s[i:i+3]) { ++ c.state, i = stateJSHTMLCloseCmt, i+2 ++ } ++ // ECMAScript also supports "hashbang" comment lines, see Section 12.5. ++ case '#': ++ if i+1 < len(s) && s[i+1] == '!' { ++ c.state, i = stateJSLineCmt, i+1 ++ } + default: + panic("unreachable") + } +@@ -387,12 +409,12 @@ func tBlockCmt(c context, s []byte) (context, int) { + return c, i + 2 + } + +-// tLineCmt is the context transition function for //comment states. ++// tLineCmt is the context transition function for //comment states, and the JS HTML-like comment state. + func tLineCmt(c context, s []byte) (context, int) { + var lineTerminators string + var endState state + switch c.state { +- case stateJSLineCmt: ++ case stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt: + lineTerminators, endState = "\n\r\u2028\u2029", stateJS + case stateCSSLineCmt: + lineTerminators, endState = "\n\f\r", stateCSS +-- +2.35.7 + diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2023-45289.patch b/meta/recipes-devtools/go/go-1.21/CVE-2023-45289.patch new file mode 100644 index 0000000000..f8ac64472f --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-45289.patch @@ -0,0 +1,121 @@ +From 3a855208e3efed2e9d7c20ad023f1fa78afcc0be Mon Sep 17 00:00:00 2001 +From: Damien Neil <dneil@google.com> +Date: Thu, 11 Jan 2024 11:31:57 -0800 +Subject: [PATCH] [release-branch.go1.22] net/http, net/http/cookiejar: avoid + subdomain matches on IPv6 zones + +When deciding whether to forward cookies or sensitive headers +across a redirect, do not attempt to interpret an IPv6 address +as a domain name. + +Avoids a case where a maliciously-crafted redirect to an +IPv6 address with a scoped addressing zone could be +misinterpreted as a within-domain redirect. For example, +we could interpret "::1%.www.example.com" as a subdomain +of "www.example.com". + +Thanks to Juho Nurminen of Mattermost for reporting this issue. + +Fixes CVE-2023-45289 +Fixes #65859 +For #65065 + +Change-Id: I8f463f59f0e700c8a18733d2b264a8bcb3a19599 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2131938 +Reviewed-by: Tatiana Bradley <tatianabradley@google.com> +Reviewed-by: Roland Shoemaker <bracewell@google.com> +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2174344 +Reviewed-by: Carlos Amedee <amedee@google.com> +Reviewed-on: https://go-review.googlesource.com/c/go/+/569236 +Reviewed-by: Carlos Amedee <carlos@golang.org> +LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> +Auto-Submit: Michael Knyszek <mknyszek@google.com> + +Upstream-Status: Backport [https://github.com/golang/go/commit/3a855208e3efed2e9d7c20ad023f1fa78afcc0be] +CVE: CVE-2023-45289 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> +--- + src/net/http/client.go | 6 ++++++ + src/net/http/client_test.go | 1 + + src/net/http/cookiejar/jar.go | 7 +++++++ + src/net/http/cookiejar/jar_test.go | 10 ++++++++++ + 4 files changed, 24 insertions(+) + +diff --git a/src/net/http/client.go b/src/net/http/client.go +index 22db96b..b2dd445 100644 +--- a/src/net/http/client.go ++++ b/src/net/http/client.go +@@ -1015,6 +1015,12 @@ func isDomainOrSubdomain(sub, parent string) bool { + if sub == parent { + return true + } ++ // If sub contains a :, it's probably an IPv6 address (and is definitely not a hostname). ++ // Don't check the suffix in this case, to avoid matching the contents of a IPv6 zone. ++ // For example, "::1%.www.example.com" is not a subdomain of "www.example.com". ++ if strings.ContainsAny(sub, ":%") { ++ return false ++ } + // If sub is "foo.example.com" and parent is "example.com", + // that means sub must end in "."+parent. + // Do it without allocating. +diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go +index 9788c7a..7a0aa53 100644 +--- a/src/net/http/client_test.go ++++ b/src/net/http/client_test.go +@@ -1729,6 +1729,7 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) { + {"cookie2", "http://foo.com/", "http://bar.com/", false}, + {"authorization", "http://foo.com/", "http://bar.com/", false}, + {"www-authenticate", "http://foo.com/", "http://bar.com/", false}, ++ {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false}, + + // But subdomains should work: + {"www-authenticate", "http://foo.com/", "http://foo.com/", true}, +diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go +index e6583da..f2cf9c2 100644 +--- a/src/net/http/cookiejar/jar.go ++++ b/src/net/http/cookiejar/jar.go +@@ -362,6 +362,13 @@ func jarKey(host string, psl PublicSuffixList) string { + + // isIP reports whether host is an IP address. + func isIP(host string) bool { ++ if strings.ContainsAny(host, ":%") { ++ // Probable IPv6 address. ++ // Hostnames can't contain : or %, so this is definitely not a valid host. ++ // Treating it as an IP is the more conservative option, and avoids the risk ++ // of interpeting ::1%.www.example.com as a subtomain of www.example.com. ++ return true ++ } + return net.ParseIP(host) != nil + } + +diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go +index 47fb1ab..fd8d40e 100644 +--- a/src/net/http/cookiejar/jar_test.go ++++ b/src/net/http/cookiejar/jar_test.go +@@ -251,6 +251,7 @@ var isIPTests = map[string]bool{ + "127.0.0.1": true, + "1.2.3.4": true, + "2001:4860:0:2001::68": true, ++ "::1%zone": true, + "example.com": false, + "1.1.1.300": false, + "www.foo.bar.net": false, +@@ -613,6 +614,15 @@ var basicsTests = [...]jarTest{ + {"http://www.host.test:1234/", "a=1"}, + }, + }, ++ { ++ "IPv6 zone is not treated as a host.", ++ "https://example.com/", ++ []string{"a=1"}, ++ "a=1", ++ []query{ ++ {"https://[::1%25.example.com]:80/", ""}, ++ }, ++ }, + } + + func TestBasics(t *testing.T) { +-- +2.25.1 + diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2023-45290.patch b/meta/recipes-devtools/go/go-1.21/CVE-2023-45290.patch new file mode 100644 index 0000000000..81f2123f34 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2023-45290.patch @@ -0,0 +1,270 @@ +From 041a47712e765e94f86d841c3110c840e76d8f82 Mon Sep 17 00:00:00 2001 +From: Damien Neil <dneil@google.com> +Date: Tue, 16 Jan 2024 15:37:52 -0800 +Subject: [PATCH] [release-branch.go1.22] net/textproto, mime/multipart: avoid + unbounded read in MIME header + +mime/multipart.Reader.ReadForm allows specifying the maximum amount +of memory that will be consumed by the form. While this limit is +correctly applied to the parsed form data structure, it was not +being applied to individual header lines in a form. + +For example, when presented with a form containing a header line +that never ends, ReadForm will continue to read the line until it +runs out of memory. + +Limit the amount of data consumed when reading a header. + +Fixes CVE-2023-45290 +Fixes #65850 +For #65383 + +Change-Id: I7f9264d25752009e95f6b2c80e3d76aaf321d658 +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2134435 +Reviewed-by: Roland Shoemaker <bracewell@google.com> +Reviewed-by: Tatiana Bradley <tatianabradley@google.com> +Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2174345 +Reviewed-by: Carlos Amedee <amedee@google.com> +Reviewed-on: https://go-review.googlesource.com/c/go/+/569237 +Reviewed-by: Carlos Amedee <carlos@golang.org> +LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> +Auto-Submit: Michael Knyszek <mknyszek@google.com> + +Upstream-Status: Backport [https://github.com/golang/go/commit/041a47712e765e94f86d841c3110c840e76d8f82] +CVE: CVE-2023-45290 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>--- + src/mime/multipart/formdata_test.go | 42 +++++++++++++++++++++++++ + src/net/textproto/reader.go | 48 ++++++++++++++++++++--------- + src/net/textproto/reader_test.go | 12 ++++++++ + 3 files changed, 87 insertions(+), 15 deletions(-) + +diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go +index c78eeb7..f729da6 100644 +--- a/src/mime/multipart/formdata_test.go ++++ b/src/mime/multipart/formdata_test.go +@@ -421,6 +421,48 @@ func TestReadFormLimits(t *testing.T) { + } + } + ++func TestReadFormEndlessHeaderLine(t *testing.T) { ++ for _, test := range []struct { ++ name string ++ prefix string ++ }{{ ++ name: "name", ++ prefix: "X-", ++ }, { ++ name: "value", ++ prefix: "X-Header: ", ++ }, { ++ name: "continuation", ++ prefix: "X-Header: foo\r\n ", ++ }} { ++ t.Run(test.name, func(t *testing.T) { ++ const eol = "\r\n" ++ s := `--boundary` + eol ++ s += `Content-Disposition: form-data; name="a"` + eol ++ s += `Content-Type: text/plain` + eol ++ s += test.prefix ++ fr := io.MultiReader( ++ strings.NewReader(s), ++ neverendingReader('X'), ++ ) ++ r := NewReader(fr, "boundary") ++ _, err := r.ReadForm(1 << 20) ++ if err != ErrMessageTooLarge { ++ t.Fatalf("ReadForm(1 << 20): %v, want ErrMessageTooLarge", err) ++ } ++ }) ++ } ++} ++ ++type neverendingReader byte ++ ++func (r neverendingReader) Read(p []byte) (n int, err error) { ++ for i := range p { ++ p[i] = byte(r) ++ } ++ return len(p), nil ++} ++ + func BenchmarkReadForm(b *testing.B) { + for _, test := range []struct { + name string +diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go +index c6569c8..3ac4d4d 100644 +--- a/src/net/textproto/reader.go ++++ b/src/net/textproto/reader.go +@@ -16,6 +16,10 @@ import ( + "sync" + ) + ++// TODO: This should be a distinguishable error (ErrMessageTooLarge) ++// to allow mime/multipart to detect it. ++var errMessageTooLarge = errors.New("message too large") ++ + // A Reader implements convenience methods for reading requests + // or responses from a text protocol network connection. + type Reader struct { +@@ -37,13 +41,13 @@ func NewReader(r *bufio.Reader) *Reader { + // ReadLine reads a single line from r, + // eliding the final \n or \r\n from the returned string. + func (r *Reader) ReadLine() (string, error) { +- line, err := r.readLineSlice() ++ line, err := r.readLineSlice(-1) + return string(line), err + } + + // ReadLineBytes is like ReadLine but returns a []byte instead of a string. + func (r *Reader) ReadLineBytes() ([]byte, error) { +- line, err := r.readLineSlice() ++ line, err := r.readLineSlice(-1) + if line != nil { + buf := make([]byte, len(line)) + copy(buf, line) +@@ -52,7 +56,10 @@ func (r *Reader) ReadLineBytes() ([]byte, error) { + return line, err + } + +-func (r *Reader) readLineSlice() ([]byte, error) { ++// readLineSlice reads a single line from r, ++// up to lim bytes long (or unlimited if lim is less than 0), ++// eliding the final \r or \r\n from the returned string. ++func (r *Reader) readLineSlice(lim int64) ([]byte, error) { + r.closeDot() + var line []byte + for { +@@ -60,6 +67,9 @@ func (r *Reader) readLineSlice() ([]byte, error) { + if err != nil { + return nil, err + } ++ if lim >= 0 && int64(len(line))+int64(len(l)) > lim { ++ return nil, errMessageTooLarge ++ } + // Avoid the copy if the first call produced a full line. + if line == nil && !more { + return l, nil +@@ -92,7 +102,7 @@ func (r *Reader) readLineSlice() ([]byte, error) { + // Empty lines are never continued. + // + func (r *Reader) ReadContinuedLine() (string, error) { +- line, err := r.readContinuedLineSlice(noValidation) ++ line, err := r.readContinuedLineSlice(-1, noValidation) + return string(line), err + } + +@@ -113,7 +123,7 @@ func trim(s []byte) []byte { + // ReadContinuedLineBytes is like ReadContinuedLine but + // returns a []byte instead of a string. + func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { +- line, err := r.readContinuedLineSlice(noValidation) ++ line, err := r.readContinuedLineSlice(-1, noValidation) + if line != nil { + buf := make([]byte, len(line)) + copy(buf, line) +@@ -126,13 +136,14 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { + // returning a byte slice with all lines. The validateFirstLine function + // is run on the first read line, and if it returns an error then this + // error is returned from readContinuedLineSlice. +-func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) { ++// It reads up to lim bytes of data (or unlimited if lim is less than 0). ++func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) { + if validateFirstLine == nil { + return nil, fmt.Errorf("missing validateFirstLine func") + } + + // Read the first line. +- line, err := r.readLineSlice() ++ line, err := r.readLineSlice(lim) + if err != nil { + return nil, err + } +@@ -160,13 +171,21 @@ func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([ + // copy the slice into buf. + r.buf = append(r.buf[:0], trim(line)...) + ++ if lim < 0 { ++ lim = math.MaxInt64 ++ } ++ lim -= int64(len(r.buf)) ++ + // Read continuation lines. + for r.skipSpace() > 0 { +- line, err := r.readLineSlice() ++ r.buf = append(r.buf, ' ') ++ if int64(len(r.buf)) >= lim { ++ return nil, errMessageTooLarge ++ } ++ line, err := r.readLineSlice(lim - int64(len(r.buf))) + if err != nil { + break + } +- r.buf = append(r.buf, ' ') + r.buf = append(r.buf, trim(line)...) + } + return r.buf, nil +@@ -511,7 +530,8 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) + + // The first line cannot start with a leading space. + if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { +- line, err := r.readLineSlice() ++ const errorLimit = 80 // arbitrary limit on how much of the line we'll quote ++ line, err := r.readLineSlice(errorLimit) + if err != nil { + return m, err + } +@@ -519,7 +539,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) + } + + for { +- kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon) ++ kv, err := r.readContinuedLineSlice(maxMemory, mustHaveFieldNameColon) + if len(kv) == 0 { + return m, err + } +@@ -540,7 +560,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) + + maxHeaders-- + if maxHeaders < 0 { +- return nil, errors.New("message too large") ++ return nil, errMessageTooLarge + } + + // backport 5c55ac9bf1e5f779220294c843526536605f42ab +@@ -567,9 +587,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) + } + maxMemory -= int64(len(value)) + if maxMemory < 0 { +- // TODO: This should be a distinguishable error (ErrMessageTooLarge) +- // to allow mime/multipart to detect it. +- return m, errors.New("message too large") ++ return m, errMessageTooLarge + } + if vv == nil && len(strs) > 0 { + // More than likely this will be a single-element key. +diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go +index 3ae0de1..db1ed91 100644 +--- a/src/net/textproto/reader_test.go ++++ b/src/net/textproto/reader_test.go +@@ -34,6 +34,18 @@ func TestReadLine(t *testing.T) { + } + } + ++func TestReadLineLongLine(t *testing.T) { ++ line := strings.Repeat("12345", 10000) ++ r := reader(line + "\r\n") ++ s, err := r.ReadLine() ++ if err != nil { ++ t.Fatalf("Line 1: %v", err) ++ } ++ if s != line { ++ t.Fatalf("%v-byte line does not match expected %v-byte line", len(s), len(line)) ++ } ++} ++ + func TestReadContinuedLine(t *testing.T) { + r := reader("line1\nline\n 2\nline3\n") + s, err := r.ReadContinuedLine() +-- +2.25.1 + |