diff options
author | Vijay Anusuri <vanusuri@mvista.com> | 2024-03-27 08:49:54 +0530 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2024-04-05 06:34:42 -0700 |
commit | 869db167b1c71e036a70565dc73cef6b002d6d22 (patch) | |
tree | beddf64378f6a00277f36d517b674febb0f69032 | |
parent | e555aefeef5e87927560aa941bb937511425f7a6 (diff) | |
download | poky-869db167b1c71e036a70565dc73cef6b002d6d22.tar.gz |
go: Fix for CVE-2023-45289 CVE-2023-45290 & CVE-2024-24785
Upstream-Status: Backport
[https://github.com/golang/go/commit/20586c0dbe03d144f914155f879fa5ee287591a1
&
https://github.com/golang/go/commit/bf80213b121074f4ad9b449410a4d13bae5e9be0
&
https://github.com/golang/go/commit/3643147a29352ca2894fd5d0d2069bc4b4335a7e]
(From OE-Core rev: 2bc50dccff15b9c4ad815092ef20caa3ef06864c)
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
Signed-off-by: Steve Sakoman <steve@sakoman.com>
-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 | |||