diff options
| -rw-r--r-- | meta/recipes-devtools/go/go-1.14.inc | 3 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch | 121 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch | 271 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch | 197 |
4 files changed, 592 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.14.inc b/meta/recipes-devtools/go/go-1.14.inc index 4fbf9d7590..69b65f3eb2 100644 --- a/meta/recipes-devtools/go/go-1.14.inc +++ b/meta/recipes-devtools/go/go-1.14.inc | |||
| @@ -88,6 +88,9 @@ SRC_URI += "\ | |||
| 88 | file://CVE-2023-45287-pre2.patch \ | 88 | file://CVE-2023-45287-pre2.patch \ |
| 89 | file://CVE-2023-45287-pre3.patch \ | 89 | file://CVE-2023-45287-pre3.patch \ |
| 90 | file://CVE-2023-45287.patch \ | 90 | file://CVE-2023-45287.patch \ |
| 91 | file://CVE-2023-45289.patch \ | ||
| 92 | file://CVE-2023-45290.patch \ | ||
| 93 | file://CVE-2024-24785.patch \ | ||
| 91 | " | 94 | " |
| 92 | 95 | ||
| 93 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" | 96 | SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" |
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch new file mode 100644 index 0000000000..13d3510504 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45289.patch | |||
| @@ -0,0 +1,121 @@ | |||
| 1 | From 20586c0dbe03d144f914155f879fa5ee287591a1 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Damien Neil <dneil@google.com> | ||
| 3 | Date: Thu, 11 Jan 2024 11:31:57 -0800 | ||
| 4 | Subject: [PATCH] [release-branch.go1.21] net/http, net/http/cookiejar: avoid | ||
| 5 | subdomain matches on IPv6 zones | ||
| 6 | |||
| 7 | When deciding whether to forward cookies or sensitive headers | ||
| 8 | across a redirect, do not attempt to interpret an IPv6 address | ||
| 9 | as a domain name. | ||
| 10 | |||
| 11 | Avoids a case where a maliciously-crafted redirect to an | ||
| 12 | IPv6 address with a scoped addressing zone could be | ||
| 13 | misinterpreted as a within-domain redirect. For example, | ||
| 14 | we could interpret "::1%.www.example.com" as a subdomain | ||
| 15 | of "www.example.com". | ||
| 16 | |||
| 17 | Thanks to Juho Nurminen of Mattermost for reporting this issue. | ||
| 18 | |||
| 19 | Fixes CVE-2023-45289 | ||
| 20 | Fixes #65385 | ||
| 21 | For #65065 | ||
| 22 | |||
| 23 | Change-Id: I8f463f59f0e700c8a18733d2b264a8bcb3a19599 | ||
| 24 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2131938 | ||
| 25 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
| 26 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
| 27 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173775 | ||
| 28 | Reviewed-by: Carlos Amedee <amedee@google.com> | ||
| 29 | Reviewed-on: https://go-review.googlesource.com/c/go/+/569239 | ||
| 30 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
| 31 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
| 32 | TryBot-Bypass: Michael Knyszek <mknyszek@google.com> | ||
| 33 | |||
| 34 | Upstream-Status: Backport [https://github.com/golang/go/commit/20586c0dbe03d144f914155f879fa5ee287591a1] | ||
| 35 | CVE: CVE-2023-45289 | ||
| 36 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 37 | --- | ||
| 38 | src/net/http/client.go | 6 ++++++ | ||
| 39 | src/net/http/client_test.go | 1 + | ||
| 40 | src/net/http/cookiejar/jar.go | 7 +++++++ | ||
| 41 | src/net/http/cookiejar/jar_test.go | 10 ++++++++++ | ||
| 42 | 4 files changed, 24 insertions(+) | ||
| 43 | |||
| 44 | diff --git a/src/net/http/client.go b/src/net/http/client.go | ||
| 45 | index a496f1c..2031834 100644 | ||
| 46 | --- a/src/net/http/client.go | ||
| 47 | +++ b/src/net/http/client.go | ||
| 48 | @@ -973,6 +973,12 @@ func isDomainOrSubdomain(sub, parent string) bool { | ||
| 49 | if sub == parent { | ||
| 50 | return true | ||
| 51 | } | ||
| 52 | + // If sub contains a :, it's probably an IPv6 address (and is definitely not a hostname). | ||
| 53 | + // Don't check the suffix in this case, to avoid matching the contents of a IPv6 zone. | ||
| 54 | + // For example, "::1%.www.example.com" is not a subdomain of "www.example.com". | ||
| 55 | + if strings.ContainsAny(sub, ":%") { | ||
| 56 | + return false | ||
| 57 | + } | ||
| 58 | // If sub is "foo.example.com" and parent is "example.com", | ||
| 59 | // that means sub must end in "."+parent. | ||
| 60 | // Do it without allocating. | ||
| 61 | diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go | ||
| 62 | index 2b4f53f..442fe35 100644 | ||
| 63 | --- a/src/net/http/client_test.go | ||
| 64 | +++ b/src/net/http/client_test.go | ||
| 65 | @@ -1703,6 +1703,7 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) { | ||
| 66 | {"cookie2", "http://foo.com/", "http://bar.com/", false}, | ||
| 67 | {"authorization", "http://foo.com/", "http://bar.com/", false}, | ||
| 68 | {"www-authenticate", "http://foo.com/", "http://bar.com/", false}, | ||
| 69 | + {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false}, | ||
| 70 | |||
| 71 | // But subdomains should work: | ||
| 72 | {"www-authenticate", "http://foo.com/", "http://foo.com/", true}, | ||
| 73 | diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go | ||
| 74 | index 9f19917..18cbfc2 100644 | ||
| 75 | --- a/src/net/http/cookiejar/jar.go | ||
| 76 | +++ b/src/net/http/cookiejar/jar.go | ||
| 77 | @@ -356,6 +356,13 @@ func jarKey(host string, psl PublicSuffixList) string { | ||
| 78 | |||
| 79 | // isIP reports whether host is an IP address. | ||
| 80 | func isIP(host string) bool { | ||
| 81 | + if strings.ContainsAny(host, ":%") { | ||
| 82 | + // Probable IPv6 address. | ||
| 83 | + // Hostnames can't contain : or %, so this is definitely not a valid host. | ||
| 84 | + // Treating it as an IP is the more conservative option, and avoids the risk | ||
| 85 | + // of interpeting ::1%.www.example.com as a subtomain of www.example.com. | ||
| 86 | + return true | ||
| 87 | + } | ||
| 88 | return net.ParseIP(host) != nil | ||
| 89 | } | ||
| 90 | |||
| 91 | diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go | ||
| 92 | index 47fb1ab..fd8d40e 100644 | ||
| 93 | --- a/src/net/http/cookiejar/jar_test.go | ||
| 94 | +++ b/src/net/http/cookiejar/jar_test.go | ||
| 95 | @@ -251,6 +251,7 @@ var isIPTests = map[string]bool{ | ||
| 96 | "127.0.0.1": true, | ||
| 97 | "1.2.3.4": true, | ||
| 98 | "2001:4860:0:2001::68": true, | ||
| 99 | + "::1%zone": true, | ||
| 100 | "example.com": false, | ||
| 101 | "1.1.1.300": false, | ||
| 102 | "www.foo.bar.net": false, | ||
| 103 | @@ -613,6 +614,15 @@ var basicsTests = [...]jarTest{ | ||
| 104 | {"http://www.host.test:1234/", "a=1"}, | ||
| 105 | }, | ||
| 106 | }, | ||
| 107 | + { | ||
| 108 | + "IPv6 zone is not treated as a host.", | ||
| 109 | + "https://example.com/", | ||
| 110 | + []string{"a=1"}, | ||
| 111 | + "a=1", | ||
| 112 | + []query{ | ||
| 113 | + {"https://[::1%25.example.com]:80/", ""}, | ||
| 114 | + }, | ||
| 115 | + }, | ||
| 116 | } | ||
| 117 | |||
| 118 | func TestBasics(t *testing.T) { | ||
| 119 | -- | ||
| 120 | 2.25.1 | ||
| 121 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch b/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch new file mode 100644 index 0000000000..ddc2f67c96 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2023-45290.patch | |||
| @@ -0,0 +1,271 @@ | |||
| 1 | From bf80213b121074f4ad9b449410a4d13bae5e9be0 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Damien Neil <dneil@google.com> | ||
| 3 | Date: Tue, 16 Jan 2024 15:37:52 -0800 | ||
| 4 | Subject: [PATCH] [release-branch.go1.21] net/textproto, mime/multipart: avoid | ||
| 5 | unbounded read in MIME header | ||
| 6 | |||
| 7 | mime/multipart.Reader.ReadForm allows specifying the maximum amount | ||
| 8 | of memory that will be consumed by the form. While this limit is | ||
| 9 | correctly applied to the parsed form data structure, it was not | ||
| 10 | being applied to individual header lines in a form. | ||
| 11 | |||
| 12 | For example, when presented with a form containing a header line | ||
| 13 | that never ends, ReadForm will continue to read the line until it | ||
| 14 | runs out of memory. | ||
| 15 | |||
| 16 | Limit the amount of data consumed when reading a header. | ||
| 17 | |||
| 18 | Fixes CVE-2023-45290 | ||
| 19 | Fixes #65389 | ||
| 20 | For #65383 | ||
| 21 | |||
| 22 | Change-Id: I7f9264d25752009e95f6b2c80e3d76aaf321d658 | ||
| 23 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2134435 | ||
| 24 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
| 25 | Reviewed-by: Tatiana Bradley <tatianabradley@google.com> | ||
| 26 | Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173776 | ||
| 27 | Reviewed-by: Carlos Amedee <amedee@google.com> | ||
| 28 | Reviewed-on: https://go-review.googlesource.com/c/go/+/569240 | ||
| 29 | Auto-Submit: Michael Knyszek <mknyszek@google.com> | ||
| 30 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
| 31 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
| 32 | |||
| 33 | Upstream-Status: Backport [https://github.com/golang/go/commit/bf80213b121074f4ad9b449410a4d13bae5e9be0] | ||
| 34 | CVE: CVE-2023-45290 | ||
| 35 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 36 | --- | ||
| 37 | src/mime/multipart/formdata_test.go | 42 +++++++++++++++++++++++++ | ||
| 38 | src/net/textproto/reader.go | 48 ++++++++++++++++++++--------- | ||
| 39 | src/net/textproto/reader_test.go | 12 ++++++++ | ||
| 40 | 3 files changed, 87 insertions(+), 15 deletions(-) | ||
| 41 | |||
| 42 | diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go | ||
| 43 | index c78eeb7..f729da6 100644 | ||
| 44 | --- a/src/mime/multipart/formdata_test.go | ||
| 45 | +++ b/src/mime/multipart/formdata_test.go | ||
| 46 | @@ -421,6 +421,48 @@ func TestReadFormLimits(t *testing.T) { | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | +func TestReadFormEndlessHeaderLine(t *testing.T) { | ||
| 51 | + for _, test := range []struct { | ||
| 52 | + name string | ||
| 53 | + prefix string | ||
| 54 | + }{{ | ||
| 55 | + name: "name", | ||
| 56 | + prefix: "X-", | ||
| 57 | + }, { | ||
| 58 | + name: "value", | ||
| 59 | + prefix: "X-Header: ", | ||
| 60 | + }, { | ||
| 61 | + name: "continuation", | ||
| 62 | + prefix: "X-Header: foo\r\n ", | ||
| 63 | + }} { | ||
| 64 | + t.Run(test.name, func(t *testing.T) { | ||
| 65 | + const eol = "\r\n" | ||
| 66 | + s := `--boundary` + eol | ||
| 67 | + s += `Content-Disposition: form-data; name="a"` + eol | ||
| 68 | + s += `Content-Type: text/plain` + eol | ||
| 69 | + s += test.prefix | ||
| 70 | + fr := io.MultiReader( | ||
| 71 | + strings.NewReader(s), | ||
| 72 | + neverendingReader('X'), | ||
| 73 | + ) | ||
| 74 | + r := NewReader(fr, "boundary") | ||
| 75 | + _, err := r.ReadForm(1 << 20) | ||
| 76 | + if err != ErrMessageTooLarge { | ||
| 77 | + t.Fatalf("ReadForm(1 << 20): %v, want ErrMessageTooLarge", err) | ||
| 78 | + } | ||
| 79 | + }) | ||
| 80 | + } | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +type neverendingReader byte | ||
| 84 | + | ||
| 85 | +func (r neverendingReader) Read(p []byte) (n int, err error) { | ||
| 86 | + for i := range p { | ||
| 87 | + p[i] = byte(r) | ||
| 88 | + } | ||
| 89 | + return len(p), nil | ||
| 90 | +} | ||
| 91 | + | ||
| 92 | func BenchmarkReadForm(b *testing.B) { | ||
| 93 | for _, test := range []struct { | ||
| 94 | name string | ||
| 95 | diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go | ||
| 96 | index ad2d777..cea6613 100644 | ||
| 97 | --- a/src/net/textproto/reader.go | ||
| 98 | +++ b/src/net/textproto/reader.go | ||
| 99 | @@ -17,6 +17,10 @@ import ( | ||
| 100 | "sync" | ||
| 101 | ) | ||
| 102 | |||
| 103 | +// TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
| 104 | +// to allow mime/multipart to detect it. | ||
| 105 | +var errMessageTooLarge = errors.New("message too large") | ||
| 106 | + | ||
| 107 | // A Reader implements convenience methods for reading requests | ||
| 108 | // or responses from a text protocol network connection. | ||
| 109 | type Reader struct { | ||
| 110 | @@ -38,13 +42,13 @@ func NewReader(r *bufio.Reader) *Reader { | ||
| 111 | // ReadLine reads a single line from r, | ||
| 112 | // eliding the final \n or \r\n from the returned string. | ||
| 113 | func (r *Reader) ReadLine() (string, error) { | ||
| 114 | - line, err := r.readLineSlice() | ||
| 115 | + line, err := r.readLineSlice(-1) | ||
| 116 | return string(line), err | ||
| 117 | } | ||
| 118 | |||
| 119 | // ReadLineBytes is like ReadLine but returns a []byte instead of a string. | ||
| 120 | func (r *Reader) ReadLineBytes() ([]byte, error) { | ||
| 121 | - line, err := r.readLineSlice() | ||
| 122 | + line, err := r.readLineSlice(-1) | ||
| 123 | if line != nil { | ||
| 124 | buf := make([]byte, len(line)) | ||
| 125 | copy(buf, line) | ||
| 126 | @@ -53,7 +57,10 @@ func (r *Reader) ReadLineBytes() ([]byte, error) { | ||
| 127 | return line, err | ||
| 128 | } | ||
| 129 | |||
| 130 | -func (r *Reader) readLineSlice() ([]byte, error) { | ||
| 131 | +// readLineSlice reads a single line from r, | ||
| 132 | +// up to lim bytes long (or unlimited if lim is less than 0), | ||
| 133 | +// eliding the final \r or \r\n from the returned string. | ||
| 134 | +func (r *Reader) readLineSlice(lim int64) ([]byte, error) { | ||
| 135 | r.closeDot() | ||
| 136 | var line []byte | ||
| 137 | for { | ||
| 138 | @@ -61,6 +68,9 @@ func (r *Reader) readLineSlice() ([]byte, error) { | ||
| 139 | if err != nil { | ||
| 140 | return nil, err | ||
| 141 | } | ||
| 142 | + if lim >= 0 && int64(len(line))+int64(len(l)) > lim { | ||
| 143 | + return nil, errMessageTooLarge | ||
| 144 | + } | ||
| 145 | // Avoid the copy if the first call produced a full line. | ||
| 146 | if line == nil && !more { | ||
| 147 | return l, nil | ||
| 148 | @@ -93,7 +103,7 @@ func (r *Reader) readLineSlice() ([]byte, error) { | ||
| 149 | // A line consisting of only white space is never continued. | ||
| 150 | // | ||
| 151 | func (r *Reader) ReadContinuedLine() (string, error) { | ||
| 152 | - line, err := r.readContinuedLineSlice(noValidation) | ||
| 153 | + line, err := r.readContinuedLineSlice(-1, noValidation) | ||
| 154 | return string(line), err | ||
| 155 | } | ||
| 156 | |||
| 157 | @@ -114,7 +124,7 @@ func trim(s []byte) []byte { | ||
| 158 | // ReadContinuedLineBytes is like ReadContinuedLine but | ||
| 159 | // returns a []byte instead of a string. | ||
| 160 | func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { | ||
| 161 | - line, err := r.readContinuedLineSlice(noValidation) | ||
| 162 | + line, err := r.readContinuedLineSlice(-1, noValidation) | ||
| 163 | if line != nil { | ||
| 164 | buf := make([]byte, len(line)) | ||
| 165 | copy(buf, line) | ||
| 166 | @@ -127,13 +137,14 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { | ||
| 167 | // returning a byte slice with all lines. The validateFirstLine function | ||
| 168 | // is run on the first read line, and if it returns an error then this | ||
| 169 | // error is returned from readContinuedLineSlice. | ||
| 170 | -func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) { | ||
| 171 | +// It reads up to lim bytes of data (or unlimited if lim is less than 0). | ||
| 172 | +func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) { | ||
| 173 | if validateFirstLine == nil { | ||
| 174 | return nil, fmt.Errorf("missing validateFirstLine func") | ||
| 175 | } | ||
| 176 | |||
| 177 | // Read the first line. | ||
| 178 | - line, err := r.readLineSlice() | ||
| 179 | + line, err := r.readLineSlice(lim) | ||
| 180 | if err != nil { | ||
| 181 | return nil, err | ||
| 182 | } | ||
| 183 | @@ -161,13 +172,21 @@ func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([ | ||
| 184 | // copy the slice into buf. | ||
| 185 | r.buf = append(r.buf[:0], trim(line)...) | ||
| 186 | |||
| 187 | + if lim < 0 { | ||
| 188 | + lim = math.MaxInt64 | ||
| 189 | + } | ||
| 190 | + lim -= int64(len(r.buf)) | ||
| 191 | + | ||
| 192 | // Read continuation lines. | ||
| 193 | for r.skipSpace() > 0 { | ||
| 194 | - line, err := r.readLineSlice() | ||
| 195 | + r.buf = append(r.buf, ' ') | ||
| 196 | + if int64(len(r.buf)) >= lim { | ||
| 197 | + return nil, errMessageTooLarge | ||
| 198 | + } | ||
| 199 | + line, err := r.readLineSlice(lim - int64(len(r.buf))) | ||
| 200 | if err != nil { | ||
| 201 | break | ||
| 202 | } | ||
| 203 | - r.buf = append(r.buf, ' ') | ||
| 204 | r.buf = append(r.buf, trim(line)...) | ||
| 205 | } | ||
| 206 | return r.buf, nil | ||
| 207 | @@ -512,7 +531,8 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
| 208 | |||
| 209 | // The first line cannot start with a leading space. | ||
| 210 | if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') { | ||
| 211 | - line, err := r.readLineSlice() | ||
| 212 | + const errorLimit = 80 // arbitrary limit on how much of the line we'll quote | ||
| 213 | + line, err := r.readLineSlice(errorLimit) | ||
| 214 | if err != nil { | ||
| 215 | return m, err | ||
| 216 | } | ||
| 217 | @@ -520,7 +540,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
| 218 | } | ||
| 219 | |||
| 220 | for { | ||
| 221 | - kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon) | ||
| 222 | + kv, err := r.readContinuedLineSlice(maxMemory, mustHaveFieldNameColon) | ||
| 223 | if len(kv) == 0 { | ||
| 224 | return m, err | ||
| 225 | } | ||
| 226 | @@ -541,7 +561,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
| 227 | |||
| 228 | maxHeaders-- | ||
| 229 | if maxHeaders < 0 { | ||
| 230 | - return nil, errors.New("message too large") | ||
| 231 | + return nil, errMessageTooLarge | ||
| 232 | } | ||
| 233 | |||
| 234 | // backport 5c55ac9bf1e5f779220294c843526536605f42ab | ||
| 235 | @@ -567,9 +587,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) | ||
| 236 | } | ||
| 237 | maxMemory -= int64(len(value)) | ||
| 238 | if maxMemory < 0 { | ||
| 239 | - // TODO: This should be a distinguishable error (ErrMessageTooLarge) | ||
| 240 | - // to allow mime/multipart to detect it. | ||
| 241 | - return m, errors.New("message too large") | ||
| 242 | + return m, errMessageTooLarge | ||
| 243 | } | ||
| 244 | if vv == nil && len(strs) > 0 { | ||
| 245 | // More than likely this will be a single-element key. | ||
| 246 | diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go | ||
| 247 | index 3ae0de1..db1ed91 100644 | ||
| 248 | --- a/src/net/textproto/reader_test.go | ||
| 249 | +++ b/src/net/textproto/reader_test.go | ||
| 250 | @@ -34,6 +34,18 @@ func TestReadLine(t *testing.T) { | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | +func TestReadLineLongLine(t *testing.T) { | ||
| 255 | + line := strings.Repeat("12345", 10000) | ||
| 256 | + r := reader(line + "\r\n") | ||
| 257 | + s, err := r.ReadLine() | ||
| 258 | + if err != nil { | ||
| 259 | + t.Fatalf("Line 1: %v", err) | ||
| 260 | + } | ||
| 261 | + if s != line { | ||
| 262 | + t.Fatalf("%v-byte line does not match expected %v-byte line", len(s), len(line)) | ||
| 263 | + } | ||
| 264 | +} | ||
| 265 | + | ||
| 266 | func TestReadContinuedLine(t *testing.T) { | ||
| 267 | r := reader("line1\nline\n 2\nline3\n") | ||
| 268 | s, err := r.ReadContinuedLine() | ||
| 269 | -- | ||
| 270 | 2.25.1 | ||
| 271 | |||
diff --git a/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch b/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch new file mode 100644 index 0000000000..1398a2ca48 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.14/CVE-2024-24785.patch | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | From 3643147a29352ca2894fd5d0d2069bc4b4335a7e Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Roland Shoemaker <roland@golang.org> | ||
| 3 | Date: Wed, 14 Feb 2024 17:18:36 -0800 | ||
| 4 | Subject: [PATCH] [release-branch.go1.21] html/template: escape additional | ||
| 5 | tokens in MarshalJSON errors | ||
| 6 | |||
| 7 | Escape "</script" and "<!--" in errors returned from MarshalJSON errors | ||
| 8 | when attempting to marshal types in script blocks. This prevents any | ||
| 9 | user controlled content from prematurely terminating the script block. | ||
| 10 | |||
| 11 | Updates #65697 | ||
| 12 | Fixes #65968 | ||
| 13 | |||
| 14 | Change-Id: Icf0e26c54ea7d9c1deed0bff11b6506c99ddef1b | ||
| 15 | Reviewed-on: https://go-review.googlesource.com/c/go/+/564196 | ||
| 16 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
| 17 | Reviewed-by: Damien Neil <dneil@google.com> | ||
| 18 | (cherry picked from commit ccbc725f2d678255df1bd326fa511a492aa3a0aa) | ||
| 19 | Reviewed-on: https://go-review.googlesource.com/c/go/+/567515 | ||
| 20 | Reviewed-by: Carlos Amedee <carlos@golang.org> | ||
| 21 | |||
| 22 | Upstream-Status: Backport [https://github.com/golang/go/commit/3643147a29352ca2894fd5d0d2069bc4b4335a7e] | ||
| 23 | CVE: CVE-2024-24785 | ||
| 24 | Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> | ||
| 25 | --- | ||
| 26 | src/html/template/js.go | 22 ++++++++- | ||
| 27 | src/html/template/js_test.go | 96 ++++++++++++++++++++---------------- | ||
| 28 | 2 files changed, 74 insertions(+), 44 deletions(-) | ||
| 29 | |||
| 30 | diff --git a/src/html/template/js.go b/src/html/template/js.go | ||
| 31 | index 35994f0..4d3b25d 100644 | ||
| 32 | --- a/src/html/template/js.go | ||
| 33 | +++ b/src/html/template/js.go | ||
| 34 | @@ -171,13 +171,31 @@ func jsValEscaper(args ...interface{}) string { | ||
| 35 | // cyclic data. This may be an unacceptable DoS risk. | ||
| 36 | b, err := json.Marshal(a) | ||
| 37 | if err != nil { | ||
| 38 | - // Put a space before comment so that if it is flush against | ||
| 39 | + // While the standard JSON marshaller does not include user controlled | ||
| 40 | + // information in the error message, if a type has a MarshalJSON method, | ||
| 41 | + // the content of the error message is not guaranteed. Since we insert | ||
| 42 | + // the error into the template, as part of a comment, we attempt to | ||
| 43 | + // prevent the error from either terminating the comment, or the script | ||
| 44 | + // block itself. | ||
| 45 | + // | ||
| 46 | + // In particular we: | ||
| 47 | + // * replace "*/" comment end tokens with "* /", which does not | ||
| 48 | + // terminate the comment | ||
| 49 | + // * replace "</script" with "\x3C/script", and "<!--" with | ||
| 50 | + // "\x3C!--", which prevents confusing script block termination | ||
| 51 | + // semantics | ||
| 52 | + // | ||
| 53 | + // We also put a space before the comment so that if it is flush against | ||
| 54 | // a division operator it is not turned into a line comment: | ||
| 55 | // x/{{y}} | ||
| 56 | // turning into | ||
| 57 | // x//* error marshaling y: | ||
| 58 | // second line of error message */null | ||
| 59 | - return fmt.Sprintf(" /* %s */null ", strings.ReplaceAll(err.Error(), "*/", "* /")) | ||
| 60 | + errStr := err.Error() | ||
| 61 | + errStr = strings.ReplaceAll(errStr, "*/", "* /") | ||
| 62 | + errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`) | ||
| 63 | + errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`) | ||
| 64 | + return fmt.Sprintf(" /* %s */null ", errStr) | ||
| 65 | } | ||
| 66 | |||
| 67 | // TODO: maybe post-process output to prevent it from containing | ||
| 68 | diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go | ||
| 69 | index de9ef28..3fc3baf 100644 | ||
| 70 | --- a/src/html/template/js_test.go | ||
| 71 | +++ b/src/html/template/js_test.go | ||
| 72 | @@ -5,6 +5,7 @@ | ||
| 73 | package template | ||
| 74 | |||
| 75 | import ( | ||
| 76 | + "errors" | ||
| 77 | "bytes" | ||
| 78 | "math" | ||
| 79 | "strings" | ||
| 80 | @@ -104,61 +105,72 @@ func TestNextJsCtx(t *testing.T) { | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | +type jsonErrType struct{} | ||
| 85 | + | ||
| 86 | +func (e *jsonErrType) MarshalJSON() ([]byte, error) { | ||
| 87 | + return nil, errors.New("beep */ boop </script blip <!--") | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | func TestJSValEscaper(t *testing.T) { | ||
| 91 | tests := []struct { | ||
| 92 | - x interface{} | ||
| 93 | - js string | ||
| 94 | + x interface{} | ||
| 95 | + js string | ||
| 96 | + skipNest bool | ||
| 97 | }{ | ||
| 98 | - {int(42), " 42 "}, | ||
| 99 | - {uint(42), " 42 "}, | ||
| 100 | - {int16(42), " 42 "}, | ||
| 101 | - {uint16(42), " 42 "}, | ||
| 102 | - {int32(-42), " -42 "}, | ||
| 103 | - {uint32(42), " 42 "}, | ||
| 104 | - {int16(-42), " -42 "}, | ||
| 105 | - {uint16(42), " 42 "}, | ||
| 106 | - {int64(-42), " -42 "}, | ||
| 107 | - {uint64(42), " 42 "}, | ||
| 108 | - {uint64(1) << 53, " 9007199254740992 "}, | ||
| 109 | + {int(42), " 42 ", false}, | ||
| 110 | + {uint(42), " 42 ", false}, | ||
| 111 | + {int16(42), " 42 ", false}, | ||
| 112 | + {uint16(42), " 42 ", false}, | ||
| 113 | + {int32(-42), " -42 ", false}, | ||
| 114 | + {uint32(42), " 42 ", false}, | ||
| 115 | + {int16(-42), " -42 ", false}, | ||
| 116 | + {uint16(42), " 42 ", false}, | ||
| 117 | + {int64(-42), " -42 ", false}, | ||
| 118 | + {uint64(42), " 42 ", false}, | ||
| 119 | + {uint64(1) << 53, " 9007199254740992 ", false}, | ||
| 120 | // ulp(1 << 53) > 1 so this loses precision in JS | ||
| 121 | // but it is still a representable integer literal. | ||
| 122 | - {uint64(1)<<53 + 1, " 9007199254740993 "}, | ||
| 123 | - {float32(1.0), " 1 "}, | ||
| 124 | - {float32(-1.0), " -1 "}, | ||
| 125 | - {float32(0.5), " 0.5 "}, | ||
| 126 | - {float32(-0.5), " -0.5 "}, | ||
| 127 | - {float32(1.0) / float32(256), " 0.00390625 "}, | ||
| 128 | - {float32(0), " 0 "}, | ||
| 129 | - {math.Copysign(0, -1), " -0 "}, | ||
| 130 | - {float64(1.0), " 1 "}, | ||
| 131 | - {float64(-1.0), " -1 "}, | ||
| 132 | - {float64(0.5), " 0.5 "}, | ||
| 133 | - {float64(-0.5), " -0.5 "}, | ||
| 134 | - {float64(0), " 0 "}, | ||
| 135 | - {math.Copysign(0, -1), " -0 "}, | ||
| 136 | - {"", `""`}, | ||
| 137 | - {"foo", `"foo"`}, | ||
| 138 | + {uint64(1)<<53 + 1, " 9007199254740993 ", false}, | ||
| 139 | + {float32(1.0), " 1 ", false}, | ||
| 140 | + {float32(-1.0), " -1 ", false}, | ||
| 141 | + {float32(0.5), " 0.5 ", false}, | ||
| 142 | + {float32(-0.5), " -0.5 ", false}, | ||
| 143 | + {float32(1.0) / float32(256), " 0.00390625 ", false}, | ||
| 144 | + {float32(0), " 0 ", false}, | ||
| 145 | + {math.Copysign(0, -1), " -0 ", false}, | ||
| 146 | + {float64(1.0), " 1 ", false}, | ||
| 147 | + {float64(-1.0), " -1 ", false}, | ||
| 148 | + {float64(0.5), " 0.5 ", false}, | ||
| 149 | + {float64(-0.5), " -0.5 ", false}, | ||
| 150 | + {float64(0), " 0 ", false}, | ||
| 151 | + {math.Copysign(0, -1), " -0 ", false}, | ||
| 152 | + {"", `""`, false}, | ||
| 153 | + {"foo", `"foo"`, false}, | ||
| 154 | // Newlines. | ||
| 155 | - {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`}, | ||
| 156 | + {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`, false}, | ||
| 157 | // "\v" == "v" on IE 6 so use "\u000b" instead. | ||
| 158 | - {"\t\x0b", `"\t\u000b"`}, | ||
| 159 | - {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`}, | ||
| 160 | - {[]interface{}{}, "[]"}, | ||
| 161 | - {[]interface{}{42, "foo", nil}, `[42,"foo",null]`}, | ||
| 162 | - {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`}, | ||
| 163 | - {"<!--", `"\u003c!--"`}, | ||
| 164 | - {"-->", `"--\u003e"`}, | ||
| 165 | - {"<![CDATA[", `"\u003c![CDATA["`}, | ||
| 166 | - {"]]>", `"]]\u003e"`}, | ||
| 167 | - {"</script", `"\u003c/script"`}, | ||
| 168 | - {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E" | ||
| 169 | - {nil, " null "}, | ||
| 170 | + {"\t\x0b", `"\t\u000b"`, false}, | ||
| 171 | + {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`, false}, | ||
| 172 | + {[]interface{}{}, "[]", false}, | ||
| 173 | + {[]interface{}{42, "foo", nil}, `[42,"foo",null]`, false}, | ||
| 174 | + {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`, false}, | ||
| 175 | + {"<!--", `"\u003c!--"`, false}, | ||
| 176 | + {"-->", `"--\u003e"`, false}, | ||
| 177 | + {"<![CDATA[", `"\u003c![CDATA["`, false}, | ||
| 178 | + {"]]>", `"]]\u003e"`, false}, | ||
| 179 | + {"</script", `"\u003c/script"`, false}, | ||
| 180 | + {"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E" | ||
| 181 | + {nil, " null ", false}, | ||
| 182 | + {&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true}, | ||
| 183 | } | ||
| 184 | |||
| 185 | for _, test := range tests { | ||
| 186 | if js := jsValEscaper(test.x); js != test.js { | ||
| 187 | t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js) | ||
| 188 | } | ||
| 189 | + if test.skipNest { | ||
| 190 | + continue | ||
| 191 | + } | ||
| 192 | // Make sure that escaping corner cases are not broken | ||
| 193 | // by nesting. | ||
| 194 | a := []interface{}{test.x} | ||
| 195 | -- | ||
| 196 | 2.25.1 | ||
| 197 | |||
