From 33ffce52a3b573ac252d419c0ff3c956028650ad Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Wed, 9 Jul 2025 16:07:34 +0200 Subject: dashboard: keep bug ID in Cc of forwarded emails When we forward an email that matched an inbox regexp, make sure the author and the original report email are Cc'd. Otherwise the automation cannot determine the original bug report. Improve tests. --- dashboard/app/email_test.go | 14 ++++++++++++-- dashboard/app/reporting_email.go | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/dashboard/app/email_test.go b/dashboard/app/email_test.go index 219c7795e..3c7a2d200 100644 --- a/dashboard/app/email_test.go +++ b/dashboard/app/email_test.go @@ -1420,7 +1420,8 @@ func TestForwardEmailInbox(t *testing.T) { } t.Run("forwarded", func(t *testing.T) { - c.incomingEmail("syzbot+prefixABCD@testapp.appspotmail.com", + from := "syzbot+prefixABCD@testapp.appspotmail.com" + c.incomingEmail(from, "#syz invalid", EmailOptMessageID(1), EmailOptFrom("someone@mail.com"), @@ -1428,8 +1429,9 @@ func TestForwardEmailInbox(t *testing.T) { msg := c.pollEmailBug() require.NotNil(t, msg) assert.Equal(t, `"syzbot" `, msg.Sender) - assert.ElementsMatch(t, []string{"forward@a.com", "forward@b.com", "someone@mail.com"}, + assert.ElementsMatch(t, []string{"forward@a.com", "forward@b.com"}, msg.To, "must be sent to the author and the missing lists") + assert.ElementsMatch(t, []string{"\"syzbot\" <" + from + ">", "someone@mail.com"}, msg.Cc) assert.Equal(t, "<1>", msg.Headers.Get("In-Reply-To")) assert.Equal(t, `For archival purposes, forwarding an incoming command email to forward@a.com, forward@b.com. @@ -1441,6 +1443,14 @@ Author: someone@mail.com #syz invalid `, msg.Body) + + t.Run("no-loop", func(t *testing.T) { + // Ensure that we don't react to our own reply. + c.incomingEmail(from, msg.Body, + EmailOptFrom(msg.Sender), + EmailOptCC(append(append([]string{}, msg.Cc...), msg.To...))) + c.expectNoEmail() + }) }) t.Run("no command", func(t *testing.T) { diff --git a/dashboard/app/reporting_email.go b/dashboard/app/reporting_email.go index a6942a952..78db7fb90 100644 --- a/dashboard/app/reporting_email.go +++ b/dashboard/app/reporting_email.go @@ -641,7 +641,7 @@ func matchInbox(c context.Context, myEmail string) *PerInboxConfig { } func processInboxEmail(c context.Context, msg *email.Email, inbox *PerInboxConfig) error { - if len(msg.Commands) == 0 || len(msg.BugIDs) == 0 { + if len(msg.Commands) == 0 || len(msg.BugIDs) == 0 || msg.OwnEmail { // Do not forward emails with no commands. // Also, we don't care about the emails that don't include any BugIDs. return nil @@ -657,6 +657,7 @@ func processInboxEmail(c context.Context, msg *email.Email, inbox *PerInboxConfi sort.Strings(missing) if len(missing) == 0 { // Everything's OK. + log.Infof(c, "email %q has all necessary lists in Cc", msg.MessageID) return nil } // We don't want to forward from a name+hash@domain address because @@ -670,7 +671,7 @@ func processInboxEmail(c context.Context, msg *email.Email, inbox *PerInboxConfi if !stringInList(msg.Cc, cc) { msg.Cc = append(msg.Cc, cc) } - return forwardEmail(c, msg, missing, "", msg.MessageID, true) + return forwardEmail(c, msg, missing, []string{cc, msg.Author}, "", msg.MessageID) } // nolint: gocyclo @@ -734,7 +735,7 @@ func processIncomingEmail(c context.Context, msg *email.Email) error { } reply := groupEmailReplies(replies) if reply == "" && len(msg.Commands) > 0 && len(missingLists) > 0 && !unCc { - return forwardEmail(c, msg, missingLists, bugInfo.bugReporting.ID, bugInfo.bugReporting.ExtID, false) + return forwardEmail(c, msg, missingLists, nil, bugInfo.bugReporting.ID, bugInfo.bugReporting.ExtID) } if reply != "" { return replyTo(c, msg, bugInfo.bugReporting.ID, reply) @@ -1421,8 +1422,8 @@ func missingMailingLists(c context.Context, msg *email.Email, emailConfig *Email return missing } -func forwardEmail(c context.Context, msg *email.Email, mailingLists []string, - bugID, inReplyTo string, includeAuthor bool) error { +func forwardEmail(c context.Context, msg *email.Email, mailingLists, cc []string, + bugID, inReplyTo string) error { log.Infof(c, "forwarding email: id=%q from=%q to=%q", msg.MessageID, msg.Author, mailingLists) body := fmt.Sprintf(`For archival purposes, forwarding an incoming command email to %v. @@ -1437,10 +1438,14 @@ Author: %s if err != nil { return err } - if includeAuthor { - mailingLists = append([]string{msg.Author}, mailingLists...) - } - return sendMailText(c, msg.Subject, from, mailingLists, inReplyTo, body) + return sendEmail(c, &aemail.Message{ + Sender: from, + To: mailingLists, + Cc: cc, + Subject: msg.Subject, + Body: body, + Headers: mail.Header{"In-Reply-To": []string{inReplyTo}}, + }) } func sendMailText(c context.Context, subject, from string, to []string, replyTo, body string) error { -- cgit mrf-deployment