github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/dashboard/app/email_test.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/google/syzkaller/dashboard/dashapi"
    14  	"github.com/google/syzkaller/pkg/email"
    15  	"github.com/google/syzkaller/sys/targets"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  // nolint: funlen
    20  func TestEmailReport(t *testing.T) {
    21  	c := NewCtx(t)
    22  	defer c.Close()
    23  
    24  	build := testBuild(1)
    25  	c.client2.UploadBuild(build)
    26  
    27  	crash := testCrash(build, 1)
    28  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`}
    29  	c.client2.ReportCrash(crash)
    30  
    31  	// Report the crash over email and check all fields.
    32  	var sender0, extBugID0, body0 string
    33  	var dbBug0 *Bug
    34  	{
    35  		msg := c.pollEmailBug()
    36  		sender0 = msg.Sender
    37  		body0 = msg.Body
    38  		sender, extBugID, err := email.RemoveAddrContext(msg.Sender)
    39  		c.expectOK(err)
    40  		extBugID0 = extBugID
    41  		dbBug, dbCrash, dbBuild := c.loadBug(extBugID0)
    42  		dbBug0 = dbBug
    43  		crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
    44  		kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
    45  		c.expectEQ(sender, fromAddr(c.ctx))
    46  		to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email
    47  		c.expectEQ(msg.To, []string{to})
    48  		c.expectEQ(msg.Subject, crash.Title)
    49  		c.expectEQ(len(msg.Attachments), 0)
    50  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
    51  
    52  syzbot found the following issue on:
    53  
    54  HEAD commit:    111111111111 kernel_commit_title1
    55  git tree:       repo1 branch1
    56  console output: %[2]v
    57  kernel config:  %[3]v
    58  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
    59  compiler:       compiler1
    60  CC:             [bar@foo.com foo@bar.com idont@want.EMAILS]
    61  
    62  Unfortunately, I don't have any reproducer for this issue yet.
    63  
    64  IMPORTANT: if you fix the issue, please add the following tag to the commit:
    65  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
    66  
    67  report1
    68  
    69  ---
    70  This report is generated by a bot. It may contain errors.
    71  See https://goo.gl/tpsmEJ for more information about syzbot.
    72  syzbot engineers can be reached at syzkaller@googlegroups.com.
    73  
    74  syzbot will keep track of this issue. See:
    75  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
    76  
    77  If the report is already addressed, let syzbot know by replying with:
    78  #syz fix: exact-commit-title
    79  
    80  If you want to overwrite report's subsystems, reply with:
    81  #syz set subsystems: new-subsystem
    82  (See the list of subsystem names on the web dashboard)
    83  
    84  If the report is a duplicate of another one, reply with:
    85  #syz dup: exact-subject-of-another-report
    86  
    87  If you want to undo deduplication, reply with:
    88  #syz undup`,
    89  			extBugID0, crashLogLink, kernelConfigLink))
    90  		c.checkURLContents(crashLogLink, crash.Log)
    91  		c.checkURLContents(kernelConfigLink, build.KernelConfig)
    92  	}
    93  
    94  	// Emulate receive of the report from a mailing list.
    95  	// This should update the bug with the link/Message-ID.
    96  	// nolint: lll
    97  	incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
    98  Date: Tue, 15 Aug 2017 14:59:00 -0700
    99  Message-ID: <1234>
   100  Subject: crash1
   101  From: %v
   102  To: foo@bar.com
   103  Content-Type: text/plain
   104  
   105  Hello
   106  
   107  syzbot will keep track of this issue.
   108  If you forgot to add the Reported-by tag, once the fix for this bug is merged
   109  into any tree, please reply to this email with:
   110  #syz fix: exact-commit-title
   111  To mark this as a duplicate of another syzbot report, please reply with:
   112  #syz dup: exact-subject-of-another-report
   113  If it's a one-off invalid bug report, please reply with:
   114  #syz invalid
   115  
   116  -- 
   117  You received this message because you are subscribed to the Google Groups "syzkaller" group.
   118  To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
   119  To post to this group, send email to syzkaller@googlegroups.com.
   120  To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com.
   121  For more options, visit https://groups.google.com/d/optout.
   122  `, sender0)
   123  
   124  	_, err := c.POST("/_ah/mail/", incoming1)
   125  	c.expectOK(err)
   126  
   127  	// Emulate that somebody sends us our own email back without quoting.
   128  	// We used to extract "#syz fix: exact-commit-title" from it.
   129  	c.incomingEmail(sender0, body0)
   130  
   131  	c.incomingEmail(sender0, "I don't want emails", EmailOptFrom(`"idont" <idont@WANT.emails>`))
   132  	c.expectNoEmail()
   133  
   134  	// This person sends an email and is listed as a maintainer, but opt-out of emails.
   135  	// We should not send anything else to them for this bug. Also don't warn about no mailing list in CC.
   136  	c.incomingEmail(sender0, "#syz uncc", EmailOptFrom(`"IDONT" <Idont@want.emails>`), EmailOptCC(nil))
   137  	c.expectNoEmail()
   138  
   139  	// Now report syz reproducer and check updated email.
   140  	build2 := testBuild(10)
   141  	build2.Arch = targets.I386
   142  	build2.KernelRepo = testConfig.Namespaces["test2"].Repos[0].URL
   143  	build2.KernelBranch = testConfig.Namespaces["test2"].Repos[0].Branch
   144  	build2.KernelCommitTitle = "a really long title, longer than 80 chars, really long-long-long-long-long-long title"
   145  	c.client2.UploadBuild(build2)
   146  	crash.BuildID = build2.ID
   147  	crash.ReproOpts = []byte("repro opts")
   148  	crash.ReproSyz = []byte("getpid()")
   149  	syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s",
   150  		dbBug0.keyHash(c.ctx), syzReproPrefix, crash.ReproOpts, crash.ReproSyz))
   151  	c.client2.ReportCrash(crash)
   152  
   153  	{
   154  		msg := c.pollEmailBug()
   155  		c.expectEQ(msg.Sender, sender0)
   156  		sender, _, err := email.RemoveAddrContext(msg.Sender)
   157  		c.expectOK(err)
   158  		_, dbCrash, dbBuild := c.loadBug(extBugID0)
   159  		reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   160  		crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   161  		kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   162  		c.expectEQ(sender, fromAddr(c.ctx))
   163  		to := []string{
   164  			"always@cc.me",
   165  			"bugs2@syzkaller.com",
   166  			"bugs@syzkaller.com", // This is from incomingEmail.
   167  			"default@sender.com", // This is from incomingEmail.
   168  			"foo@bar.com",
   169  			c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email,
   170  		}
   171  		c.expectEQ(msg.To, to)
   172  		c.expectEQ(msg.Subject, "Re: "+crash.Title)
   173  		c.expectEQ(len(msg.Attachments), 0)
   174  		c.expectEQ(msg.Headers["In-Reply-To"], []string{"<1234>"})
   175  		c.expectEQ(msg.Body, fmt.Sprintf(`syzbot has found a reproducer for the following issue on:
   176  
   177  HEAD commit:    101010101010 a really long title, longer than 80 chars, re..
   178  git tree:       repo10alias
   179  console output: %[3]v
   180  kernel config:  %[4]v
   181  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   182  compiler:       compiler10
   183  userspace arch: i386
   184  syz repro:      %[2]v
   185  CC:             [bar@foo.com foo@bar.com maintainers@repo10.org bugs@repo10.org]
   186  
   187  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   188  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   189  
   190  report1
   191  
   192  ---
   193  If you want syzbot to run the reproducer, reply with:
   194  #syz test: git://repo/address.git branch-or-commit-hash
   195  If you attach or paste a git patch, syzbot will apply it before testing.
   196  `, extBugID0, reproSyzLink, crashLogLink, kernelConfigLink))
   197  		c.checkURLContents(reproSyzLink, syzRepro)
   198  		c.checkURLContents(crashLogLink, crash.Log)
   199  		c.checkURLContents(kernelConfigLink, build2.KernelConfig)
   200  	}
   201  
   202  	// Now upstream the bug and check that it reaches the next reporting.
   203  	c.incomingEmail(sender0, "#syz upstream")
   204  
   205  	sender1, extBugID1 := "", ""
   206  	{
   207  		msg := c.pollEmailBug()
   208  		sender1 = msg.Sender
   209  		c.expectNE(sender1, sender0)
   210  		sender, extBugID, err := email.RemoveAddrContext(msg.Sender)
   211  		c.expectOK(err)
   212  		extBugID1 = extBugID
   213  		_, dbCrash, dbBuild := c.loadBug(extBugID1)
   214  		reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   215  		crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   216  		kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   217  		c.expectEQ(sender, fromAddr(c.ctx))
   218  		c.expectEQ(msg.To, []string{
   219  			"always@cc.me",
   220  			"bar@foo.com",
   221  			"bugs@repo10.org",
   222  			"bugs@syzkaller.com",
   223  			"default@maintainers.com",
   224  			"foo@bar.com",
   225  			"maintainers@repo10.org",
   226  		})
   227  		c.expectEQ(msg.Subject, "[syzbot] "+crash.Title)
   228  		c.expectEQ(len(msg.Attachments), 0)
   229  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   230  
   231  syzbot found the following issue on:
   232  
   233  HEAD commit:    101010101010 a really long title, longer than 80 chars, re..
   234  git tree:       repo10alias
   235  console output: %[3]v
   236  kernel config:  %[4]v
   237  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   238  compiler:       compiler10
   239  userspace arch: i386
   240  syz repro:      %[2]v
   241  CC:             [bar@foo.com foo@bar.com maintainers@repo10.org bugs@repo10.org]
   242  
   243  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   244  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   245  
   246  report1
   247  
   248  ---
   249  This report is generated by a bot. It may contain errors.
   250  See https://goo.gl/tpsmEJ for more information about syzbot.
   251  syzbot engineers can be reached at syzkaller@googlegroups.com.
   252  
   253  syzbot will keep track of this issue. See:
   254  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   255  
   256  If the report is already addressed, let syzbot know by replying with:
   257  #syz fix: exact-commit-title
   258  
   259  If you want syzbot to run the reproducer, reply with:
   260  #syz test: git://repo/address.git branch-or-commit-hash
   261  If you attach or paste a git patch, syzbot will apply it before testing.
   262  
   263  If you want to overwrite report's subsystems, reply with:
   264  #syz set subsystems: new-subsystem
   265  (See the list of subsystem names on the web dashboard)
   266  
   267  If the report is a duplicate of another one, reply with:
   268  #syz dup: exact-subject-of-another-report
   269  
   270  If you want to undo deduplication, reply with:
   271  #syz undup`,
   272  			extBugID1, reproSyzLink, crashLogLink, kernelConfigLink))
   273  		c.checkURLContents(reproSyzLink, syzRepro)
   274  		c.checkURLContents(crashLogLink, crash.Log)
   275  		c.checkURLContents(kernelConfigLink, build2.KernelConfig)
   276  	}
   277  
   278  	// Model that somebody adds more emails to CC list.
   279  	incoming3 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
   280  Date: Tue, 15 Aug 2017 14:59:00 -0700
   281  Message-ID: <1234>
   282  Subject: crash1
   283  From: foo@bar.com
   284  To: %v
   285  CC: new@new.com, "another" <another@another.com>, bar@foo.com, bugs@syzkaller.com, foo@bar.com
   286  Content-Type: text/plain
   287  
   288  +more people
   289  `, sender1)
   290  
   291  	_, err = c.POST("/_ah/mail/", incoming3)
   292  	c.expectOK(err)
   293  
   294  	// Now upload a C reproducer.
   295  	crash.ReproC = []byte("int main() {}")
   296  	crash.Maintainers = []string{"\"qux\" <qux@qux.com>"}
   297  	c.client2.ReportCrash(crash)
   298  	cRepro := []byte(fmt.Sprintf("// https://testapp.appspot.com/bug?id=%v\n%s",
   299  		dbBug0.keyHash(c.ctx), crash.ReproC))
   300  
   301  	{
   302  		msg := c.pollEmailBug()
   303  		c.expectEQ(msg.Sender, sender1)
   304  		sender, _, err := email.RemoveAddrContext(msg.Sender)
   305  		c.expectOK(err)
   306  		_, dbCrash, dbBuild := c.loadBug(extBugID1)
   307  		reproCLink := externalLink(c.ctx, textReproC, dbCrash.ReproC)
   308  		reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   309  		crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   310  		kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   311  		c.expectEQ(sender, fromAddr(c.ctx))
   312  		c.expectEQ(msg.To, []string{
   313  			"always@cc.me",
   314  			"another@another.com", "bar@foo.com", "bugs@repo10.org",
   315  			"bugs@syzkaller.com", "default@maintainers.com", "foo@bar.com",
   316  			"maintainers@repo10.org", "new@new.com", "qux@qux.com"})
   317  		c.expectEQ(msg.Subject, "Re: [syzbot] "+crash.Title)
   318  		c.expectEQ(len(msg.Attachments), 0)
   319  		c.expectEQ(msg.Body, fmt.Sprintf(`syzbot has found a reproducer for the following issue on:
   320  
   321  HEAD commit:    101010101010 a really long title, longer than 80 chars, re..
   322  git tree:       repo10alias
   323  console output: %[4]v
   324  kernel config:  %[5]v
   325  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   326  compiler:       compiler10
   327  userspace arch: i386
   328  syz repro:      %[3]v
   329  C reproducer:   %[2]v
   330  CC:             [qux@qux.com maintainers@repo10.org bugs@repo10.org]
   331  
   332  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   333  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   334  
   335  report1
   336  
   337  ---
   338  If you want syzbot to run the reproducer, reply with:
   339  #syz test: git://repo/address.git branch-or-commit-hash
   340  If you attach or paste a git patch, syzbot will apply it before testing.
   341  `, extBugID1, reproCLink, reproSyzLink, crashLogLink, kernelConfigLink))
   342  		c.checkURLContents(reproCLink, cRepro)
   343  		c.checkURLContents(reproSyzLink, syzRepro)
   344  		c.checkURLContents(crashLogLink, crash.Log)
   345  		c.checkURLContents(kernelConfigLink, build2.KernelConfig)
   346  	}
   347  
   348  	// Send an invalid command.
   349  	incoming4 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
   350  Date: Tue, 15 Aug 2017 14:59:00 -0700
   351  Message-ID: <abcdef>
   352  Subject: title1
   353  From: foo@bar.com
   354  To: %v
   355  Content-Type: text/plain
   356  
   357  #syz bad-command
   358  `, sender1)
   359  
   360  	_, err = c.POST("/_ah/mail/", incoming4)
   361  	c.expectOK(err)
   362  
   363  	{
   364  		msg := c.pollEmailBug()
   365  		c.expectEQ(msg.To, []string{"foo@bar.com"})
   366  		c.expectEQ(msg.Subject, "Re: title1")
   367  		c.expectEQ(msg.Headers["In-Reply-To"], []string{"<abcdef>"})
   368  		if !strings.Contains(msg.Body, `> #syz bad-command
   369  
   370  unknown command "bad-command"
   371  `) {
   372  			t.Fatal("no unknown command reply for bad command")
   373  		}
   374  	}
   375  
   376  	// Now mark the bug as fixed.
   377  	c.incomingEmail(sender1, "#syz fix: some: commit title",
   378  		EmailOptCC([]string{"bugs@syzkaller.com", "default@maintainers.com"}),
   379  		EmailOptSubject("fix bug title"))
   380  
   381  	// Check that the commit is now passed to builders.
   382  	builderPollResp, _ := c.client2.BuilderPoll(build.Manager)
   383  	c.expectEQ(len(builderPollResp.PendingCommits), 1)
   384  	c.expectEQ(builderPollResp.PendingCommits[0], "some: commit title")
   385  
   386  	build3 := testBuild(3)
   387  	build3.Manager = build.Manager
   388  	build3.Commits = []string{"some: commit title"}
   389  	c.client2.UploadBuild(build3)
   390  
   391  	build4 := testBuild(4)
   392  	build4.Manager = build2.Manager
   393  	build4.Commits = []string{"some: commit title"}
   394  	c.client2.UploadBuild(build4)
   395  
   396  	// New crash must produce new bug in the first reporting.
   397  	c.client2.ReportCrash(crash)
   398  	{
   399  		msg := c.pollEmailBug()
   400  		c.expectEQ(msg.Subject, crash.Title+" (2)")
   401  		c.expectNE(msg.Sender, sender0)
   402  	}
   403  }
   404  
   405  // Bug must not be mailed to maintainers if maintainers list is empty.
   406  func TestEmailNoMaintainers(t *testing.T) {
   407  	c := NewCtx(t)
   408  	defer c.Close()
   409  
   410  	build := testBuild(1)
   411  	c.client2.UploadBuild(build)
   412  
   413  	crash := testCrash(build, 1)
   414  	c.client2.ReportCrash(crash)
   415  
   416  	sender := c.pollEmailBug().Sender
   417  
   418  	incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
   419  Date: Tue, 15 Aug 2017 14:59:00 -0700
   420  Message-ID: <1234>
   421  Subject: crash1
   422  From: %v
   423  To: foo@bar.com
   424  Content-Type: text/plain
   425  
   426  #syz upstream
   427  `, sender)
   428  	_, err := c.POST("/_ah/mail/", incoming1)
   429  	c.expectOK(err)
   430  }
   431  
   432  // Basic dup scenario: mark one bug as dup of another.
   433  func TestEmailDup(t *testing.T) {
   434  	c := NewCtx(t)
   435  	defer c.Close()
   436  
   437  	build := testBuild(1)
   438  	c.client2.UploadBuild(build)
   439  
   440  	crash1 := testCrash(build, 1)
   441  	crash1.Title = "BUG: slightly more elaborate title"
   442  	c.client2.ReportCrash(crash1)
   443  
   444  	crash2 := testCrash(build, 2)
   445  	crash2.Title = "KASAN: another title"
   446  	c.client2.ReportCrash(crash2)
   447  
   448  	msg1 := c.pollEmailBug()
   449  	msg2 := c.pollEmailBug()
   450  
   451  	// Dup crash2 to crash1.
   452  	c.incomingEmail(msg2.Sender, "#syz dup: BUG: slightly more elaborate title")
   453  	c.expectNoEmail()
   454  
   455  	// Second crash happens again.
   456  	crash2.ReproC = []byte("int main() {}")
   457  	c.client2.ReportCrash(crash2)
   458  	c.expectNoEmail()
   459  
   460  	// Now close the original bug, and check that new bugs for dup are now created.
   461  	c.incomingEmail(msg1.Sender, "#syz invalid")
   462  
   463  	// "uncc" command must not trugger error reply even for closed bug.
   464  	c.incomingEmail(msg1.Sender, "#syz uncc", EmailOptCC(nil))
   465  	c.expectNoEmail()
   466  
   467  	// New crash must produce new bug in the first reporting.
   468  	c.client2.ReportCrash(crash2)
   469  	{
   470  		msg := c.pollEmailBug()
   471  		c.expectEQ(msg.Subject, crash2.Title+" (2)")
   472  	}
   473  }
   474  
   475  func TestEmailDup2(t *testing.T) {
   476  	for i := 0; i < 4; i++ {
   477  		i := i
   478  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   479  			c := NewCtx(t)
   480  			defer c.Close()
   481  			build := testBuild(1)
   482  			c.client2.UploadBuild(build)
   483  
   484  			crash1 := testCrash(build, 1)
   485  			crash1.Title = "BUG: something bad"
   486  			c.client2.ReportCrash(crash1)
   487  			msg1 := c.pollEmailBug()
   488  			c.incomingEmail(msg1.Sender, "#syz upstream")
   489  			msg1 = c.pollEmailBug()
   490  			c.expectEQ(msg1.Subject, "[syzbot] BUG: something bad")
   491  
   492  			crash2 := testCrash(build, 2)
   493  			crash2.Title = "KASAN: another bad thing"
   494  			c.client2.ReportCrash(crash2)
   495  			msg2 := c.pollEmailBug()
   496  			c.incomingEmail(msg2.Sender, "#syz upstream")
   497  			msg2 = c.pollEmailBug()
   498  			c.expectEQ(msg2.Subject, "[syzbot] KASAN: another bad thing")
   499  
   500  			cc := EmailOptCC([]string{"bugs@syzkaller.com", "default@maintainers.com"})
   501  			switch i {
   502  			case 0:
   503  				c.incomingEmail(msg2.Sender, "#syz dup: BUG: something bad", cc)
   504  			case 1:
   505  				c.incomingEmail(msg2.Sender, "#syz dup: [syzbot] BUG: something bad", cc)
   506  			case 2:
   507  				c.incomingEmail(msg2.Sender, "#syz dup: [syzbot] [subsystemA?] BUG: something bad", cc)
   508  			default:
   509  				c.incomingEmail(msg2.Sender, "#syz dup: syzbot: BUG: something bad", cc)
   510  				reply := c.pollEmailBug()
   511  				c.expectTrue(strings.Contains(reply.Body, "can't find the dup bug"))
   512  			}
   513  		})
   514  	}
   515  }
   516  
   517  func TestEmailUndup(t *testing.T) {
   518  	c := NewCtx(t)
   519  	defer c.Close()
   520  
   521  	build := testBuild(1)
   522  	c.client2.UploadBuild(build)
   523  
   524  	crash1 := testCrash(build, 1)
   525  	crash1.Title = "BUG: slightly more elaborate title"
   526  	c.client2.ReportCrash(crash1)
   527  
   528  	crash2 := testCrash(build, 2)
   529  	crash1.Title = "KASAN: another title"
   530  	c.client2.ReportCrash(crash2)
   531  
   532  	msg1 := c.pollEmailBug()
   533  	msg2 := c.pollEmailBug()
   534  
   535  	// Dup crash2 to crash1.
   536  	c.incomingEmail(msg2.Sender, "#syz dup BUG: slightly more elaborate title")
   537  	c.expectNoEmail()
   538  
   539  	// Undup crash2.
   540  	c.incomingEmail(msg2.Sender, "#syz undup")
   541  	c.expectNoEmail()
   542  
   543  	// Now close the original bug, and check that new crashes for the dup does not create bugs.
   544  	c.incomingEmail(msg1.Sender, "#syz invalid")
   545  	c.client2.ReportCrash(crash2)
   546  	c.expectNoEmail()
   547  }
   548  
   549  func TestEmailCrossReportingDup(t *testing.T) {
   550  	c := NewCtx(t)
   551  	defer c.Close()
   552  
   553  	build := testBuild(1)
   554  	c.client2.UploadBuild(build)
   555  
   556  	tests := []struct {
   557  		bug    int
   558  		dup    int
   559  		result bool
   560  	}{
   561  		{0, 0, true},
   562  		{0, 1, false},
   563  		{0, 2, false},
   564  		{1, 0, false},
   565  		{1, 1, true},
   566  		{1, 2, true},
   567  		{2, 0, false},
   568  		{2, 1, false},
   569  		{2, 2, true},
   570  	}
   571  	for i, test := range tests {
   572  		t.Logf("duping %v->%v, expect %v", test.bug, test.dup, test.result)
   573  		c.advanceTime(24 * time.Hour) // to not hit email limit per day
   574  		crash1 := testCrash(build, 1)
   575  		crash1.Title = fmt.Sprintf("bug_%v", i)
   576  		c.client2.ReportCrash(crash1)
   577  		bugSender := c.pollEmailBug().Sender
   578  		cc := EmailOptCC([]string{"default@maintainers.com", "test@syzkaller.com",
   579  			"bugs@syzkaller.com", "default2@maintainers.com", "bugs2@syzkaller.com"})
   580  		for j := 0; j < test.bug; j++ {
   581  			c.incomingEmail(bugSender, "#syz upstream", cc)
   582  			bugSender = c.pollEmailBug().Sender
   583  		}
   584  
   585  		crash2 := testCrash(build, 2)
   586  		crash2.Title = fmt.Sprintf("dup_%v", i)
   587  		c.client2.ReportCrash(crash2)
   588  		dupSender := c.pollEmailBug().Sender
   589  		for j := 0; j < test.dup; j++ {
   590  			c.incomingEmail(dupSender, "#syz upstream", cc)
   591  			dupSender = c.pollEmailBug().Sender
   592  		}
   593  
   594  		c.incomingEmail(bugSender, "#syz dup: "+crash2.Title, cc)
   595  		if test.result {
   596  			c.expectNoEmail()
   597  		} else {
   598  			msg := c.pollEmailBug()
   599  			if !strings.Contains(msg.Body, "> #syz dup:") ||
   600  				!strings.Contains(msg.Body, "Can't dup bug to a bug in different reporting") {
   601  				c.t.Fatalf("bad reply body:\n%v", msg.Body)
   602  			}
   603  		}
   604  	}
   605  }
   606  
   607  func TestEmailErrors(t *testing.T) {
   608  	c := NewCtx(t)
   609  	defer c.Close()
   610  
   611  	// No reply for email without bug hash and no commands.
   612  	c.incomingEmail("syzbot@testapp.appspotmail.com", "Investment Proposal")
   613  	c.expectNoEmail()
   614  
   615  	// If email contains a command we need to reply.
   616  	c.incomingEmail("syzbot@testapp.appspotmail.com", "#syz invalid")
   617  	reply := c.pollEmailBug()
   618  	c.expectEQ(reply.To, []string{"default@sender.com"})
   619  	c.expectEQ(reply.Body, `> #syz invalid
   620  
   621  I see the command but can't find the corresponding bug.
   622  Please resend the email to syzbot+HASH@testapp.appspotmail.com address
   623  that is the sender of the bug report (also present in the Reported-by tag).
   624  
   625  `)
   626  
   627  	c.incomingEmail("syzbot+123@testapp.appspotmail.com", "#syz invalid")
   628  	reply = c.pollEmailBug()
   629  	c.expectEQ(reply.Body, `> #syz invalid
   630  
   631  I see the command but can't find the corresponding bug.
   632  The email is sent to  syzbot+HASH@testapp.appspotmail.com address
   633  but the HASH does not correspond to any known bug.
   634  Please double check the address.
   635  
   636  `)
   637  }
   638  
   639  func TestEmailFailedBuild(t *testing.T) {
   640  	c := NewCtx(t)
   641  	defer c.Close()
   642  
   643  	build := testBuild(1)
   644  	c.client2.UploadBuild(build)
   645  
   646  	failedBuild := testBuild(10)
   647  	failedBuild.KernelRepo = testConfig.Namespaces["test2"].Repos[0].URL
   648  	failedBuild.KernelBranch = testConfig.Namespaces["test2"].Repos[0].Branch
   649  	failedBuild.KernelCommit = "kern2"
   650  	failedBuild.KernelCommitTitle = "failed build 1"
   651  	failedBuild.SyzkallerCommit = "syz2"
   652  	buildErrorReq := &dashapi.BuildErrorReq{
   653  		Build: *failedBuild,
   654  		Crash: dashapi.Crash{
   655  			Title:       "failed build 1",
   656  			Report:      []byte("report line 1\nreport line 2\n"),
   657  			Log:         []byte("log line 1\nlog line 2\n"),
   658  			Maintainers: []string{"maintainer@crash"},
   659  		},
   660  	}
   661  	c.expectOK(c.client2.ReportBuildError(buildErrorReq))
   662  
   663  	msg := c.pollEmailBug()
   664  	sender, extBugID, err := email.RemoveAddrContext(msg.Sender)
   665  	c.expectOK(err)
   666  	_, dbCrash, dbBuild := c.loadBug(extBugID)
   667  	crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   668  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   669  	c.expectEQ(sender, fromAddr(c.ctx))
   670  	c.expectEQ(msg.To, []string{
   671  		"always@cc.me",
   672  		"test@syzkaller.com",
   673  	})
   674  	c.expectEQ(msg.Subject, buildErrorReq.Crash.Title)
   675  	c.expectEQ(len(msg.Attachments), 0)
   676  	c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   677  
   678  syzbot found the following issue on:
   679  
   680  HEAD commit:    kern2 failed build 1
   681  git tree:       repo10alias
   682  console output: %[2]v
   683  kernel config:  %[3]v
   684  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   685  compiler:       compiler10
   686  CC:             [maintainer@crash maintainers@repo10.org bugs@repo10.org build-maintainers@repo10.org]
   687  
   688  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   689  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   690  
   691  report line 1
   692  report line 2
   693  
   694  
   695  ---
   696  This report is generated by a bot. It may contain errors.
   697  See https://goo.gl/tpsmEJ for more information about syzbot.
   698  syzbot engineers can be reached at syzkaller@googlegroups.com.
   699  
   700  syzbot will keep track of this issue. See:
   701  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   702  
   703  If the report is already addressed, let syzbot know by replying with:
   704  #syz fix: exact-commit-title
   705  
   706  If you want to overwrite report's subsystems, reply with:
   707  #syz set subsystems: new-subsystem
   708  (See the list of subsystem names on the web dashboard)
   709  
   710  If the report is a duplicate of another one, reply with:
   711  #syz dup: exact-subject-of-another-report
   712  
   713  If you want to undo deduplication, reply with:
   714  #syz undup`,
   715  		extBugID, crashLogLink, kernelConfigLink))
   716  }
   717  
   718  // Test for unfix command which should unmark a bug as fixed by any commits.
   719  func TestEmailUnfix(t *testing.T) {
   720  	c := NewCtx(t)
   721  	defer c.Close()
   722  
   723  	build := testBuild(1)
   724  	c.client2.UploadBuild(build)
   725  
   726  	crash := testCrash(build, 1)
   727  	c.client2.ReportCrash(crash)
   728  
   729  	msg := c.pollEmailBug()
   730  
   731  	c.incomingEmail(msg.Sender, "#syz fix: some commit")
   732  	c.expectNoEmail()
   733  	c.incomingEmail(msg.Sender, "#syz unfix")
   734  	c.expectNoEmail()
   735  
   736  	build2 := testBuild(2)
   737  	build2.Manager = build.Manager
   738  	build2.Commits = []string{"some commit"}
   739  	c.client2.UploadBuild(build2)
   740  
   741  	// The bug should be still unfixed, since we unmarked it.
   742  	c.client2.ReportCrash(crash)
   743  	c.expectNoEmail()
   744  }
   745  
   746  func TestEmailManagerCC(t *testing.T) {
   747  	c := NewCtx(t)
   748  	defer c.Close()
   749  
   750  	// Test that we add manager CC.
   751  	build1 := testBuild(1)
   752  	build1.Manager = specialCCManager
   753  	c.client2.UploadBuild(build1)
   754  
   755  	crash := testCrash(build1, 1)
   756  	c.client2.ReportCrash(crash)
   757  
   758  	msg := c.pollEmailBug()
   759  	c.expectEQ(msg.To, []string{
   760  		"always@manager.org",
   761  		"test@syzkaller.com",
   762  	})
   763  
   764  	// Test that we add manager maintainers.
   765  	c.incomingEmail(msg.Sender, "#syz upstream")
   766  	msg = c.pollEmailBug()
   767  	c.expectEQ(msg.To, []string{
   768  		"always@manager.org",
   769  		"bugs@syzkaller.com",
   770  		"default@maintainers.com",
   771  		"maintainers@manager.org",
   772  	})
   773  
   774  	// Test that we add manager build maintainers.
   775  	build2 := testBuild(2)
   776  	build2.Manager = specialCCManager
   777  	buildErrorReq := &dashapi.BuildErrorReq{
   778  		Build: *build2,
   779  		Crash: dashapi.Crash{
   780  			Title:  "failed build 1",
   781  			Report: []byte("report\n"),
   782  			Log:    []byte("log\n"),
   783  		},
   784  	}
   785  	c.expectOK(c.client2.ReportBuildError(buildErrorReq))
   786  	msg = c.pollEmailBug()
   787  	c.expectEQ(msg.To, []string{
   788  		"always@manager.org",
   789  		"test@syzkaller.com",
   790  	})
   791  
   792  	c.incomingEmail(msg.Sender, "#syz upstream")
   793  	msg = c.pollEmailBug()
   794  	c.expectEQ(msg.To, []string{
   795  		"always@manager.org",
   796  		"bugs@syzkaller.com",
   797  		"build-maintainers@manager.org",
   798  		"default@maintainers.com",
   799  		"maintainers@manager.org",
   800  	})
   801  
   802  	// Test that we don't add manager CC when the crash happened on 1+ managers.
   803  	build3 := testBuild(3)
   804  	build1.Manager = specialCCManager
   805  	c.client2.UploadBuild(build3)
   806  	crash = testCrash(build3, 2)
   807  	c.client2.ReportCrash(crash)
   808  
   809  	build4 := testBuild(4)
   810  	c.client2.UploadBuild(build4)
   811  	crash = testCrash(build4, 2)
   812  	c.client2.ReportCrash(crash)
   813  
   814  	msg = c.pollEmailBug()
   815  	c.expectEQ(msg.To, []string{
   816  		"test@syzkaller.com",
   817  	})
   818  
   819  	c.incomingEmail(msg.Sender, "#syz upstream")
   820  	msg = c.pollEmailBug()
   821  	c.expectEQ(msg.To, []string{
   822  		"bugs@syzkaller.com",
   823  		"default@maintainers.com",
   824  	})
   825  }
   826  
   827  func TestStraceReport(t *testing.T) {
   828  	c := NewCtx(t)
   829  	defer c.Close()
   830  
   831  	build := testBuild(1)
   832  	c.client2.UploadBuild(build)
   833  
   834  	crash := testCrash(build, 1)
   835  	crash.Flags = dashapi.CrashUnderStrace
   836  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`}
   837  	c.client2.ReportCrash(crash)
   838  
   839  	// Report the crash over email and check all fields.
   840  	msg := c.pollEmailBug()
   841  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
   842  	c.expectOK(err)
   843  	_, dbCrash, dbBuild := c.loadBug(extBugID)
   844  	crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   845  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   846  	c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   847  
   848  syzbot found the following issue on:
   849  
   850  HEAD commit:    111111111111 kernel_commit_title1
   851  git tree:       repo1 branch1
   852  console+strace: %[2]v
   853  kernel config:  %[3]v
   854  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   855  compiler:       compiler1
   856  CC:             [bar@foo.com foo@bar.com idont@want.EMAILS]
   857  
   858  Unfortunately, I don't have any reproducer for this issue yet.
   859  
   860  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   861  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   862  
   863  report1
   864  
   865  ---
   866  This report is generated by a bot. It may contain errors.
   867  See https://goo.gl/tpsmEJ for more information about syzbot.
   868  syzbot engineers can be reached at syzkaller@googlegroups.com.
   869  
   870  syzbot will keep track of this issue. See:
   871  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   872  
   873  If the report is already addressed, let syzbot know by replying with:
   874  #syz fix: exact-commit-title
   875  
   876  If you want to overwrite report's subsystems, reply with:
   877  #syz set subsystems: new-subsystem
   878  (See the list of subsystem names on the web dashboard)
   879  
   880  If the report is a duplicate of another one, reply with:
   881  #syz dup: exact-subject-of-another-report
   882  
   883  If you want to undo deduplication, reply with:
   884  #syz undup`,
   885  		extBugID, crashLogLink, kernelConfigLink))
   886  	c.checkURLContents(crashLogLink, crash.Log)
   887  }
   888  
   889  func TestSubjectTitleParser(t *testing.T) {
   890  	tests := []struct {
   891  		inSubject string
   892  		outTitle  string
   893  		outSeq    int64
   894  	}{
   895  		{
   896  			inSubject: "Re: kernel BUG in blk_mq_dispatch_rq_list (4)",
   897  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   898  			outSeq:    3,
   899  		},
   900  		{
   901  			inSubject: "Re: [syzbot] kernel BUG in blk_mq_dispatch_rq_list (4)",
   902  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   903  			outSeq:    3,
   904  		},
   905  		{
   906  			// Make sure we always take the (number) at the end.
   907  			inSubject: "Re: kernel BUG in blk_mq_dispatch_rq_list(6) (4)",
   908  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list(6)",
   909  			outSeq:    3,
   910  		},
   911  		{
   912  			inSubject: "RE: kernel BUG in blk_mq_dispatch_rq_list",
   913  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   914  			outSeq:    0,
   915  		},
   916  		{
   917  			// Make sure we trim the title.
   918  			inSubject: "RE:  kernel BUG in blk_mq_dispatch_rq_list ",
   919  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   920  			outSeq:    0,
   921  		},
   922  		{
   923  			inSubject: "Re: ",
   924  			outTitle:  "",
   925  			outSeq:    0,
   926  		},
   927  		{
   928  			inSubject: "Re: [syzbot]",
   929  			outTitle:  "",
   930  			outSeq:    0,
   931  		},
   932  		{
   933  			inSubject: "Re: [syzbot] [subsystemA?]",
   934  			outTitle:  "",
   935  			outSeq:    0,
   936  		},
   937  		{
   938  			// Make sure we delete filesystem tags.
   939  			inSubject: "Re: [syzbot] [ntfs3?] [ext4?] kernel BUG in blk_mq_dispatch_rq_list (4)",
   940  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   941  			outSeq:    3,
   942  		},
   943  	}
   944  
   945  	p := makeSubjectTitleParser(context.Background())
   946  	for _, test := range tests {
   947  		title, seq, err := p.parseTitle(test.inSubject)
   948  		if test.outTitle == "" {
   949  			if err == nil {
   950  				t.Fatalf("subj: %q, expected error, got none (%q)", test.inSubject, title)
   951  			}
   952  		} else if title != test.outTitle {
   953  			t.Fatalf("subj: %q, expected title=%q, got %q", test.inSubject, test.outTitle, title)
   954  		} else if seq != test.outSeq {
   955  			t.Fatalf("subj: %q, expected seq=%q, got %q", test.inSubject, test.outSeq, seq)
   956  		}
   957  	}
   958  }
   959  
   960  func TestBugFromSubjectInference(t *testing.T) {
   961  	c := NewCtx(t)
   962  	defer c.Close()
   963  
   964  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
   965  	client2 := c.makeClient(clientPublicEmail2, keyPublicEmail2, true)
   966  
   967  	build := testBuild(1)
   968  	client.UploadBuild(build)
   969  
   970  	build2 := testBuild(2)
   971  	client2.UploadBuild(build2)
   972  
   973  	const crashTitle = "WARNING in corrupted"
   974  	upstreamCrash := func(client *apiClient, build *dashapi.Build, title string) string {
   975  		// Upload some garbage crashes.
   976  		crash := testCrash(build, 1)
   977  		crash.Title = title
   978  		crash.Log = []byte(fmt.Sprintf("log%v", title))
   979  		crash.Maintainers = []string{"maintainer@kernel.org"}
   980  		client.ReportCrash(crash)
   981  
   982  		sender := c.pollEmailBug().Sender
   983  		c.incomingEmail(sender, "#syz upstream\n")
   984  
   985  		return c.pollEmailBug().Sender
   986  	}
   987  
   988  	upstreamCrash(client, build, "unrelated crash")
   989  	origSender := upstreamCrash(client, build, crashTitle)
   990  	upstreamCrash(client, build, "unrelated crash 2")
   991  
   992  	mailingList := "<" + c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + ">"
   993  
   994  	// First try to ping some non-existing bug.
   995  	subject := "Re: unknown-bug"
   996  	c.incomingEmail("bugs@syzkaller.com",
   997  		syzTestGitBranchSamplePatch,
   998  		EmailOptOrigFrom("test@requester.com"),
   999  		EmailOptFrom(mailingList), EmailOptSubject(subject),
  1000  	)
  1001  	syzbotReply := c.pollEmailBug()
  1002  	c.expectNE(syzbotReply.Sender, origSender)
  1003  	c.expectEQ(strings.Contains(syzbotReply.Body, "can't find the corresponding bug"), true)
  1004  
  1005  	// Now try to test the exiting bug, but with the wrong mailing list.
  1006  	subject = "Re: " + crashTitle
  1007  	c.incomingEmail("bugs@syzkaller.com",
  1008  		syzTestGitBranchSamplePatch,
  1009  		EmailOptOrigFrom("test@requester.com"),
  1010  		EmailOptFrom("<unknown-list@syzkaller.com>"), EmailOptSubject(subject),
  1011  	)
  1012  	body := c.pollEmailBug().Body
  1013  	c.expectEQ(strings.Contains(body, "can't find the corresponding bug"), true)
  1014  
  1015  	// Now try to test the exiting bug with the proper mailing list.
  1016  	c.incomingEmail("bugs@syzkaller.com",
  1017  		syzTestGitBranchSamplePatch,
  1018  		EmailOptFrom(mailingList), EmailOptOrigFrom("test@requester.com"),
  1019  		EmailOptSubject(subject),
  1020  	)
  1021  	syzbotReply = c.pollEmailBug()
  1022  	c.expectEQ(syzbotReply.Sender, origSender)
  1023  	c.expectEQ(strings.Contains(syzbotReply.Body, "This crash does not have a reproducer"), true)
  1024  
  1025  	// Test that a different type of email headers is also parsed fine.
  1026  	c.incomingEmail("bugs@syzkaller.com",
  1027  		syzTestGitBranchSamplePatch,
  1028  		EmailOptSender(mailingList), EmailOptFrom("test@requester.com"),
  1029  		EmailOptSubject(subject),
  1030  	)
  1031  	body = c.pollEmailBug().Body
  1032  	c.expectEQ(strings.Contains(body, "This crash does not have a reproducer"), true)
  1033  
  1034  	// Upstream a same-titled bug in another namespace.
  1035  	upstreamCrash(client2, build2, crashTitle)
  1036  
  1037  	// Ensure that the inference fails with the proper title.
  1038  	c.incomingEmail("bugs@syzkaller.com",
  1039  		syzTestGitBranchSamplePatch,
  1040  		EmailOptSender(mailingList), EmailOptFrom("test@requester.com"),
  1041  		EmailOptSubject(subject),
  1042  	)
  1043  	body = c.pollEmailBug().Body
  1044  	c.expectEQ(strings.Contains(body, "Several bugs with the exact same title"), true)
  1045  
  1046  	// Close the existing bug.
  1047  	c.incomingEmail("bugs@syzkaller.com", "#syz invalid",
  1048  		EmailOptFrom("test@requester.com"), EmailOptSubject(subject),
  1049  		EmailOptCC([]string{mailingList, origSender}),
  1050  	)
  1051  	c.expectNoEmail()
  1052  
  1053  	// Create the (2) of the bug.
  1054  	upstreamCrash(client, build, crashTitle)
  1055  
  1056  	// Make sure syzbot can understand the (2) version.
  1057  	subject = "Re: " + crashTitle + " (2)"
  1058  	c.incomingEmail("bugs@syzkaller.com",
  1059  		syzTestGitBranchSamplePatch,
  1060  		EmailOptFrom(mailingList), EmailOptOrigFrom("<test@requester.com>"),
  1061  		EmailOptSubject(subject),
  1062  	)
  1063  	email := c.pollEmailBug()
  1064  	c.expectEQ(email.To, []string{"test@requester.com"})
  1065  	c.expectEQ(strings.Contains(email.Body, "This crash does not have a reproducer"), true)
  1066  }
  1067  
  1068  // nolint: funlen
  1069  func TestEmailLinks(t *testing.T) {
  1070  	c := NewCtx(t)
  1071  	defer c.Close()
  1072  
  1073  	build := testBuild(1)
  1074  	c.client2.UploadBuild(build)
  1075  
  1076  	crash := testCrash(build, 1)
  1077  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`}
  1078  	c.client2.ReportCrash(crash)
  1079  
  1080  	// Report the crash over email.
  1081  	msg := c.pollEmailBug()
  1082  
  1083  	// Emulate receive of the report from a mailing list.
  1084  	// This should update the bug with the link/Message-ID.
  1085  	// nolint: lll
  1086  	incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
  1087  Date: Tue, 15 Aug 2017 14:59:00 -0700
  1088  Message-ID: <1234>
  1089  Subject: crash1
  1090  From: %v
  1091  To: foo@bar.com
  1092  Content-Type: text/plain
  1093  
  1094  Hello
  1095  
  1096  syzbot will keep track of this issue.
  1097  If you forgot to add the Reported-by tag, once the fix for this bug is merged
  1098  into any tree, please reply to this email with:
  1099  #syz fix: exact-commit-title
  1100  To mark this as a duplicate of another syzbot report, please reply with:
  1101  #syz dup: exact-subject-of-another-report
  1102  If it's a one-off invalid bug report, please reply with:
  1103  #syz invalid
  1104  
  1105  -- 
  1106  You received this message because you are subscribed to the Google Groups "syzkaller" group.
  1107  To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
  1108  To post to this group, send email to syzkaller@googlegroups.com.
  1109  To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com.
  1110  For more options, visit https://groups.google.com/d/optout.
  1111  `, msg.Sender)
  1112  
  1113  	_, err := c.POST("/_ah/mail/", incoming1)
  1114  	c.expectOK(err)
  1115  
  1116  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
  1117  	c.expectOK(err)
  1118  
  1119  	// Make sure Link is set for the last Reporting.
  1120  	dbBug, _, _ := c.loadBug(extBugID)
  1121  	reporting := lastReportedReporting(dbBug)
  1122  	c.expectNE(reporting.Link, "")
  1123  }
  1124  
  1125  func TestEmailPatchTestingAccess(t *testing.T) {
  1126  	c := NewCtx(t)
  1127  	defer c.Close()
  1128  
  1129  	client := c.client2
  1130  
  1131  	build := testBuild(1)
  1132  	client.UploadBuild(build)
  1133  
  1134  	crash := testCrash(build, 1)
  1135  	client.ReportCrash(crash)
  1136  
  1137  	sender := c.pollEmailBug().Sender
  1138  	c.incomingEmail(sender,
  1139  		syzTestGitBranchSamplePatch,
  1140  		EmailOptFrom("user@kernel.org"), EmailOptSubject("Re: "+crash.Title),
  1141  	)
  1142  
  1143  	// We expect syzbot to just ignore this patch testing request.
  1144  	c.expectNoEmail()
  1145  
  1146  	// The patch test job should also not be created.
  1147  	pollResp := client.pollJobs(build.Manager)
  1148  	c.expectEQ(pollResp.ID, "")
  1149  }
  1150  
  1151  func TestEmailSetInvalidSubsystems(t *testing.T) {
  1152  	c := NewCtx(t)
  1153  	defer c.Close()
  1154  
  1155  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1156  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1157  
  1158  	build := testBuild(1)
  1159  	client.UploadBuild(build)
  1160  
  1161  	crash := testCrash(build, 1)
  1162  	client.ReportCrash(crash)
  1163  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1164  
  1165  	sender := c.pollEmailBug().Sender
  1166  
  1167  	// Invalid subsystem name.
  1168  	c.incomingEmail(sender, "#syz set subsystems: non-existent",
  1169  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1170  	c.expectEQ(c.pollEmailBug().Body, `> #syz set subsystems: non-existent
  1171  
  1172  The specified label value is incorrect.
  1173  "non-existent" is not among the allowed values.
  1174  Please use one of the supported label values.
  1175  
  1176  The following labels are suported:
  1177  missing-backport, no-reminders, prio: {low, normal, high}, subsystems: {.. see below ..}
  1178  The list of subsystems: https://testapp.appspot.com/access-public-email/subsystems?all=true
  1179  
  1180  `)
  1181  }
  1182  
  1183  func TestEmailSetSubsystems(t *testing.T) {
  1184  	c := NewCtx(t)
  1185  	defer c.Close()
  1186  
  1187  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1188  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1189  
  1190  	build := testBuild(1)
  1191  	client.UploadBuild(build)
  1192  
  1193  	crash := testCrash(build, 1)
  1194  	client.ReportCrash(crash)
  1195  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1196  
  1197  	sender := c.pollEmailBug().Sender
  1198  	_, extBugID, err := email.RemoveAddrContext(sender)
  1199  	c.expectOK(err)
  1200  
  1201  	// At the beginning, there are no subsystems.
  1202  	expectLabels(t, client, extBugID)
  1203  
  1204  	// Set one subsystem.
  1205  	c.incomingEmail(sender, "#syz set subsystems: subsystemA\n",
  1206  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1207  	expectLabels(t, client, extBugID, "subsystems:subsystemA")
  1208  
  1209  	// Set two subsystems.
  1210  	c.incomingEmail(sender, "#syz set subsystems: subsystemA, subsystemB\n",
  1211  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1212  	expectLabels(t, client, extBugID, "subsystems:subsystemA", "subsystems:subsystemB")
  1213  }
  1214  
  1215  func TestEmailBugLabels(t *testing.T) {
  1216  	c := NewCtx(t)
  1217  	defer c.Close()
  1218  
  1219  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1220  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1221  
  1222  	build := testBuild(1)
  1223  	client.UploadBuild(build)
  1224  
  1225  	crash := testCrash(build, 1)
  1226  	client.ReportCrash(crash)
  1227  
  1228  	sender := c.pollEmailBug().Sender
  1229  	_, extBugID, err := email.RemoveAddrContext(sender)
  1230  	c.expectOK(err)
  1231  
  1232  	// At the beginning, there are no tags.
  1233  	expectLabels(t, client, extBugID)
  1234  
  1235  	// Set a tag.
  1236  	c.incomingEmail(sender, "#syz set prio: low\n",
  1237  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1238  	expectLabels(t, client, extBugID, "prio:low")
  1239  
  1240  	// Notice that medium prio supercedes low prio since they are of the oneOf type.
  1241  	c.incomingEmail(sender, "#syz set prio: high\n",
  1242  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1243  	expectLabels(t, client, extBugID, "prio:high")
  1244  
  1245  	// Also set a flag label.
  1246  	c.incomingEmail(sender, "#syz set no-reminders\n",
  1247  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1248  	expectLabels(t, client, extBugID, "prio:high", "no-reminders")
  1249  
  1250  	// Remove a tag.
  1251  	c.incomingEmail(sender, "#syz unset prio\n",
  1252  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1253  	expectLabels(t, client, extBugID, "no-reminders")
  1254  
  1255  	// Remove another tag.
  1256  	c.incomingEmail(sender, "#syz unset no-reminders\n",
  1257  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1258  	expectLabels(t, client, extBugID)
  1259  }
  1260  
  1261  func TestInvalidEmailBugLabels(t *testing.T) {
  1262  	c := NewCtx(t)
  1263  	defer c.Close()
  1264  
  1265  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1266  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1267  
  1268  	build := testBuild(1)
  1269  	client.UploadBuild(build)
  1270  
  1271  	crash := testCrash(build, 1)
  1272  	client.ReportCrash(crash)
  1273  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1274  
  1275  	sender := c.pollEmailBug().Sender
  1276  
  1277  	// Non-existing label.
  1278  	c.incomingEmail(sender, "#syz set label: tag",
  1279  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1280  	body := c.pollEmailBug().Body
  1281  	c.expectEQ(body, `> #syz set label: tag
  1282  
  1283  The specified label "label" is unknown.
  1284  Please use one of the supported labels.
  1285  
  1286  The following labels are suported:
  1287  missing-backport, no-reminders, prio: {low, normal, high}, subsystems: {.. see below ..}
  1288  The list of subsystems: https://testapp.appspot.com/access-public-email/subsystems?all=true
  1289  
  1290  `)
  1291  
  1292  	// Existing label, wrong value.
  1293  	c.incomingEmail(sender, "#syz set prio: unknown\n",
  1294  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1295  	c.expectEQ(strings.Contains(c.pollEmailBug().Body,
  1296  		`The specified label value is incorrect.
  1297  "unknown" is not among the allowed values`), true)
  1298  
  1299  	// Existing label, too many values.
  1300  	c.incomingEmail(sender, "#syz set prio: low, high\n",
  1301  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1302  	c.expectEQ(strings.Contains(c.pollEmailBug().Body,
  1303  		`The specified label value is incorrect.
  1304  You must specify only one of the allowed values.`), true)
  1305  
  1306  	// Removing a non-existing label.
  1307  	c.incomingEmail(sender, "#syz unset tag2\n",
  1308  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1309  	syzbotReply := c.pollEmailBug()
  1310  	c.expectEQ(strings.Contains(syzbotReply.Body, "The following labels did not exist: tag2"), true)
  1311  }
  1312  
  1313  func expectLabels(t *testing.T, client *apiClient, extID string, labels ...string) {
  1314  	t.Helper()
  1315  	bug, _, _ := client.Ctx.loadBug(extID)
  1316  	names := []string{}
  1317  	for _, item := range bug.Labels {
  1318  		names = append(names, item.String())
  1319  	}
  1320  	assert.ElementsMatch(t, names, labels)
  1321  }
  1322  
  1323  var forwardEmailConfig = EmailConfig{
  1324  	Email:              "test@syzkaller.com",
  1325  	HandleListEmails:   true,
  1326  	SubjectPrefix:      "[syzbot]",
  1327  	MailMaintainers:    true,
  1328  	DefaultMaintainers: []string{"some@list.com"},
  1329  }
  1330  
  1331  func TestSingleListForward(t *testing.T) {
  1332  	c := NewCtx(t)
  1333  	defer c.Close()
  1334  
  1335  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1336  	c.updateReporting("access-public-email", "access-public-email-reporting1",
  1337  		func(r Reporting) Reporting {
  1338  			r.Config = &forwardEmailConfig
  1339  			return r
  1340  		})
  1341  
  1342  	build := testBuild(1)
  1343  	client.UploadBuild(build)
  1344  
  1345  	crash := testCrash(build, 1)
  1346  	client.ReportCrash(crash)
  1347  
  1348  	sender := c.pollEmailBug().Sender
  1349  
  1350  	c.incomingEmail(sender, "#syz fix: some: commit title",
  1351  		EmailOptCC([]string{"some@list.com"}), EmailOptSubject("fix bug title"))
  1352  
  1353  	forwarded := c.pollEmailBug()
  1354  	c.expectEQ(forwarded.Subject, "[syzbot] fix bug title")
  1355  	c.expectEQ(forwarded.Sender, sender)
  1356  	c.expectEQ(forwarded.To, []string{"test@syzkaller.com"})
  1357  	c.expectEQ(len(forwarded.Cc), 0)
  1358  	c.expectEQ(forwarded.Body, `For archival purposes, forwarding an incoming command email to
  1359  test@syzkaller.com.
  1360  
  1361  ***
  1362  
  1363  Subject: fix bug title
  1364  Author: default@sender.com
  1365  
  1366  #syz fix: some: commit title
  1367  `)
  1368  }
  1369  
  1370  func TestTwoListsForward(t *testing.T) {
  1371  	c := NewCtx(t)
  1372  	defer c.Close()
  1373  
  1374  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1375  	c.updateReporting("access-public-email", "access-public-email-reporting1",
  1376  		func(r Reporting) Reporting {
  1377  			r.Config = &forwardEmailConfig
  1378  			return r
  1379  		})
  1380  
  1381  	build := testBuild(1)
  1382  	client.UploadBuild(build)
  1383  
  1384  	crash := testCrash(build, 1)
  1385  	client.ReportCrash(crash)
  1386  
  1387  	sender := c.pollEmailBug().Sender
  1388  
  1389  	c.incomingEmail(sender, "#syz fix: some: commit title",
  1390  		EmailOptCC(nil), EmailOptSubject("fix bug title"))
  1391  
  1392  	forwarded := c.pollEmailBug()
  1393  	c.expectEQ(forwarded.Subject, "[syzbot] fix bug title")
  1394  	c.expectEQ(forwarded.Sender, sender)
  1395  	c.expectEQ(forwarded.To, []string{"some@list.com", "test@syzkaller.com"})
  1396  	c.expectEQ(len(forwarded.Cc), 0)
  1397  	c.expectEQ(forwarded.Body, `For archival purposes, forwarding an incoming command email to
  1398  some@list.com, test@syzkaller.com.
  1399  
  1400  ***
  1401  
  1402  Subject: fix bug title
  1403  Author: default@sender.com
  1404  
  1405  #syz fix: some: commit title
  1406  `)
  1407  }