// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // +build aetest package dash import ( "fmt" "strings" "testing" "github.com/google/syzkaller/pkg/email" ) func TestEmailReport(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client2.UploadBuild(build) crash := testCrash(build, 1) crash.Maintainers = []string{`"Foo Bar" `, `bar@foo.com`} c.client2.ReportCrash(crash) // Report the crash over email and check all fields. var sender0, extBugID0, body0 string { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink sender0 = msg.Sender body0 = msg.Body sender, extBugID, err := email.RemoveAddrContext(msg.Sender) if err != nil { t.Fatalf("failed to remove sender context: %v", err) } extBugID0 = extBugID _, dbCrash, dbBuild := c.loadBug(extBugID0) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) to := config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email c.expectEQ(msg.To, []string{to}) c.expectEQ(msg.Subject, crash.Title) c.expectEQ(len(msg.Attachments), 0) body := fmt.Sprintf(`Hello, syzbot found the following crash on: HEAD commit: 111111111111 kernel_commit_title1 git tree: repo1/branch1 console output: %[2]v kernel config: %[3]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler1 CC: [bar@foo.com foo@bar.com] Unfortunately, I don't have any reproducer for this crash yet. IMPORTANT: if you fix the bug, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 --- This bug is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller@googlegroups.com. syzbot will keep track of this bug report. See: https://goo.gl/tpsmEJ#bug-status-tracking for how to communicate with syzbot.`, extBugID0, crashLogLink, kernelConfigLink) if msg.Body != body { t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body) } c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build.KernelConfig) } // Emulate receive of the report from a mailing list. // This should update the bug with the link/Message-ID. // nolint: lll incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com Date: Tue, 15 Aug 2017 14:59:00 -0700 Message-ID: <1234> Subject: crash1 From: %v To: foo@bar.com Content-Type: text/plain Hello syzbot will keep track of this bug report. If you forgot to add the Reported-by tag, once the fix for this bug is merged into any tree, please reply to this email with: #syz fix: exact-commit-title To mark this as a duplicate of another syzbot report, please reply with: #syz dup: exact-subject-of-another-report If it's a one-off invalid bug report, please reply with: #syz invalid -- You received this message because you are subscribed to the Google Groups "syzkaller" group. To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com. To post to this group, send email to syzkaller@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com. For more options, visit https://groups.google.com/d/optout. `, sender0) c.expectOK(c.POST("/_ah/mail/", incoming1)) // Emulate that somebody sends us our own email back without quoting. // We used to extract "#syz fix: exact-commit-title" from it. c.incomingEmail(sender0, body0) // Now report syz reproducer and check updated email. crash.ReproOpts = []byte("repro opts") crash.ReproSyz = []byte("getpid()") syzRepro := []byte(fmt.Sprintf("%s#%s\n%s", syzReproPrefix, crash.ReproOpts, crash.ReproSyz)) c.client2.ReportCrash(crash) { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink c.expectEQ(msg.Sender, sender0) sender, _, err := email.RemoveAddrContext(msg.Sender) if err != nil { t.Fatalf("failed to remove sender context: %v", err) } _, dbCrash, dbBuild := c.loadBug(extBugID0) reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) to := []string{ "bugs@syzkaller.com", "default@sender.com", // This is from incomingEmail. "foo@bar.com", config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email, } c.expectEQ(msg.To, to) c.expectEQ(msg.Subject, "Re: "+crash.Title) c.expectEQ(len(msg.Attachments), 0) c.expectEQ(msg.Headers["In-Reply-To"], []string{"<1234>"}) body := fmt.Sprintf(`syzbot has found a reproducer for the following crash on: HEAD commit: 111111111111 kernel_commit_title1 git tree: repo1/branch1 console output: %[3]v kernel config: %[4]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler1 syzkaller repro:%[2]v CC: [bar@foo.com foo@bar.com] IMPORTANT: if you fix the bug, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 `, extBugID0, reproSyzLink, crashLogLink, kernelConfigLink) if msg.Body != body { t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body) } c.checkURLContents(reproSyzLink, syzRepro) c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build.KernelConfig) } // Now upstream the bug and check that it reaches the next reporting. c.incomingEmail(sender0, "#syz upstream") sender1, extBugID1 := "", "" { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink sender1 = msg.Sender if sender1 == sender0 { t.Fatalf("same ID in different reporting") } sender, extBugID, err := email.RemoveAddrContext(msg.Sender) if err != nil { t.Fatalf("failed to remove sender context: %v", err) } extBugID1 = extBugID _, dbCrash, dbBuild := c.loadBug(extBugID1) reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) c.expectEQ(msg.To, []string{"bar@foo.com", "bugs@syzkaller.com", "default@maintainers.com", "foo@bar.com"}) c.expectEQ(msg.Subject, crash.Title) c.expectEQ(len(msg.Attachments), 0) body := fmt.Sprintf(`Hello, syzbot found the following crash on: HEAD commit: 111111111111 kernel_commit_title1 git tree: repo1/branch1 console output: %[3]v kernel config: %[4]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler1 syzkaller repro:%[2]v IMPORTANT: if you fix the bug, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 --- This bug is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller@googlegroups.com. syzbot will keep track of this bug report. See: https://goo.gl/tpsmEJ#bug-status-tracking for how to communicate with syzbot. syzbot can test patches for this bug, for details see: https://goo.gl/tpsmEJ#testing-patches`, extBugID1, reproSyzLink, crashLogLink, kernelConfigLink) if msg.Body != body { t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body) } c.checkURLContents(reproSyzLink, syzRepro) c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build.KernelConfig) } // Model that somebody adds more emails to CC list. incoming3 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com Date: Tue, 15 Aug 2017 14:59:00 -0700 Message-ID: <1234> Subject: crash1 From: foo@bar.com To: %v CC: new@new.com, "another" , bar@foo.com, bugs@syzkaller.com, foo@bar.com Content-Type: text/plain +more people `, sender1) c.expectOK(c.POST("/_ah/mail/", incoming3)) // Now upload a C reproducer. build2 := testBuild(2) build2.KernelCommitTitle = "a really long title, longer than 80 chars, really long-long-long-long-long-long title" c.client2.UploadBuild(build2) crash.BuildID = build2.ID crash.ReproC = []byte("int main() {}") crash.Maintainers = []string{"\"qux\" "} c.client2.ReportCrash(crash) { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink c.expectEQ(msg.Sender, sender1) sender, _, err := email.RemoveAddrContext(msg.Sender) if err != nil { t.Fatalf("failed to remove sender context: %v", err) } _, dbCrash, dbBuild := c.loadBug(extBugID1) reproCLink := externalLink(c.ctx, textReproC, dbCrash.ReproC) reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) c.expectEQ(msg.To, []string{"another@another.com", "bar@foo.com", "bugs@syzkaller.com", "default@maintainers.com", "foo@bar.com", "new@new.com", "qux@qux.com"}) c.expectEQ(msg.Subject, "Re: "+crash.Title) c.expectEQ(len(msg.Attachments), 0) body := fmt.Sprintf(`syzbot has found a reproducer for the following crash on: HEAD commit: 222222222222 a really long title, longer than 80 chars, re.. git tree: repo2/branch2 console output: %[4]v kernel config: %[5]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler2 syzkaller repro:%[3]v C reproducer: %[2]v IMPORTANT: if you fix the bug, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 `, extBugID1, reproCLink, reproSyzLink, crashLogLink, kernelConfigLink) if msg.Body != body { t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body) } c.checkURLContents(reproCLink, crash.ReproC) c.checkURLContents(reproSyzLink, syzRepro) c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build2.KernelConfig) } // Send an invalid command. incoming4 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com Date: Tue, 15 Aug 2017 14:59:00 -0700 Message-ID: Subject: title1 From: foo@bar.com To: %v Content-Type: text/plain #syz bad-command `, sender1) c.expectOK(c.POST("/_ah/mail/", incoming4)) { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink c.expectEQ(msg.To, []string{""}) c.expectEQ(msg.Subject, "Re: title1") c.expectEQ(msg.Headers["In-Reply-To"], []string{""}) if !strings.Contains(msg.Body, `> #syz bad-command unknown command "bad-command" `) { t.Fatal("no unknown command reply for bad command") } } // Now mark the bug as fixed. c.incomingEmail(sender1, "#syz fix: some: commit title") c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) // Check that the commit is now passed to builders. builderPollResp, _ := c.client2.BuilderPoll(build.Manager) c.expectEQ(len(builderPollResp.PendingCommits), 1) c.expectEQ(builderPollResp.PendingCommits[0], "some: commit title") build3 := testBuild(3) build3.Manager = build.Manager build3.Commits = []string{"some: commit title"} c.client2.UploadBuild(build3) build4 := testBuild(4) build4.Manager = build2.Manager build4.Commits = []string{"some: commit title"} c.client2.UploadBuild(build4) // New crash must produce new bug in the first reporting. c.client2.ReportCrash(crash) { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink c.expectEQ(msg.Subject, crash.Title+" (2)") if msg.Sender == sender0 { t.Fatalf("same reporting ID for new bug") } } } // Bug must not be mailed to maintainers if maintainers list is empty. func TestEmailNoMaintainers(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client2.UploadBuild(build) crash := testCrash(build, 1) c.client2.ReportCrash(crash) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) sender := (<-c.emailSink).Sender incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com Date: Tue, 15 Aug 2017 14:59:00 -0700 Message-ID: <1234> Subject: crash1 From: %v To: foo@bar.com Content-Type: text/plain #syz upstream `, sender) c.expectOK(c.POST("/_ah/mail/", incoming1)) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) } // Basic dup scenario: mark one bug as dup of another. func TestEmailDup(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client2.UploadBuild(build) crash1 := testCrash(build, 1) crash1.Title = "BUG: slightly more elaborate title" c.client2.ReportCrash(crash1) crash2 := testCrash(build, 2) crash1.Title = "KASAN: another title" c.client2.ReportCrash(crash2) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 2) msg1 := <-c.emailSink msg2 := <-c.emailSink // Dup crash2 to crash1. c.incomingEmail(msg2.Sender, "#syz dup: BUG: slightly more elaborate title") c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) // Second crash happens again crash2.ReproC = []byte("int main() {}") c.client2.ReportCrash(crash2) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) // Now close the original bug, and check that new bugs for dup are now created. c.incomingEmail(msg1.Sender, "#syz invalid") // New crash must produce new bug in the first reporting. c.client2.ReportCrash(crash2) { c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 1) msg := <-c.emailSink c.expectEQ(msg.Subject, crash2.Title+" (2)") } } func TestEmailUndup(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client2.UploadBuild(build) crash1 := testCrash(build, 1) crash1.Title = "BUG: slightly more elaborate title" c.client2.ReportCrash(crash1) crash2 := testCrash(build, 2) crash1.Title = "KASAN: another title" c.client2.ReportCrash(crash2) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 2) msg1 := <-c.emailSink msg2 := <-c.emailSink // Dup crash2 to crash1. c.incomingEmail(msg2.Sender, "#syz dup: BUG: slightly more elaborate title") c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) // Undup crash2. c.incomingEmail(msg2.Sender, "#syz undup") c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) // Now close the original bug, and check that new crashes for the dup does not create bugs. c.incomingEmail(msg1.Sender, "#syz invalid") c.client2.ReportCrash(crash2) c.expectOK(c.GET("/email_poll")) c.expectEQ(len(c.emailSink), 0) }