| // Copyright 2021 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. |
| |
| package task |
| |
| import ( |
| "bytes" |
| "context" |
| "errors" |
| "net/mail" |
| "os" |
| "path/filepath" |
| "strings" |
| "testing" |
| "time" |
| |
| "github.com/google/go-cmp/cmp" |
| "golang.org/x/build/internal/workflow" |
| ) |
| |
| // Test that the task doesn't start running if the provided |
| // context doesn't have sufficient time for the task to run. |
| func TestAnnounceReleaseShortContext(t *testing.T) { |
| ctx, cancel := context.WithTimeout(context.Background(), time.Second) |
| defer cancel() |
| _, err := (AnnounceMailTasks{}).AnnounceRelease(&workflow.TaskContext{Context: ctx}, KindMinor, []Published{{Version: "go1.18.1"}, {Version: "go1.17.8"}}, nil, nil) |
| if err == nil { |
| t.Errorf("want non-nil error") |
| } else if !strings.HasPrefix(err.Error(), "insufficient time") { |
| t.Errorf("want error that starts with 'insufficient time' instead of: %s", err) |
| } |
| } |
| |
| func TestAnnouncementMail(t *testing.T) { |
| tests := [...]struct { |
| name string |
| in any |
| wantSubject string |
| }{ |
| { |
| name: "announce-minor", |
| in: releaseAnnouncement{ |
| Kind: KindMinor, |
| Version: "go1.18.1", |
| SecondaryVersion: "go1.17.9", |
| Names: []string{"Alice", "Bob", "Charlie"}, |
| }, |
| wantSubject: "Go 1.18.1 and Go 1.17.9 are released", |
| }, |
| { |
| name: "announce-minor-with-security", |
| in: releaseAnnouncement{ |
| Kind: KindMinor, |
| Version: "go1.18.1", |
| SecondaryVersion: "go1.17.9", |
| Security: []string{ |
| `encoding/pem: fix stack overflow in Decode |
| |
| A large (more than 5 MB) PEM input can cause a stack overflow in Decode, leading the program to crash. |
| |
| Thanks to Juho Nurminen of Mattermost who reported the error. |
| |
| This is CVE-2022-24675 and https://go.dev/issue/51853.`, |
| `crypto/elliptic: tolerate all oversized scalars in generic P-256 |
| |
| 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. |
| |
| This was discovered thanks to a Project Wycheproof test vector. |
| |
| This is CVE-2022-28327 and https://go.dev/issue/52075.`, |
| `crypto/x509: non-compliant certificates can cause a panic in Verify on macOS in Go 1.18 |
| |
| Verifying certificate chains containing certificates which are not compliant with RFC 5280 causes Certificate.Verify to panic on macOS. |
| |
| These chains can be delivered through TLS and can cause a crypto/tls or net/http client to crash. |
| |
| Thanks to Tailscale for doing weird things and finding this. |
| |
| This is CVE-2022-27536 and https://go.dev/issue/51759.`, |
| }, |
| }, |
| wantSubject: "[security] Go 1.18.1 and Go 1.17.9 are released", |
| }, |
| { |
| name: "announce-minor-solo", |
| in: releaseAnnouncement{ |
| Kind: KindMinor, |
| Version: "go1.11.1", |
| Security: []string{"abc: security fix 1", "xyz: security fix 2"}, |
| Names: []string{"Alice"}, |
| }, |
| wantSubject: "[security] Go 1.11.1 is released", |
| }, |
| { |
| name: "announce-beta", |
| in: releaseAnnouncement{ |
| Kind: KindBeta, |
| Version: "go1.19beta5", |
| }, |
| wantSubject: "Go 1.19 Beta 5 is released", |
| }, |
| { |
| name: "announce-rc", |
| in: releaseAnnouncement{ |
| Kind: KindRC, |
| Version: "go1.23rc1", |
| }, |
| wantSubject: "Go 1.23 Release Candidate 1 is released", |
| }, |
| { |
| name: "announce-major", |
| in: releaseAnnouncement{ |
| Kind: KindMajor, |
| Version: "go1.21.0", |
| }, |
| wantSubject: "Go 1.21.0 is released", |
| }, |
| |
| { |
| name: "pre-announce-minor", |
| in: releasePreAnnouncement{ |
| Target: Date{2022, time.July, 12}, |
| Version: "go1.18.4", |
| SecondaryVersion: "go1.17.12", |
| Security: "the standard library", |
| CVEs: []string{"cve-1234", "cve-5678"}, |
| Names: []string{"Alice"}, |
| }, |
| wantSubject: "[security] Go 1.18.4 and Go 1.17.12 pre-announcement", |
| }, |
| { |
| name: "pre-announce-minor-solo", |
| in: releasePreAnnouncement{ |
| Target: Date{2022, time.July, 12}, |
| Version: "go1.18.4", |
| Security: "the toolchain", |
| CVEs: []string{"cve-1234", "cve-5678"}, |
| Names: []string{"Alice", "Bob"}, |
| }, |
| wantSubject: "[security] Go 1.18.4 pre-announcement", |
| }, |
| { |
| name: "gopls-pre-announce", |
| in: goplsPrereleaseAnnouncement{ |
| Version: "v0.16.2-pre.1", |
| Branch: "gopls-release-branch.0.16", |
| Commit: "abc123def456ghi789", |
| Issue: 12345, |
| }, |
| wantSubject: "Gopls v0.16.2-pre.1 is released", |
| }, |
| } |
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| m, err := announcementMail(tc.in) |
| if err != nil { |
| t.Fatal("announcementMail returned non-nil error:", err) |
| } |
| if *updateFlag { |
| writeTestdataFile(t, tc.name+".html", []byte(m.BodyHTML)) |
| writeTestdataFile(t, tc.name+".txt", []byte(m.BodyText)) |
| return |
| } |
| if diff := cmp.Diff(tc.wantSubject, m.Subject); diff != "" { |
| t.Errorf("subject mismatch (-want +got):\n%s", diff) |
| } |
| if diff := cmp.Diff(testdataFile(t, tc.name+".html"), m.BodyHTML); diff != "" { |
| t.Errorf("body HTML mismatch (-want +got):\n%s", diff) |
| } |
| if diff := cmp.Diff(testdataFile(t, tc.name+".txt"), m.BodyText); diff != "" { |
| t.Errorf("body text mismatch (-want +got):\n%s", diff) |
| } |
| if t.Failed() { |
| t.Log("\n\n(if the new output is intentional, use -update flag to update goldens)") |
| } |
| }) |
| } |
| } |
| |
| // testdataFile reads the named file in the testdata directory. |
| func testdataFile(t *testing.T, name string) string { |
| t.Helper() |
| b, err := os.ReadFile(filepath.Join("testdata", name)) |
| if err != nil { |
| t.Fatal(err) |
| } |
| return string(b) |
| } |
| |
| // writeTestdataFile writes the named file in the testdata directory. |
| func writeTestdataFile(t *testing.T, name string, data []byte) { |
| t.Helper() |
| err := os.WriteFile(filepath.Join("testdata", name), data, 0644) |
| if err != nil { |
| t.Fatal(err) |
| } |
| } |
| |
| func TestAnnounceRelease(t *testing.T) { |
| if testing.Short() { |
| t.Skip("not running test that uses internet in short mode") |
| } |
| |
| tests := [...]struct { |
| name string |
| kind ReleaseKind |
| published []Published |
| security []string |
| coordinators []string |
| want SentMail |
| wantLog string |
| }{ |
| { |
| name: "minor", |
| kind: KindMinor, |
| 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. |
| coordinators: []string{"heschi", "dmitshur"}, |
| want: SentMail{Subject: "Go 1.18.1 and Go 1.17.8 are released"}, |
| wantLog: `announcement subject: Go 1.18.1 and Go 1.17.8 are released |
| |
| announcement body HTML: |
| <p>Hello gophers,</p> |
| <p>We have just released Go versions 1.18.1 and 1.17.8, minor point releases.</p> |
| <p>View the release notes for more information:<br> |
| <a href="https://go.dev/doc/devel/release#go1.18.1">https://go.dev/doc/devel/release#go1.18.1</a></p> |
| <p>You can download binary and source distributions from the Go website:<br> |
| <a href="https://go.dev/dl/">https://go.dev/dl/</a></p> |
| <p>To compile from source using a Git clone, update to the release with<br> |
| <code>git checkout go1.18.1</code> and build as usual.</p> |
| <p>Thanks to everyone who contributed to the releases.</p> |
| <p>Cheers,<br> |
| Heschi and Dmitri for the Go team</p> |
| |
| announcement body text: |
| Hello gophers, |
| |
| We have just released Go versions 1.18.1 and 1.17.8, minor point releases. |
| |
| View the release notes for more information: |
| https://go.dev/doc/devel/release#go1.18.1 |
| |
| You can download binary and source distributions from the Go website: |
| https://go.dev/dl/ |
| |
| To compile from source using a Git clone, update to the release with |
| git checkout go1.18.1 and build as usual. |
| |
| Thanks to everyone who contributed to the releases. |
| |
| Cheers, |
| Heschi and Dmitri for the Go team` + "\n", |
| }, |
| // Just one test case is enough, since TestAnnouncementMail |
| // has very thorough coverage for all release types. |
| } |
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| annMail := MailHeader{ |
| From: mail.Address{Address: "from-address@golang.test"}, |
| To: mail.Address{Address: "to-address@golang.test"}, |
| } |
| tasks := AnnounceMailTasks{ |
| SendMail: func(h MailHeader, c MailContent) error { |
| if diff := cmp.Diff(annMail, h); diff != "" { |
| t.Errorf("mail header mismatch (-want +got):\n%s", diff) |
| } |
| if diff := cmp.Diff(tc.want.Subject, c.Subject); diff != "" { |
| t.Errorf("mail subject mismatch (-want +got):\n%s", diff) |
| } |
| return nil |
| }, |
| AnnounceMailHeader: annMail, |
| } |
| var buf bytes.Buffer |
| ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}} |
| sentMail, err := tasks.AnnounceRelease(ctx, tc.kind, tc.published, tc.security, tc.coordinators) |
| if err != nil { |
| if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { |
| t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. |
| } |
| t.Fatal("task function returned non-nil error:", err) |
| } |
| if diff := cmp.Diff(tc.want, sentMail); diff != "" { |
| t.Errorf("sent mail mismatch (-want +got):\n%s", diff) |
| } |
| if diff := cmp.Diff(tc.wantLog, buf.String()); diff != "" { |
| t.Errorf("log mismatch (-want +got):\n%s", diff) |
| } |
| }) |
| } |
| } |
| |
| func TestPreAnnounceRelease(t *testing.T) { |
| if testing.Short() { |
| t.Skip("not running test that uses internet in short mode") |
| } |
| |
| tests := [...]struct { |
| name string |
| versions []string |
| target Date |
| security string |
| cves []string |
| coordinators []string |
| want SentMail |
| wantLog string |
| }{ |
| { |
| name: "minor", |
| versions: []string{"go1.18.4", "go1.17.11"}, // Intentionally not 1.17.12 so the real email doesn't get in the way. |
| target: Date{2022, time.July, 12}, |
| security: "the standard library", |
| cves: []string{"cve-2022-1234", "cve-2023-1234"}, |
| coordinators: []string{"tatiana"}, |
| want: SentMail{Subject: "[security] Go 1.18.4 and Go 1.17.11 pre-announcement"}, |
| wantLog: `pre-announcement subject: [security] Go 1.18.4 and Go 1.17.11 pre-announcement |
| |
| pre-announcement body HTML: |
| <p>Hello gophers,</p> |
| <p>We plan to issue Go 1.18.4 and Go 1.17.11 during US business hours on Tuesday, July 12.</p> |
| <p>These minor releases include PRIVATE security fixes to the standard library, covering the following CVEs:</p> |
| <ul> |
| <li>cve-2022-1234</li> |
| <li>cve-2023-1234</li> |
| </ul> |
| <p>Following our security policy, this is the pre-announcement of those releases.</p> |
| <p>Thanks,<br> |
| Tatiana for the Go team</p> |
| |
| pre-announcement body text: |
| Hello gophers, |
| |
| We plan to issue Go 1.18.4 and Go 1.17.11 during US business hours on Tuesday, July 12. |
| |
| These minor releases include PRIVATE security fixes to the standard library, covering the following CVEs: |
| |
| - cve-2022-1234 |
| |
| - cve-2023-1234 |
| |
| Following our security policy, this is the pre-announcement of those releases. |
| |
| Thanks, |
| Tatiana for the Go team` + "\n", |
| }, |
| // TestAnnouncementMail has additional coverage. |
| } |
| for _, tc := range tests { |
| t.Run(tc.name, func(t *testing.T) { |
| tasks := AnnounceMailTasks{ |
| SendMail: func(h MailHeader, c MailContent) error { return nil }, |
| testHookNow: func() time.Time { return time.Date(2022, time.July, 7, 0, 0, 0, 0, time.UTC) }, |
| } |
| var buf bytes.Buffer |
| ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}} |
| sentMail, err := tasks.PreAnnounceRelease(ctx, tc.versions, tc.target, tc.security, tc.cves, tc.coordinators) |
| if err != nil { |
| if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { |
| t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. |
| } |
| t.Fatal("task function returned non-nil error:", err) |
| } |
| if diff := cmp.Diff(tc.want, sentMail); diff != "" { |
| t.Errorf("sent mail mismatch (-want +got):\n%s", diff) |
| } |
| if diff := cmp.Diff(tc.wantLog, buf.String()); diff != "" { |
| t.Errorf("log mismatch (-want +got):\n%s", diff) |
| } |
| }) |
| } |
| } |
| |
| func TestFindGoogleGroupsThread(t *testing.T) { |
| if testing.Short() { |
| t.Skip("not running test that uses internet in short mode") |
| } |
| |
| threadURL, err := findGoogleGroupsThread(&workflow.TaskContext{ |
| Context: context.Background(), |
| }, "[security] Go 1.18.3 and Go 1.17.11 are released") |
| if err != nil { |
| if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable { |
| t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541. |
| } |
| t.Fatalf("findGoogleGroupsThread returned a non-nil error: %v", err) |
| } |
| // Just log the threadURL since we can't rely on stable output. |
| // This test is mostly for debugging if we need to. |
| t.Logf("threadURL: %q\n", threadURL) |
| } |
| |
| func TestMarkdownToText(t *testing.T) { |
| const in = `Hello gophers, |
| |
| This is a simple Markdown document that exercises |
| a limited set of features used in email templates. |
| |
| There may be security fixes following the [security policy](https://go.dev/security): |
| |
| - abc: Read hangs on extremely large input |
| |
| On an operating system, ` + "`Read`" + ` will hang indefinitely if |
| the buffer size is larger than 1 << 64 - 1 bytes. |
| |
| Thanks to Gopher A for reporting the issue. |
| |
| This is CVE-123 and Go issue https://go.dev/issue/123. |
| |
| - xyz: Clean("X") returns "Y" when Z |
| |
| Some description of the problem here. |
| |
| Regular Code Block |
| Can |
| Be |
| Here |
| |
| Another paragraph. |
| |
| ` + "```" + ` |
| Fenced Code Block |
| Can |
| Be |
| Here |
| ` + "```" + ` |
| |
| Markdown allows one to use backslash escapes, like \_underscore\_ |
| or \*literal asterisks\*, so we might encounter that. |
| |
| View release notes: |
| https://go.dev/doc/devel/release#go1.18.3 |
| |
| You can download binaries: |
| https://go.dev/dl/ |
| |
| To builds from source, use |
| ` + "`git checkout`" + `. |
| |
| An easy way to try go1.19beta1 |
| is by using the go command: |
| |
| ` + "```" + ` |
| $ go install example.org@latest |
| $ example download |
| ` + "```" + ` |
| |
| That's all for now. |
| ` |
| _, got, err := renderMarkdown(strings.NewReader(in)) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| const want = `Hello gophers, |
| |
| This is a simple Markdown document that exercises |
| a limited set of features used in email templates. |
| |
| There may be security fixes following the security policy <https://go.dev/security>: |
| |
| - abc: Read hangs on extremely large input |
| |
| On an operating system, Read will hang indefinitely if |
| the buffer size is larger than 1 << 64 - 1 bytes. |
| |
| Thanks to Gopher A for reporting the issue. |
| |
| This is CVE-123 and Go issue https://go.dev/issue/123. |
| |
| - xyz: Clean("X") returns "Y" when Z |
| |
| Some description of the problem here. |
| |
| Regular Code Block |
| Can |
| Be |
| Here |
| |
| Another paragraph. |
| |
| Fenced Code Block |
| Can |
| Be |
| Here |
| |
| Markdown allows one to use backslash escapes, like \_underscore\_ |
| or \*literal asterisks\*, so we might encounter that. |
| |
| View release notes: |
| https://go.dev/doc/devel/release#go1.18.3 |
| |
| You can download binaries: |
| https://go.dev/dl/ |
| |
| To builds from source, use |
| git checkout. |
| |
| An easy way to try go1.19beta1 |
| is by using the go command: |
| |
| $ go install example.org@latest |
| $ example download |
| |
| That's all for now. |
| ` |
| if diff := cmp.Diff(want, got); diff != "" { |
| t.Errorf("plain text rendering mismatch (-want +got):\n%s", diff) |
| } |
| } |