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 }