github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  // nolint: funlen
    21  func TestEmailReport(t *testing.T) {
    22  	c := NewCtx(t)
    23  	defer c.Close()
    24  
    25  	build := testBuild(1)
    26  	c.client2.UploadBuild(build)
    27  
    28  	crash := testCrash(build, 1)
    29  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`}
    30  	c.client2.ReportCrash(crash)
    31  
    32  	// Report the crash over email and check all fields.
    33  	var sender0, extBugID0, body0 string
    34  	var dbBug0 *Bug
    35  	{
    36  		msg := c.pollEmailBug()
    37  		sender0 = msg.Sender
    38  		body0 = msg.Body
    39  		sender, extBugID, err := email.RemoveAddrContext(msg.Sender)
    40  		c.expectOK(err)
    41  		extBugID0 = extBugID
    42  		dbBug, dbCrash, dbBuild := c.loadBug(extBugID0)
    43  		dbBug0 = dbBug
    44  		crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
    45  		kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
    46  		c.expectEQ(sender, fromAddr(c.ctx))
    47  		to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email
    48  		c.expectEQ(msg.To, []string{to})
    49  		c.expectEQ(msg.Subject, crash.Title)
    50  		c.expectEQ(len(msg.Attachments), 0)
    51  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
    52  
    53  syzbot found the following issue on:
    54  
    55  HEAD commit:    111111111111 kernel_commit_title1
    56  git tree:       repo1 branch1
    57  console output: %[2]v
    58  kernel config:  %[3]v
    59  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
    60  compiler:       compiler1
    61  CC:             [bar@foo.com foo@bar.com idont@want.EMAILS]
    62  
    63  Unfortunately, I don't have any reproducer for this issue yet.
    64  
    65  IMPORTANT: if you fix the issue, please add the following tag to the commit:
    66  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
    67  
    68  report1
    69  
    70  ---
    71  This report is generated by a bot. It may contain errors.
    72  See https://goo.gl/tpsmEJ for more information about syzbot.
    73  syzbot engineers can be reached at syzkaller@googlegroups.com.
    74  
    75  syzbot will keep track of this issue. See:
    76  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
    77  
    78  If the report is already addressed, let syzbot know by replying with:
    79  #syz fix: exact-commit-title
    80  
    81  If you want to overwrite report's subsystems, reply with:
    82  #syz set subsystems: new-subsystem
    83  (See the list of subsystem names on the web dashboard)
    84  
    85  If the report is a duplicate of another one, reply with:
    86  #syz dup: exact-subject-of-another-report
    87  
    88  If you want to undo deduplication, reply with:
    89  #syz undup`,
    90  			extBugID0, crashLogLink, kernelConfigLink))
    91  		c.checkURLContents(crashLogLink, crash.Log)
    92  		c.checkURLContents(kernelConfigLink, build.KernelConfig)
    93  	}
    94  
    95  	// Emulate receive of the report from a mailing list.
    96  	// This should update the bug with the link/Message-ID.
    97  	// nolint: lll
    98  	incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
    99  Date: Tue, 15 Aug 2017 14:59:00 -0700
   100  Message-ID: <1234>
   101  Subject: crash1
   102  From: %v
   103  To: foo@bar.com
   104  Content-Type: text/plain
   105  
   106  Hello
   107  
   108  syzbot will keep track of this issue.
   109  If you forgot to add the Reported-by tag, once the fix for this bug is merged
   110  into any tree, please reply to this email with:
   111  #syz fix: exact-commit-title
   112  To mark this as a duplicate of another syzbot report, please reply with:
   113  #syz dup: exact-subject-of-another-report
   114  If it's a one-off invalid bug report, please reply with:
   115  #syz invalid
   116  
   117  -- 
   118  You received this message because you are subscribed to the Google Groups "syzkaller" group.
   119  To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
   120  To post to this group, send email to syzkaller@googlegroups.com.
   121  To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com.
   122  For more options, visit https://groups.google.com/d/optout.
   123  `, sender0)
   124  
   125  	_, err := c.POST("/_ah/mail/", incoming1)
   126  	c.expectOK(err)
   127  
   128  	// Emulate that somebody sends us our own email back without quoting.
   129  	// We used to extract "#syz fix: exact-commit-title" from it.
   130  	c.incomingEmail(sender0, body0)
   131  
   132  	c.incomingEmail(sender0, "I don't want emails", EmailOptFrom(`"idont" <idont@WANT.emails>`))
   133  	c.expectNoEmail()
   134  
   135  	// This person sends an email and is listed as a maintainer, but opt-out of emails.
   136  	// We should not send anything else to them for this bug. Also don't warn about no mailing list in CC.
   137  	c.incomingEmail(sender0, "#syz uncc", EmailOptFrom(`"IDONT" <Idont@want.emails>`), EmailOptCC(nil))
   138  	c.expectNoEmail()
   139  
   140  	// Now report syz reproducer and check updated email.
   141  	build2 := testBuild(10)
   142  	build2.Arch = targets.I386
   143  	build2.KernelRepo, build2.KernelBranch = testConfig.Namespaces["test2"].mainRepoBranch()
   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  		t.Run(fmt.Sprint(i), func(t *testing.T) {
   478  			c := NewCtx(t)
   479  			defer c.Close()
   480  			build := testBuild(1)
   481  			c.client2.UploadBuild(build)
   482  
   483  			crash1 := testCrash(build, 1)
   484  			crash1.Title = "BUG: something bad"
   485  			c.client2.ReportCrash(crash1)
   486  			msg1 := c.pollEmailBug()
   487  			c.incomingEmail(msg1.Sender, "#syz upstream")
   488  			msg1 = c.pollEmailBug()
   489  			c.expectEQ(msg1.Subject, "[syzbot] BUG: something bad")
   490  
   491  			crash2 := testCrash(build, 2)
   492  			crash2.Title = "KASAN: another bad thing"
   493  			c.client2.ReportCrash(crash2)
   494  			msg2 := c.pollEmailBug()
   495  			c.incomingEmail(msg2.Sender, "#syz upstream")
   496  			msg2 = c.pollEmailBug()
   497  			c.expectEQ(msg2.Subject, "[syzbot] KASAN: another bad thing")
   498  
   499  			cc := EmailOptCC([]string{"bugs@syzkaller.com", "default@maintainers.com"})
   500  			switch i {
   501  			case 0:
   502  				c.incomingEmail(msg2.Sender, "#syz dup: BUG: something bad", cc)
   503  			case 1:
   504  				c.incomingEmail(msg2.Sender, "#syz dup: [syzbot] BUG: something bad", cc)
   505  			case 2:
   506  				c.incomingEmail(msg2.Sender, "#syz dup: [syzbot] [subsystemA?] BUG: something bad", cc)
   507  			default:
   508  				c.incomingEmail(msg2.Sender, "#syz dup: syzbot: BUG: something bad", cc)
   509  				reply := c.pollEmailBug()
   510  				c.expectTrue(strings.Contains(reply.Body, "can't find the dup bug"))
   511  			}
   512  		})
   513  	}
   514  }
   515  
   516  func TestEmailUndup(t *testing.T) {
   517  	c := NewCtx(t)
   518  	defer c.Close()
   519  
   520  	build := testBuild(1)
   521  	c.client2.UploadBuild(build)
   522  
   523  	crash1 := testCrash(build, 1)
   524  	crash1.Title = "BUG: slightly more elaborate title"
   525  	c.client2.ReportCrash(crash1)
   526  
   527  	crash2 := testCrash(build, 2)
   528  	crash1.Title = "KASAN: another title"
   529  	c.client2.ReportCrash(crash2)
   530  
   531  	msg1 := c.pollEmailBug()
   532  	msg2 := c.pollEmailBug()
   533  
   534  	// Dup crash2 to crash1.
   535  	c.incomingEmail(msg2.Sender, "#syz dup BUG: slightly more elaborate title")
   536  	c.expectNoEmail()
   537  
   538  	// Undup crash2.
   539  	c.incomingEmail(msg2.Sender, "#syz undup")
   540  	c.expectNoEmail()
   541  
   542  	// Now close the original bug, and check that new crashes for the dup does not create bugs.
   543  	c.incomingEmail(msg1.Sender, "#syz invalid")
   544  	c.client2.ReportCrash(crash2)
   545  	c.expectNoEmail()
   546  }
   547  
   548  func TestEmailCrossReportingDup(t *testing.T) {
   549  	c := NewCtx(t)
   550  	defer c.Close()
   551  
   552  	build := testBuild(1)
   553  	c.client2.UploadBuild(build)
   554  
   555  	tests := []struct {
   556  		bug    int
   557  		dup    int
   558  		result bool
   559  	}{
   560  		{0, 0, true},
   561  		{0, 1, false},
   562  		{0, 2, false},
   563  		{1, 0, false},
   564  		{1, 1, true},
   565  		{1, 2, true},
   566  		{2, 0, false},
   567  		{2, 1, false},
   568  		{2, 2, true},
   569  	}
   570  	for i, test := range tests {
   571  		t.Logf("duping %v->%v, expect %v", test.bug, test.dup, test.result)
   572  		c.advanceTime(24 * time.Hour) // to not hit email limit per day
   573  		crash1 := testCrash(build, 1)
   574  		crash1.Title = fmt.Sprintf("bug_%v", i)
   575  		c.client2.ReportCrash(crash1)
   576  		bugSender := c.pollEmailBug().Sender
   577  		cc := EmailOptCC([]string{"default@maintainers.com", "test@syzkaller.com",
   578  			"bugs@syzkaller.com", "default2@maintainers.com", "bugs2@syzkaller.com"})
   579  		for j := 0; j < test.bug; j++ {
   580  			c.incomingEmail(bugSender, "#syz upstream", cc)
   581  			bugSender = c.pollEmailBug().Sender
   582  		}
   583  
   584  		crash2 := testCrash(build, 2)
   585  		crash2.Title = fmt.Sprintf("dup_%v", i)
   586  		c.client2.ReportCrash(crash2)
   587  		dupSender := c.pollEmailBug().Sender
   588  		for j := 0; j < test.dup; j++ {
   589  			c.incomingEmail(dupSender, "#syz upstream", cc)
   590  			dupSender = c.pollEmailBug().Sender
   591  		}
   592  
   593  		c.incomingEmail(bugSender, "#syz dup: "+crash2.Title, cc)
   594  		if test.result {
   595  			c.expectNoEmail()
   596  		} else {
   597  			msg := c.pollEmailBug()
   598  			if !strings.Contains(msg.Body, "> #syz dup:") ||
   599  				!strings.Contains(msg.Body, "Can't dup bug to a bug in different reporting") {
   600  				c.t.Fatalf("bad reply body:\n%v", msg.Body)
   601  			}
   602  		}
   603  	}
   604  }
   605  
   606  func TestEmailErrors(t *testing.T) {
   607  	c := NewCtx(t)
   608  	defer c.Close()
   609  
   610  	// No reply for email without bug hash and no commands.
   611  	c.incomingEmail("syzbot@testapp.appspotmail.com", "Investment Proposal")
   612  	c.expectNoEmail()
   613  
   614  	// If email contains a command we need to reply.
   615  	c.incomingEmail("syzbot@testapp.appspotmail.com", "#syz invalid")
   616  	reply := c.pollEmailBug()
   617  	c.expectEQ(reply.To, []string{"default@sender.com"})
   618  	c.expectEQ(reply.Body, `> #syz invalid
   619  
   620  I see the command but can't find the corresponding bug.
   621  Please resend the email to syzbot+HASH@testapp.appspotmail.com address
   622  that is the sender of the bug report (also present in the Reported-by tag).
   623  
   624  `)
   625  
   626  	c.incomingEmail("syzbot+123@testapp.appspotmail.com", "#syz invalid")
   627  	reply = c.pollEmailBug()
   628  	c.expectEQ(reply.Body, `> #syz invalid
   629  
   630  I see the command but can't find the corresponding bug.
   631  The email is sent to  syzbot+HASH@testapp.appspotmail.com address
   632  but the HASH does not correspond to any known bug.
   633  Please double check the address.
   634  
   635  `)
   636  }
   637  
   638  func TestEmailFailedBuild(t *testing.T) {
   639  	c := NewCtx(t)
   640  	defer c.Close()
   641  
   642  	build := testBuild(1)
   643  	c.client2.UploadBuild(build)
   644  
   645  	failedBuild := testBuild(10)
   646  	failedBuild.KernelRepo, failedBuild.KernelBranch = testConfig.Namespaces["test2"].mainRepoBranch()
   647  	failedBuild.KernelCommit = "kern2"
   648  	failedBuild.KernelCommitTitle = "failed build 1"
   649  	failedBuild.SyzkallerCommit = "syz2"
   650  	buildErrorReq := &dashapi.BuildErrorReq{
   651  		Build: *failedBuild,
   652  		Crash: dashapi.Crash{
   653  			Title:       "failed build 1",
   654  			Report:      []byte("report line 1\nreport line 2\n"),
   655  			Log:         []byte("log line 1\nlog line 2\n"),
   656  			Maintainers: []string{"maintainer@crash"},
   657  		},
   658  	}
   659  	c.expectOK(c.client2.ReportBuildError(buildErrorReq))
   660  
   661  	msg := c.pollEmailBug()
   662  	sender, extBugID, err := email.RemoveAddrContext(msg.Sender)
   663  	c.expectOK(err)
   664  	_, dbCrash, dbBuild := c.loadBug(extBugID)
   665  	crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   666  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   667  	c.expectEQ(sender, fromAddr(c.ctx))
   668  	c.expectEQ(msg.To, []string{
   669  		"always@cc.me",
   670  		"test@syzkaller.com",
   671  	})
   672  	c.expectEQ(msg.Subject, buildErrorReq.Crash.Title)
   673  	c.expectEQ(len(msg.Attachments), 0)
   674  	c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   675  
   676  syzbot found the following issue on:
   677  
   678  HEAD commit:    kern2 failed build 1
   679  git tree:       repo10alias
   680  console output: %[2]v
   681  kernel config:  %[3]v
   682  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   683  compiler:       compiler10
   684  CC:             [maintainer@crash maintainers@repo10.org bugs@repo10.org build-maintainers@repo10.org]
   685  
   686  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   687  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   688  
   689  report line 1
   690  report line 2
   691  
   692  
   693  ---
   694  This report is generated by a bot. It may contain errors.
   695  See https://goo.gl/tpsmEJ for more information about syzbot.
   696  syzbot engineers can be reached at syzkaller@googlegroups.com.
   697  
   698  syzbot will keep track of this issue. See:
   699  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   700  
   701  If the report is already addressed, let syzbot know by replying with:
   702  #syz fix: exact-commit-title
   703  
   704  If you want to overwrite report's subsystems, reply with:
   705  #syz set subsystems: new-subsystem
   706  (See the list of subsystem names on the web dashboard)
   707  
   708  If the report is a duplicate of another one, reply with:
   709  #syz dup: exact-subject-of-another-report
   710  
   711  If you want to undo deduplication, reply with:
   712  #syz undup`,
   713  		extBugID, crashLogLink, kernelConfigLink))
   714  }
   715  
   716  // Test for unfix command which should unmark a bug as fixed by any commits.
   717  func TestEmailUnfix(t *testing.T) {
   718  	c := NewCtx(t)
   719  	defer c.Close()
   720  
   721  	build := testBuild(1)
   722  	c.client2.UploadBuild(build)
   723  
   724  	crash := testCrash(build, 1)
   725  	c.client2.ReportCrash(crash)
   726  
   727  	msg := c.pollEmailBug()
   728  
   729  	c.incomingEmail(msg.Sender, "#syz fix: some commit")
   730  	c.expectNoEmail()
   731  	c.incomingEmail(msg.Sender, "#syz unfix")
   732  	c.expectNoEmail()
   733  
   734  	build2 := testBuild(2)
   735  	build2.Manager = build.Manager
   736  	build2.Commits = []string{"some commit"}
   737  	c.client2.UploadBuild(build2)
   738  
   739  	// The bug should be still unfixed, since we unmarked it.
   740  	c.client2.ReportCrash(crash)
   741  	c.expectNoEmail()
   742  }
   743  
   744  func TestEmailManagerCC(t *testing.T) {
   745  	c := NewCtx(t)
   746  	defer c.Close()
   747  
   748  	// Test that we add manager CC.
   749  	build1 := testBuild(1)
   750  	build1.Manager = specialCCManager
   751  	c.client2.UploadBuild(build1)
   752  
   753  	crash := testCrash(build1, 1)
   754  	c.client2.ReportCrash(crash)
   755  
   756  	msg := c.pollEmailBug()
   757  	c.expectEQ(msg.To, []string{
   758  		"always@manager.org",
   759  		"test@syzkaller.com",
   760  	})
   761  
   762  	// Test that we add manager maintainers.
   763  	c.incomingEmail(msg.Sender, "#syz upstream")
   764  	msg = c.pollEmailBug()
   765  	c.expectEQ(msg.To, []string{
   766  		"always@manager.org",
   767  		"bugs@syzkaller.com",
   768  		"default@maintainers.com",
   769  		"maintainers@manager.org",
   770  	})
   771  
   772  	// Test that we add manager build maintainers.
   773  	build2 := testBuild(2)
   774  	build2.Manager = specialCCManager
   775  	buildErrorReq := &dashapi.BuildErrorReq{
   776  		Build: *build2,
   777  		Crash: dashapi.Crash{
   778  			Title:  "failed build 1",
   779  			Report: []byte("report\n"),
   780  			Log:    []byte("log\n"),
   781  		},
   782  	}
   783  	c.expectOK(c.client2.ReportBuildError(buildErrorReq))
   784  	msg = c.pollEmailBug()
   785  	c.expectEQ(msg.To, []string{
   786  		"always@manager.org",
   787  		"test@syzkaller.com",
   788  	})
   789  
   790  	c.incomingEmail(msg.Sender, "#syz upstream")
   791  	msg = c.pollEmailBug()
   792  	c.expectEQ(msg.To, []string{
   793  		"always@manager.org",
   794  		"bugs@syzkaller.com",
   795  		"build-maintainers@manager.org",
   796  		"default@maintainers.com",
   797  		"maintainers@manager.org",
   798  	})
   799  
   800  	// Test that we don't add manager CC when the crash happened on 1+ managers.
   801  	build3 := testBuild(3)
   802  	build1.Manager = specialCCManager
   803  	c.client2.UploadBuild(build3)
   804  	crash = testCrash(build3, 2)
   805  	c.client2.ReportCrash(crash)
   806  
   807  	build4 := testBuild(4)
   808  	c.client2.UploadBuild(build4)
   809  	crash = testCrash(build4, 2)
   810  	c.client2.ReportCrash(crash)
   811  
   812  	msg = c.pollEmailBug()
   813  	c.expectEQ(msg.To, []string{
   814  		"test@syzkaller.com",
   815  	})
   816  
   817  	c.incomingEmail(msg.Sender, "#syz upstream")
   818  	msg = c.pollEmailBug()
   819  	c.expectEQ(msg.To, []string{
   820  		"bugs@syzkaller.com",
   821  		"default@maintainers.com",
   822  	})
   823  }
   824  
   825  func TestStraceReport(t *testing.T) {
   826  	c := NewCtx(t)
   827  	defer c.Close()
   828  
   829  	build := testBuild(1)
   830  	c.client2.UploadBuild(build)
   831  
   832  	crash := testCrash(build, 1)
   833  	crash.Flags = dashapi.CrashUnderStrace
   834  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`}
   835  	c.client2.ReportCrash(crash)
   836  
   837  	// Report the crash over email and check all fields.
   838  	msg := c.pollEmailBug()
   839  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
   840  	c.expectOK(err)
   841  	_, dbCrash, dbBuild := c.loadBug(extBugID)
   842  	crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log)
   843  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   844  	c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   845  
   846  syzbot found the following issue on:
   847  
   848  HEAD commit:    111111111111 kernel_commit_title1
   849  git tree:       repo1 branch1
   850  console+strace: %[2]v
   851  kernel config:  %[3]v
   852  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   853  compiler:       compiler1
   854  CC:             [bar@foo.com foo@bar.com idont@want.EMAILS]
   855  
   856  Unfortunately, I don't have any reproducer for this issue yet.
   857  
   858  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   859  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   860  
   861  report1
   862  
   863  ---
   864  This report is generated by a bot. It may contain errors.
   865  See https://goo.gl/tpsmEJ for more information about syzbot.
   866  syzbot engineers can be reached at syzkaller@googlegroups.com.
   867  
   868  syzbot will keep track of this issue. See:
   869  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   870  
   871  If the report is already addressed, let syzbot know by replying with:
   872  #syz fix: exact-commit-title
   873  
   874  If you want to overwrite report's subsystems, reply with:
   875  #syz set subsystems: new-subsystem
   876  (See the list of subsystem names on the web dashboard)
   877  
   878  If the report is a duplicate of another one, reply with:
   879  #syz dup: exact-subject-of-another-report
   880  
   881  If you want to undo deduplication, reply with:
   882  #syz undup`,
   883  		extBugID, crashLogLink, kernelConfigLink))
   884  	c.checkURLContents(crashLogLink, crash.Log)
   885  }
   886  
   887  func TestSubjectTitleParser(t *testing.T) {
   888  	tests := []struct {
   889  		inSubject string
   890  		outTitle  string
   891  		outSeq    int64
   892  	}{
   893  		{
   894  			inSubject: "Re: kernel BUG in blk_mq_dispatch_rq_list (4)",
   895  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   896  			outSeq:    3,
   897  		},
   898  		{
   899  			inSubject: "Re: [syzbot] kernel BUG in blk_mq_dispatch_rq_list (4)",
   900  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   901  			outSeq:    3,
   902  		},
   903  		{
   904  			// Make sure we always take the (number) at the end.
   905  			inSubject: "Re: kernel BUG in blk_mq_dispatch_rq_list(6) (4)",
   906  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list(6)",
   907  			outSeq:    3,
   908  		},
   909  		{
   910  			inSubject: "RE: kernel BUG in blk_mq_dispatch_rq_list",
   911  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   912  			outSeq:    0,
   913  		},
   914  		{
   915  			// Make sure we trim the title.
   916  			inSubject: "RE:  kernel BUG in blk_mq_dispatch_rq_list ",
   917  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   918  			outSeq:    0,
   919  		},
   920  		{
   921  			inSubject: "Re: ",
   922  			outTitle:  "",
   923  			outSeq:    0,
   924  		},
   925  		{
   926  			inSubject: "Re: [syzbot]",
   927  			outTitle:  "",
   928  			outSeq:    0,
   929  		},
   930  		{
   931  			inSubject: "Re: [syzbot] [subsystemA?]",
   932  			outTitle:  "",
   933  			outSeq:    0,
   934  		},
   935  		{
   936  			// Make sure we delete filesystem tags.
   937  			inSubject: "Re: [syzbot] [ntfs3?] [ext4?] kernel BUG in blk_mq_dispatch_rq_list (4)",
   938  			outTitle:  "kernel BUG in blk_mq_dispatch_rq_list",
   939  			outSeq:    3,
   940  		},
   941  	}
   942  
   943  	p := makeSubjectTitleParser(context.Background())
   944  	for _, test := range tests {
   945  		title, seq, err := p.parseTitle(test.inSubject)
   946  		if test.outTitle == "" {
   947  			if err == nil {
   948  				t.Fatalf("subj: %q, expected error, got none (%q)", test.inSubject, title)
   949  			}
   950  		} else if title != test.outTitle {
   951  			t.Fatalf("subj: %q, expected title=%q, got %q", test.inSubject, test.outTitle, title)
   952  		} else if seq != test.outSeq {
   953  			t.Fatalf("subj: %q, expected seq=%q, got %q", test.inSubject, test.outSeq, seq)
   954  		}
   955  	}
   956  }
   957  
   958  func TestBugFromSubjectInference(t *testing.T) {
   959  	c := NewCtx(t)
   960  	defer c.Close()
   961  
   962  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
   963  	client2 := c.makeClient(clientPublicEmail2, keyPublicEmail2, true)
   964  
   965  	build := testBuild(1)
   966  	client.UploadBuild(build)
   967  
   968  	build2 := testBuild(2)
   969  	client2.UploadBuild(build2)
   970  
   971  	const crashTitle = "WARNING in corrupted"
   972  	upstreamCrash := func(client *apiClient, build *dashapi.Build, title string) string {
   973  		// Upload some garbage crashes.
   974  		crash := testCrash(build, 1)
   975  		crash.Title = title
   976  		crash.Log = []byte(fmt.Sprintf("log%v", title))
   977  		crash.Maintainers = []string{"maintainer@kernel.org"}
   978  		client.ReportCrash(crash)
   979  
   980  		sender := c.pollEmailBug().Sender
   981  		c.incomingEmail(sender, "#syz upstream\n")
   982  
   983  		return c.pollEmailBug().Sender
   984  	}
   985  
   986  	upstreamCrash(client, build, "unrelated crash")
   987  	origSender := upstreamCrash(client, build, crashTitle)
   988  	upstreamCrash(client, build, "unrelated crash 2")
   989  
   990  	mailingList := "<" + c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email + ">"
   991  
   992  	// First try to ping some non-existing bug.
   993  	subject := "Re: unknown-bug"
   994  	c.incomingEmail("bugs@syzkaller.com",
   995  		syzTestGitBranchSamplePatch,
   996  		EmailOptOrigFrom("test@requester.com"),
   997  		EmailOptFrom(mailingList), EmailOptSubject(subject),
   998  	)
   999  	syzbotReply := c.pollEmailBug()
  1000  	c.expectNE(syzbotReply.Sender, origSender)
  1001  	c.expectEQ(strings.Contains(syzbotReply.Body, "can't find the corresponding bug"), true)
  1002  
  1003  	// Now try to test the exiting bug, but with the wrong mailing list.
  1004  	subject = "Re: " + crashTitle
  1005  	c.incomingEmail("bugs@syzkaller.com",
  1006  		syzTestGitBranchSamplePatch,
  1007  		EmailOptOrigFrom("test@requester.com"),
  1008  		EmailOptFrom("<unknown-list@syzkaller.com>"), EmailOptSubject(subject),
  1009  	)
  1010  	body := c.pollEmailBug().Body
  1011  	c.expectEQ(strings.Contains(body, "can't find the corresponding bug"), true)
  1012  
  1013  	// Now try to test the exiting bug with the proper mailing list.
  1014  	c.incomingEmail("bugs@syzkaller.com",
  1015  		syzTestGitBranchSamplePatch,
  1016  		EmailOptFrom(mailingList), EmailOptOrigFrom("test@requester.com"),
  1017  		EmailOptSubject(subject),
  1018  	)
  1019  	syzbotReply = c.pollEmailBug()
  1020  	c.expectEQ(syzbotReply.Sender, origSender)
  1021  	c.expectEQ(strings.Contains(syzbotReply.Body, "This crash does not have a reproducer"), true)
  1022  
  1023  	// Test that a different type of email headers is also parsed fine.
  1024  	c.incomingEmail("bugs@syzkaller.com",
  1025  		syzTestGitBranchSamplePatch,
  1026  		EmailOptSender(mailingList), EmailOptFrom("test@requester.com"),
  1027  		EmailOptSubject(subject),
  1028  	)
  1029  	body = c.pollEmailBug().Body
  1030  	c.expectEQ(strings.Contains(body, "This crash does not have a reproducer"), true)
  1031  
  1032  	// Upstream a same-titled bug in another namespace.
  1033  	upstreamCrash(client2, build2, crashTitle)
  1034  
  1035  	// Ensure that the inference fails with the proper title.
  1036  	c.incomingEmail("bugs@syzkaller.com",
  1037  		syzTestGitBranchSamplePatch,
  1038  		EmailOptSender(mailingList), EmailOptFrom("test@requester.com"),
  1039  		EmailOptSubject(subject),
  1040  	)
  1041  	body = c.pollEmailBug().Body
  1042  	c.expectEQ(strings.Contains(body, "Several bugs with the exact same title"), true)
  1043  
  1044  	// Close the existing bug.
  1045  	c.incomingEmail("bugs@syzkaller.com", "#syz invalid",
  1046  		EmailOptFrom("test@requester.com"), EmailOptSubject(subject),
  1047  		EmailOptCC([]string{mailingList, origSender}),
  1048  	)
  1049  	c.expectNoEmail()
  1050  
  1051  	// Create the (2) of the bug.
  1052  	upstreamCrash(client, build, crashTitle)
  1053  
  1054  	// Make sure syzbot can understand the (2) version.
  1055  	subject = "Re: " + crashTitle + " (2)"
  1056  	c.incomingEmail("bugs@syzkaller.com",
  1057  		syzTestGitBranchSamplePatch,
  1058  		EmailOptFrom(mailingList), EmailOptOrigFrom("<test@requester.com>"),
  1059  		EmailOptSubject(subject),
  1060  	)
  1061  	email := c.pollEmailBug()
  1062  	c.expectEQ(email.To, []string{"test@requester.com"})
  1063  	c.expectEQ(strings.Contains(email.Body, "This crash does not have a reproducer"), true)
  1064  }
  1065  
  1066  // nolint: funlen
  1067  func TestEmailLinks(t *testing.T) {
  1068  	c := NewCtx(t)
  1069  	defer c.Close()
  1070  
  1071  	build := testBuild(1)
  1072  	c.client2.UploadBuild(build)
  1073  
  1074  	crash := testCrash(build, 1)
  1075  	crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`}
  1076  	c.client2.ReportCrash(crash)
  1077  
  1078  	// Report the crash over email.
  1079  	msg := c.pollEmailBug()
  1080  
  1081  	// Emulate receive of the report from a mailing list.
  1082  	// This should update the bug with the link/Message-ID.
  1083  	// nolint: lll
  1084  	incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
  1085  Date: Tue, 15 Aug 2017 14:59:00 -0700
  1086  Message-ID: <1234>
  1087  Subject: crash1
  1088  From: %v
  1089  To: foo@bar.com
  1090  Content-Type: text/plain
  1091  
  1092  Hello
  1093  
  1094  syzbot will keep track of this issue.
  1095  If you forgot to add the Reported-by tag, once the fix for this bug is merged
  1096  into any tree, please reply to this email with:
  1097  #syz fix: exact-commit-title
  1098  To mark this as a duplicate of another syzbot report, please reply with:
  1099  #syz dup: exact-subject-of-another-report
  1100  If it's a one-off invalid bug report, please reply with:
  1101  #syz invalid
  1102  
  1103  -- 
  1104  You received this message because you are subscribed to the Google Groups "syzkaller" group.
  1105  To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
  1106  To post to this group, send email to syzkaller@googlegroups.com.
  1107  To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com.
  1108  For more options, visit https://groups.google.com/d/optout.
  1109  `, msg.Sender)
  1110  
  1111  	_, err := c.POST("/_ah/mail/", incoming1)
  1112  	c.expectOK(err)
  1113  
  1114  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
  1115  	c.expectOK(err)
  1116  
  1117  	// Make sure Link is set for the last Reporting.
  1118  	dbBug, _, _ := c.loadBug(extBugID)
  1119  	reporting := lastReportedReporting(dbBug)
  1120  	c.expectNE(reporting.Link, "")
  1121  }
  1122  
  1123  func TestEmailPatchTestingAccess(t *testing.T) {
  1124  	c := NewCtx(t)
  1125  	defer c.Close()
  1126  
  1127  	client := c.client2
  1128  
  1129  	build := testBuild(1)
  1130  	client.UploadBuild(build)
  1131  
  1132  	crash := testCrash(build, 1)
  1133  	client.ReportCrash(crash)
  1134  
  1135  	sender := c.pollEmailBug().Sender
  1136  	c.incomingEmail(sender,
  1137  		syzTestGitBranchSamplePatch,
  1138  		EmailOptFrom("user@kernel.org"), EmailOptSubject("Re: "+crash.Title),
  1139  	)
  1140  
  1141  	// We expect syzbot to just ignore this patch testing request.
  1142  	c.expectNoEmail()
  1143  
  1144  	// The patch test job should also not be created.
  1145  	pollResp := client.pollJobs(build.Manager)
  1146  	c.expectEQ(pollResp.ID, "")
  1147  }
  1148  
  1149  func TestEmailSetInvalidSubsystems(t *testing.T) {
  1150  	c := NewCtx(t)
  1151  	defer c.Close()
  1152  
  1153  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1154  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1155  
  1156  	build := testBuild(1)
  1157  	client.UploadBuild(build)
  1158  
  1159  	crash := testCrash(build, 1)
  1160  	client.ReportCrash(crash)
  1161  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1162  
  1163  	sender := c.pollEmailBug().Sender
  1164  
  1165  	// Invalid subsystem name.
  1166  	c.incomingEmail(sender, "#syz set subsystems: non-existent",
  1167  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1168  	c.expectEQ(c.pollEmailBug().Body, `> #syz set subsystems: non-existent
  1169  
  1170  The specified label value is incorrect.
  1171  "non-existent" is not among the allowed values.
  1172  Please use one of the supported label values.
  1173  
  1174  The following labels are suported:
  1175  missing-backport, no-reminders, prio: {low, normal, high}, subsystems: {.. see below ..}
  1176  The list of subsystems: https://testapp.appspot.com/access-public-email/subsystems?all=true
  1177  
  1178  `)
  1179  }
  1180  
  1181  func TestEmailSetSubsystems(t *testing.T) {
  1182  	c := NewCtx(t)
  1183  	defer c.Close()
  1184  
  1185  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1186  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1187  
  1188  	build := testBuild(1)
  1189  	client.UploadBuild(build)
  1190  
  1191  	crash := testCrash(build, 1)
  1192  	client.ReportCrash(crash)
  1193  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1194  
  1195  	sender := c.pollEmailBug().Sender
  1196  	_, extBugID, err := email.RemoveAddrContext(sender)
  1197  	c.expectOK(err)
  1198  
  1199  	// At the beginning, there are no subsystems.
  1200  	expectLabels(t, client, extBugID)
  1201  
  1202  	// Set one subsystem.
  1203  	c.incomingEmail(sender, "#syz set subsystems: subsystemA\n",
  1204  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1205  	expectLabels(t, client, extBugID, "subsystems:subsystemA")
  1206  
  1207  	// Set two subsystems.
  1208  	c.incomingEmail(sender, "#syz set subsystems: subsystemA, subsystemB\n",
  1209  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1210  	expectLabels(t, client, extBugID, "subsystems:subsystemA", "subsystems:subsystemB")
  1211  }
  1212  
  1213  func TestEmailBugLabels(t *testing.T) {
  1214  	c := NewCtx(t)
  1215  	defer c.Close()
  1216  
  1217  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1218  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1219  
  1220  	build := testBuild(1)
  1221  	client.UploadBuild(build)
  1222  
  1223  	crash := testCrash(build, 1)
  1224  	client.ReportCrash(crash)
  1225  
  1226  	sender := c.pollEmailBug().Sender
  1227  	_, extBugID, err := email.RemoveAddrContext(sender)
  1228  	c.expectOK(err)
  1229  
  1230  	// At the beginning, there are no tags.
  1231  	expectLabels(t, client, extBugID)
  1232  
  1233  	// Set a tag.
  1234  	c.incomingEmail(sender, "#syz set prio: low\n",
  1235  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1236  	expectLabels(t, client, extBugID, "prio:low")
  1237  
  1238  	// Notice that medium prio supercedes low prio since they are of the oneOf type.
  1239  	c.incomingEmail(sender, "#syz set prio: high\n",
  1240  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1241  	expectLabels(t, client, extBugID, "prio:high")
  1242  
  1243  	// Also set a flag label.
  1244  	c.incomingEmail(sender, "#syz set no-reminders\n",
  1245  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1246  	expectLabels(t, client, extBugID, "prio:high", "no-reminders")
  1247  
  1248  	// Remove a tag.
  1249  	c.incomingEmail(sender, "#syz unset prio\n",
  1250  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1251  	expectLabels(t, client, extBugID, "no-reminders")
  1252  
  1253  	// Remove another tag.
  1254  	c.incomingEmail(sender, "#syz unset no-reminders\n",
  1255  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1256  	expectLabels(t, client, extBugID)
  1257  }
  1258  
  1259  func TestInvalidEmailBugLabels(t *testing.T) {
  1260  	c := NewCtx(t)
  1261  	defer c.Close()
  1262  
  1263  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1264  	mailingList := c.config().Namespaces["access-public-email"].Reporting[0].Config.(*EmailConfig).Email
  1265  
  1266  	build := testBuild(1)
  1267  	client.UploadBuild(build)
  1268  
  1269  	crash := testCrash(build, 1)
  1270  	client.ReportCrash(crash)
  1271  	c.incomingEmail(c.pollEmailBug().Sender, "#syz upstream\n")
  1272  
  1273  	sender := c.pollEmailBug().Sender
  1274  
  1275  	// Non-existing label.
  1276  	c.incomingEmail(sender, "#syz set label: tag",
  1277  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1278  	body := c.pollEmailBug().Body
  1279  	c.expectEQ(body, `> #syz set label: tag
  1280  
  1281  The specified label "label" is unknown.
  1282  Please use one of the supported labels.
  1283  
  1284  The following labels are suported:
  1285  missing-backport, no-reminders, prio: {low, normal, high}, subsystems: {.. see below ..}
  1286  The list of subsystems: https://testapp.appspot.com/access-public-email/subsystems?all=true
  1287  
  1288  `)
  1289  
  1290  	// Existing label, wrong value.
  1291  	c.incomingEmail(sender, "#syz set prio: unknown\n",
  1292  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1293  	c.expectEQ(strings.Contains(c.pollEmailBug().Body,
  1294  		`The specified label value is incorrect.
  1295  "unknown" is not among the allowed values`), true)
  1296  
  1297  	// Existing label, too many values.
  1298  	c.incomingEmail(sender, "#syz set prio: low, high\n",
  1299  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1300  	c.expectEQ(strings.Contains(c.pollEmailBug().Body,
  1301  		`The specified label value is incorrect.
  1302  You must specify only one of the allowed values.`), true)
  1303  
  1304  	// Removing a non-existing label.
  1305  	c.incomingEmail(sender, "#syz unset tag2\n",
  1306  		EmailOptFrom("test@requester.com"), EmailOptCC([]string{mailingList}))
  1307  	syzbotReply := c.pollEmailBug()
  1308  	c.expectEQ(strings.Contains(syzbotReply.Body, "The following labels did not exist: tag2"), true)
  1309  }
  1310  
  1311  func expectLabels(t *testing.T, client *apiClient, extID string, labels ...string) {
  1312  	t.Helper()
  1313  	bug, _, _ := client.Ctx.loadBug(extID)
  1314  	names := []string{}
  1315  	for _, item := range bug.Labels {
  1316  		names = append(names, item.String())
  1317  	}
  1318  	assert.ElementsMatch(t, names, labels)
  1319  }
  1320  
  1321  var forwardEmailConfig = EmailConfig{
  1322  	Email:              "test@syzkaller.com",
  1323  	HandleListEmails:   true,
  1324  	SubjectPrefix:      "[syzbot]",
  1325  	MailMaintainers:    true,
  1326  	DefaultMaintainers: []string{"some@list.com"},
  1327  }
  1328  
  1329  func TestSingleListForward(t *testing.T) {
  1330  	c := NewCtx(t)
  1331  	defer c.Close()
  1332  
  1333  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1334  	c.updateReporting("access-public-email", "access-public-email-reporting1",
  1335  		func(r Reporting) Reporting {
  1336  			r.Config = &forwardEmailConfig
  1337  			return r
  1338  		})
  1339  
  1340  	build := testBuild(1)
  1341  	client.UploadBuild(build)
  1342  
  1343  	crash := testCrash(build, 1)
  1344  	client.ReportCrash(crash)
  1345  
  1346  	sender := c.pollEmailBug().Sender
  1347  
  1348  	c.incomingEmail(sender, "#syz fix: some: commit title",
  1349  		EmailOptCC([]string{"some@list.com"}), EmailOptSubject("fix bug title"))
  1350  
  1351  	forwarded := c.pollEmailBug()
  1352  	c.expectEQ(forwarded.Subject, "Forwarded: fix bug title")
  1353  	c.expectEQ(forwarded.Sender, sender)
  1354  	c.expectEQ(forwarded.To, []string{"test@syzkaller.com"})
  1355  	c.expectEQ(len(forwarded.Cc), 0)
  1356  	c.expectEQ(forwarded.Body, `For archival purposes, forwarding an incoming command email to
  1357  test@syzkaller.com.
  1358  
  1359  ***
  1360  
  1361  Subject: fix bug title
  1362  Author: default@sender.com
  1363  
  1364  #syz fix: some: commit title
  1365  `)
  1366  }
  1367  
  1368  func TestTwoListsForward(t *testing.T) {
  1369  	c := NewCtx(t)
  1370  	defer c.Close()
  1371  
  1372  	client := c.makeClient(clientPublicEmail, keyPublicEmail, true)
  1373  	c.updateReporting("access-public-email", "access-public-email-reporting1",
  1374  		func(r Reporting) Reporting {
  1375  			r.Config = &forwardEmailConfig
  1376  			return r
  1377  		})
  1378  
  1379  	build := testBuild(1)
  1380  	client.UploadBuild(build)
  1381  
  1382  	crash := testCrash(build, 1)
  1383  	client.ReportCrash(crash)
  1384  
  1385  	sender := c.pollEmailBug().Sender
  1386  
  1387  	c.incomingEmail(sender, "#syz fix: some: commit title",
  1388  		EmailOptCC(nil), EmailOptSubject("fix bug title"))
  1389  
  1390  	forwarded := c.pollEmailBug()
  1391  	c.expectEQ(forwarded.Subject, "Forwarded: fix bug title")
  1392  	c.expectEQ(forwarded.Sender, sender)
  1393  	c.expectEQ(forwarded.To, []string{"some@list.com", "test@syzkaller.com"})
  1394  	c.expectEQ(len(forwarded.Cc), 0)
  1395  	c.expectEQ(forwarded.Body, `For archival purposes, forwarding an incoming command email to
  1396  some@list.com, test@syzkaller.com.
  1397  
  1398  ***
  1399  
  1400  Subject: fix bug title
  1401  Author: default@sender.com
  1402  
  1403  #syz fix: some: commit title
  1404  `)
  1405  }
  1406  
  1407  func TestForwardEmailInbox(t *testing.T) {
  1408  	c := NewCtx(t)
  1409  	defer c.Close()
  1410  
  1411  	c.transformContext = func(c context.Context) context.Context {
  1412  		newConfig := *getConfig(c)
  1413  		newConfig.MonitoredInboxes = []*PerInboxConfig{
  1414  			{
  1415  				InboxRe:   `^syzbot\+prefix.*@testapp\.appspotmail\.com$`,
  1416  				ForwardTo: []string{`forward@a.com`, `forward@b.com`},
  1417  			},
  1418  		}
  1419  		return contextWithConfig(c, &newConfig)
  1420  	}
  1421  
  1422  	t.Run("forwarded", func(t *testing.T) {
  1423  		from := "syzbot+prefixABCD@testapp.appspotmail.com"
  1424  		c.incomingEmail(from,
  1425  			"#syz invalid",
  1426  			EmailOptSubject("test subject"),
  1427  			EmailOptMessageID(1),
  1428  			EmailOptFrom("someone@mail.com"),
  1429  			EmailOptCC([]string{"some@list.com"}))
  1430  		msg := c.pollEmailBug()
  1431  		require.NotNil(t, msg)
  1432  		assert.Equal(t, `"syzbot" <syzbot@testapp.appspotmail.com>`, msg.Sender)
  1433  		assert.Equal(t, "Forwarded: test subject", msg.Subject)
  1434  		assert.ElementsMatch(t, []string{"forward@a.com", "forward@b.com"},
  1435  			msg.To, "must be sent to the author and the missing lists")
  1436  		assert.ElementsMatch(t, []string{"\"syzbot\" <" + from + ">", "someone@mail.com"}, msg.Cc)
  1437  		assert.Equal(t, "<1>", msg.Headers.Get("In-Reply-To"))
  1438  		assert.Equal(t, `For archival purposes, forwarding an incoming command email to
  1439  forward@a.com, forward@b.com.
  1440  
  1441  ***
  1442  
  1443  Subject: test subject
  1444  Author: someone@mail.com
  1445  
  1446  #syz invalid
  1447  `, msg.Body)
  1448  
  1449  		t.Run("no-loop", func(t *testing.T) {
  1450  			// Ensure that we don't react to replies.
  1451  			c.incomingEmail("syzbot@testapp.appspotmail.com", msg.Body,
  1452  				EmailOptFrom("syzbot@testapp.appspotmail.com"),
  1453  				EmailOptCC(append(append([]string{}, msg.Cc...), msg.To...)))
  1454  			c.expectNoEmail()
  1455  		})
  1456  	})
  1457  
  1458  	t.Run("no command", func(t *testing.T) {
  1459  		c.incomingEmail("syzbot+prefixABCD@testapp.appspotmail.com",
  1460  			"Some spam message",
  1461  			EmailOptMessageID(1),
  1462  			EmailOptFrom("someone@mail.com"))
  1463  		c.expectNoEmail()
  1464  	})
  1465  
  1466  	t.Run("unrelated", func(t *testing.T) {
  1467  		// It will react as if the email targeted the bug ABCD.
  1468  		c.incomingEmail("syzbot+ABCD@testapp.appspotmail.com",
  1469  			"#syz invalid",
  1470  			EmailOptMessageID(1),
  1471  			EmailOptFrom("someone@mail.com"),
  1472  			EmailOptCC([]string{"some@list.com"}))
  1473  		msg := c.pollEmailBug()
  1474  		require.NotNil(t, msg)
  1475  		assert.Contains(t, msg.Body, "I see the command but can't find the corresponding bug")
  1476  	})
  1477  }