golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/task/announce_test.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package task 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "net/mail" 12 "os" 13 "path/filepath" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/google/go-cmp/cmp" 19 "golang.org/x/build/internal/workflow" 20 ) 21 22 // Test that the task doesn't start running if the provided 23 // context doesn't have sufficient time for the task to run. 24 func TestAnnounceReleaseShortContext(t *testing.T) { 25 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 26 defer cancel() 27 _, err := (AnnounceMailTasks{}).AnnounceRelease(&workflow.TaskContext{Context: ctx}, KindMinor, []Published{{Version: "go1.18.1"}, {Version: "go1.17.8"}}, nil, nil) 28 if err == nil { 29 t.Errorf("want non-nil error") 30 } else if !strings.HasPrefix(err.Error(), "insufficient time") { 31 t.Errorf("want error that starts with 'insufficient time' instead of: %s", err) 32 } 33 } 34 35 func TestAnnouncementMail(t *testing.T) { 36 tests := [...]struct { 37 name string 38 in any 39 wantSubject string 40 }{ 41 { 42 name: "announce-minor", 43 in: releaseAnnouncement{ 44 Kind: KindMinor, 45 Version: "go1.18.1", 46 SecondaryVersion: "go1.17.9", 47 Names: []string{"Alice", "Bob", "Charlie"}, 48 }, 49 wantSubject: "Go 1.18.1 and Go 1.17.9 are released", 50 }, 51 { 52 name: "announce-minor-with-security", 53 in: releaseAnnouncement{ 54 Kind: KindMinor, 55 Version: "go1.18.1", 56 SecondaryVersion: "go1.17.9", 57 Security: []string{ 58 `encoding/pem: fix stack overflow in Decode 59 60 A large (more than 5 MB) PEM input can cause a stack overflow in Decode, leading the program to crash. 61 62 Thanks to Juho Nurminen of Mattermost who reported the error. 63 64 This is CVE-2022-24675 and https://go.dev/issue/51853.`, 65 `crypto/elliptic: tolerate all oversized scalars in generic P-256 66 67 A crafted scalar input longer than 32 bytes can cause P256().ScalarMult or P256().ScalarBaseMult to panic. Indirect uses through crypto/ecdsa and crypto/tls are unaffected. amd64, arm64, ppc64le, and s390x are unaffected. 68 69 This was discovered thanks to a Project Wycheproof test vector. 70 71 This is CVE-2022-28327 and https://go.dev/issue/52075.`, 72 `crypto/x509: non-compliant certificates can cause a panic in Verify on macOS in Go 1.18 73 74 Verifying certificate chains containing certificates which are not compliant with RFC 5280 causes Certificate.Verify to panic on macOS. 75 76 These chains can be delivered through TLS and can cause a crypto/tls or net/http client to crash. 77 78 Thanks to Tailscale for doing weird things and finding this. 79 80 This is CVE-2022-27536 and https://go.dev/issue/51759.`, 81 }, 82 }, 83 wantSubject: "[security] Go 1.18.1 and Go 1.17.9 are released", 84 }, 85 { 86 name: "announce-minor-solo", 87 in: releaseAnnouncement{ 88 Kind: KindMinor, 89 Version: "go1.11.1", 90 Security: []string{"abc: security fix 1", "xyz: security fix 2"}, 91 Names: []string{"Alice"}, 92 }, 93 wantSubject: "[security] Go 1.11.1 is released", 94 }, 95 { 96 name: "announce-beta", 97 in: releaseAnnouncement{ 98 Kind: KindBeta, 99 Version: "go1.19beta5", 100 }, 101 wantSubject: "Go 1.19 Beta 5 is released", 102 }, 103 { 104 name: "announce-rc", 105 in: releaseAnnouncement{ 106 Kind: KindRC, 107 Version: "go1.19rc6", 108 }, 109 wantSubject: "Go 1.19 Release Candidate 6 is released", 110 }, 111 { 112 name: "announce-major", 113 in: releaseAnnouncement{ 114 Kind: KindMajor, 115 Version: "go1.21.0", 116 }, 117 wantSubject: "Go 1.21.0 is released", 118 }, 119 120 { 121 name: "pre-announce-minor", 122 in: releasePreAnnouncement{ 123 Target: Date{2022, time.July, 12}, 124 Version: "go1.18.4", 125 SecondaryVersion: "go1.17.12", 126 Security: "the standard library", 127 CVEs: []string{"cve-1234", "cve-5678"}, 128 Names: []string{"Alice"}, 129 }, 130 wantSubject: "[security] Go 1.18.4 and Go 1.17.12 pre-announcement", 131 }, 132 { 133 name: "pre-announce-minor-solo", 134 in: releasePreAnnouncement{ 135 Target: Date{2022, time.July, 12}, 136 Version: "go1.18.4", 137 Security: "the toolchain", 138 CVEs: []string{"cve-1234", "cve-5678"}, 139 Names: []string{"Alice", "Bob"}, 140 }, 141 wantSubject: "[security] Go 1.18.4 pre-announcement", 142 }, 143 } 144 for _, tc := range tests { 145 t.Run(tc.name, func(t *testing.T) { 146 m, err := announcementMail(tc.in) 147 if err != nil { 148 t.Fatal("announcementMail returned non-nil error:", err) 149 } 150 if *updateFlag { 151 writeTestdataFile(t, tc.name+".html", []byte(m.BodyHTML)) 152 writeTestdataFile(t, tc.name+".txt", []byte(m.BodyText)) 153 return 154 } 155 if diff := cmp.Diff(tc.wantSubject, m.Subject); diff != "" { 156 t.Errorf("subject mismatch (-want +got):\n%s", diff) 157 } 158 if diff := cmp.Diff(testdataFile(t, tc.name+".html"), m.BodyHTML); diff != "" { 159 t.Errorf("body HTML mismatch (-want +got):\n%s", diff) 160 } 161 if diff := cmp.Diff(testdataFile(t, tc.name+".txt"), m.BodyText); diff != "" { 162 t.Errorf("body text mismatch (-want +got):\n%s", diff) 163 } 164 if t.Failed() { 165 t.Log("\n\n(if the new output is intentional, use -update flag to update goldens)") 166 } 167 }) 168 } 169 } 170 171 // testdataFile reads the named file in the testdata directory. 172 func testdataFile(t *testing.T, name string) string { 173 t.Helper() 174 b, err := os.ReadFile(filepath.Join("testdata", name)) 175 if err != nil { 176 t.Fatal(err) 177 } 178 return string(b) 179 } 180 181 // writeTestdataFile writes the named file in the testdata directory. 182 func writeTestdataFile(t *testing.T, name string, data []byte) { 183 t.Helper() 184 err := os.WriteFile(filepath.Join("testdata", name), data, 0644) 185 if err != nil { 186 t.Fatal(err) 187 } 188 } 189 190 func TestAnnounceRelease(t *testing.T) { 191 if testing.Short() { 192 t.Skip("not running test that uses internet in short mode") 193 } 194 195 tests := [...]struct { 196 name string 197 kind ReleaseKind 198 published []Published 199 security []string 200 coordinators []string 201 want SentMail 202 wantLog string 203 }{ 204 { 205 name: "minor", 206 kind: KindMinor, 207 published: []Published{{Version: "go1.18.1"}, {Version: "go1.17.8"}}, // Intentionally not 1.17.9 so the real email doesn't get in the way. 208 coordinators: []string{"heschi", "dmitshur"}, 209 want: SentMail{Subject: "Go 1.18.1 and Go 1.17.8 are released"}, 210 wantLog: `announcement subject: Go 1.18.1 and Go 1.17.8 are released 211 212 announcement body HTML: 213 <p>Hello gophers,</p> 214 <p>We have just released Go versions 1.18.1 and 1.17.8, minor point releases.</p> 215 <p>View the release notes for more information:<br> 216 <a href="https://go.dev/doc/devel/release#go1.18.1">https://go.dev/doc/devel/release#go1.18.1</a></p> 217 <p>You can download binary and source distributions from the Go website:<br> 218 <a href="https://go.dev/dl/">https://go.dev/dl/</a></p> 219 <p>To compile from source using a Git clone, update to the release with<br> 220 <code>git checkout go1.18.1</code> and build as usual.</p> 221 <p>Thanks to everyone who contributed to the releases.</p> 222 <p>Cheers,<br> 223 Heschi and Dmitri for the Go team</p> 224 225 announcement body text: 226 Hello gophers, 227 228 We have just released Go versions 1.18.1 and 1.17.8, minor point releases. 229 230 View the release notes for more information: 231 https://go.dev/doc/devel/release#go1.18.1 232 233 You can download binary and source distributions from the Go website: 234 https://go.dev/dl/ 235 236 To compile from source using a Git clone, update to the release with 237 git checkout go1.18.1 and build as usual. 238 239 Thanks to everyone who contributed to the releases. 240 241 Cheers, 242 Heschi and Dmitri for the Go team` + "\n", 243 }, 244 // Just one test case is enough, since TestAnnouncementMail 245 // has very thorough coverage for all release types. 246 } 247 for _, tc := range tests { 248 t.Run(tc.name, func(t *testing.T) { 249 annMail := MailHeader{ 250 From: mail.Address{Address: "from-address@golang.test"}, 251 To: mail.Address{Address: "to-address@golang.test"}, 252 } 253 tasks := AnnounceMailTasks{ 254 SendMail: func(h MailHeader, c MailContent) error { 255 if diff := cmp.Diff(annMail, h); diff != "" { 256 t.Errorf("mail header mismatch (-want +got):\n%s", diff) 257 } 258 if diff := cmp.Diff(tc.want.Subject, c.Subject); diff != "" { 259 t.Errorf("mail subject mismatch (-want +got):\n%s", diff) 260 } 261 return nil 262 }, 263 AnnounceMailHeader: annMail, 264 } 265 var buf bytes.Buffer 266 ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}} 267 sentMail, err := tasks.AnnounceRelease(ctx, tc.kind, tc.published, tc.security, tc.coordinators) 268 if err != nil { 269 if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { 270 t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. 271 } 272 t.Fatal("task function returned non-nil error:", err) 273 } 274 if diff := cmp.Diff(tc.want, sentMail); diff != "" { 275 t.Errorf("sent mail mismatch (-want +got):\n%s", diff) 276 } 277 if diff := cmp.Diff(tc.wantLog, buf.String()); diff != "" { 278 t.Errorf("log mismatch (-want +got):\n%s", diff) 279 } 280 }) 281 } 282 } 283 284 func TestPreAnnounceRelease(t *testing.T) { 285 if testing.Short() { 286 t.Skip("not running test that uses internet in short mode") 287 } 288 289 tests := [...]struct { 290 name string 291 versions []string 292 target Date 293 security string 294 cves []string 295 coordinators []string 296 want SentMail 297 wantLog string 298 }{ 299 { 300 name: "minor", 301 versions: []string{"go1.18.4", "go1.17.11"}, // Intentionally not 1.17.12 so the real email doesn't get in the way. 302 target: Date{2022, time.July, 12}, 303 security: "the standard library", 304 cves: []string{"cve-2022-1234", "cve-2023-1234"}, 305 coordinators: []string{"tatiana"}, 306 want: SentMail{Subject: "[security] Go 1.18.4 and Go 1.17.11 pre-announcement"}, 307 wantLog: `pre-announcement subject: [security] Go 1.18.4 and Go 1.17.11 pre-announcement 308 309 pre-announcement body HTML: 310 <p>Hello gophers,</p> 311 <p>We plan to issue Go 1.18.4 and Go 1.17.11 during US business hours on Tuesday, July 12.</p> 312 <p>These minor releases include PRIVATE security fixes to the standard library, covering the following CVEs:</p> 313 <ul> 314 <li>cve-2022-1234</li> 315 <li>cve-2023-1234</li> 316 </ul> 317 <p>Following our security policy, this is the pre-announcement of those releases.</p> 318 <p>Thanks,<br> 319 Tatiana for the Go team</p> 320 321 pre-announcement body text: 322 Hello gophers, 323 324 We plan to issue Go 1.18.4 and Go 1.17.11 during US business hours on Tuesday, July 12. 325 326 These minor releases include PRIVATE security fixes to the standard library, covering the following CVEs: 327 328 - cve-2022-1234 329 330 - cve-2023-1234 331 332 Following our security policy, this is the pre-announcement of those releases. 333 334 Thanks, 335 Tatiana for the Go team` + "\n", 336 }, 337 // TestAnnouncementMail has additional coverage. 338 } 339 for _, tc := range tests { 340 t.Run(tc.name, func(t *testing.T) { 341 tasks := AnnounceMailTasks{ 342 SendMail: func(h MailHeader, c MailContent) error { return nil }, 343 testHookNow: func() time.Time { return time.Date(2022, time.July, 7, 0, 0, 0, 0, time.UTC) }, 344 } 345 var buf bytes.Buffer 346 ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}} 347 sentMail, err := tasks.PreAnnounceRelease(ctx, tc.versions, tc.target, tc.security, tc.cves, tc.coordinators) 348 if err != nil { 349 if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { 350 t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. 351 } 352 t.Fatal("task function returned non-nil error:", err) 353 } 354 if diff := cmp.Diff(tc.want, sentMail); diff != "" { 355 t.Errorf("sent mail mismatch (-want +got):\n%s", diff) 356 } 357 if diff := cmp.Diff(tc.wantLog, buf.String()); diff != "" { 358 t.Errorf("log mismatch (-want +got):\n%s", diff) 359 } 360 }) 361 } 362 } 363 364 func TestFindGoogleGroupsThread(t *testing.T) { 365 if testing.Short() { 366 t.Skip("not running test that uses internet in short mode") 367 } 368 369 threadURL, err := findGoogleGroupsThread(&workflow.TaskContext{ 370 Context: context.Background(), 371 }, "[security] Go 1.18.3 and Go 1.17.11 are released") 372 if err != nil { 373 if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { 374 t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. 375 } 376 t.Fatalf("findGoogleGroupsThread returned a non-nil error: %v", err) 377 } 378 // Just log the threadURL since we can't rely on stable output. 379 // This test is mostly for debugging if we need to. 380 t.Logf("threadURL: %q\n", threadURL) 381 } 382 383 func TestMarkdownToText(t *testing.T) { 384 const in = `Hello gophers, 385 386 This is a simple Markdown document that exercises 387 a limited set of features used in email templates. 388 389 There may be security fixes following the [security policy](https://go.dev/security): 390 391 - abc: Read hangs on extremely large input 392 393 On an operating system, ` + "`Read`" + ` will hang indefinitely if 394 the buffer size is larger than 1 << 64 - 1 bytes. 395 396 Thanks to Gopher A for reporting the issue. 397 398 This is CVE-123 and Go issue https://go.dev/issue/123. 399 400 - xyz: Clean("X") returns "Y" when Z 401 402 Some description of the problem here. 403 404 Markdown allows one to use backslash escapes, like \_underscore\_ 405 or \*literal asterisks\*, so we might encounter that. 406 407 View release notes: 408 https://go.dev/doc/devel/release#go1.18.3 409 410 You can download binaries: 411 https://go.dev/dl/ 412 413 To builds from source, use 414 ` + "`git checkout`" + `. 415 416 An easy way to try go1.19beta1 417 is by using the go command: 418 $ go install example.org@latest 419 $ example download 420 421 That's all for now. 422 ` 423 _, got, err := renderMarkdown(strings.NewReader(in)) 424 if err != nil { 425 t.Fatal(err) 426 } 427 428 const want = `Hello gophers, 429 430 This is a simple Markdown document that exercises 431 a limited set of features used in email templates. 432 433 There may be security fixes following the security policy <https://go.dev/security>: 434 435 - abc: Read hangs on extremely large input 436 437 On an operating system, Read will hang indefinitely if 438 the buffer size is larger than 1 << 64 - 1 bytes. 439 440 Thanks to Gopher A for reporting the issue. 441 442 This is CVE-123 and Go issue https://go.dev/issue/123. 443 444 - xyz: Clean("X") returns "Y" when Z 445 446 Some description of the problem here. 447 448 Markdown allows one to use backslash escapes, like \_underscore\_ 449 or \*literal asterisks\*, so we might encounter that. 450 451 View release notes: 452 https://go.dev/doc/devel/release#go1.18.3 453 454 You can download binaries: 455 https://go.dev/dl/ 456 457 To builds from source, use 458 git checkout. 459 460 An easy way to try go1.19beta1 461 is by using the go command: 462 $ go install example.org@latest 463 $ example download 464 465 That's all for now. 466 ` 467 if diff := cmp.Diff(want, got); diff != "" { 468 t.Errorf("plain text rendering mismatch (-want +got):\n%s", diff) 469 } 470 }