From 69423a1d41ce792ddad1c5636b61df7cb2e91440 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Mon, 13 May 2019 14:15:14 +0200 Subject: pkg/email: allow commands in subject Several users attempted this and there does not seem to be any reason to not allow this. So parse out command from subject as well. --- pkg/email/parser.go | 32 ++++++++++++++++---------------- pkg/email/parser_test.go | 23 +++++++++++++++++++++-- 2 files changed, 37 insertions(+), 18 deletions(-) (limited to 'pkg') diff --git a/pkg/email/parser.go b/pkg/email/parser.go index 0f4e1d40c..9c37331f7 100644 --- a/pkg/email/parser.go +++ b/pkg/email/parser.go @@ -4,7 +4,6 @@ package email import ( - "bytes" "encoding/base64" "fmt" "io" @@ -104,6 +103,7 @@ func Parse(r io.Reader, ownEmails []string) (*Email, error) { return nil, err } bodyStr := string(body) + subject := msg.Header.Get("Subject") cmd := CmdNone patch, cmdStr, cmdArgs := "", "", "" if !fromMe { @@ -116,7 +116,7 @@ func Parse(r io.Reader, ownEmails []string) (*Email, error) { if patch == "" { _, patch, _ = ParsePatch(bodyStr) } - cmd, cmdStr, cmdArgs = extractCommand(body) + cmd, cmdStr, cmdArgs = extractCommand(subject + "\n" + bodyStr) } link := "" if match := groupsLinkRe.FindStringSubmatchIndex(bodyStr); match != nil { @@ -126,10 +126,10 @@ func Parse(r io.Reader, ownEmails []string) (*Email, error) { BugID: bugID, MessageID: msg.Header.Get("Message-ID"), Link: link, - Subject: msg.Header.Get("Subject"), + Subject: subject, From: from[0].String(), Cc: ccList, - Body: string(body), + Body: bodyStr, Patch: patch, Command: cmd, CommandStr: cmdStr, @@ -195,8 +195,8 @@ func CanonicalEmail(email string) string { // extractCommand extracts command to syzbot from email body. // Commands are of the following form: // ^#syz cmd args... -func extractCommand(body []byte) (cmd Command, str, args string) { - cmdPos := bytes.Index(append([]byte{'\n'}, body...), []byte("\n"+commandPrefix)) +func extractCommand(body string) (cmd Command, str, args string) { + cmdPos := strings.Index("\n"+body, "\n"+commandPrefix) if cmdPos == -1 { cmd = CmdNone return @@ -205,17 +205,17 @@ func extractCommand(body []byte) (cmd Command, str, args string) { for cmdPos < len(body) && body[cmdPos] == ' ' { cmdPos++ } - cmdEnd := bytes.IndexByte(body[cmdPos:], '\n') + cmdEnd := strings.IndexByte(body[cmdPos:], '\n') if cmdEnd == -1 { cmdEnd = len(body) - cmdPos } - if cmdEnd1 := bytes.IndexByte(body[cmdPos:], '\r'); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { + if cmdEnd1 := strings.IndexByte(body[cmdPos:], '\r'); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { cmdEnd = cmdEnd1 } - if cmdEnd1 := bytes.IndexByte(body[cmdPos:], ' '); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { + if cmdEnd1 := strings.IndexByte(body[cmdPos:], ' '); cmdEnd1 != -1 && cmdEnd1 < cmdEnd { cmdEnd = cmdEnd1 } - str = string(body[cmdPos : cmdPos+cmdEnd]) + str = body[cmdPos : cmdPos+cmdEnd] switch str { default: cmd = CmdUnknown @@ -253,14 +253,14 @@ func extractCommand(body []byte) (cmd Command, str, args string) { return } -func extractArgsTokens(body []byte, num int) string { +func extractArgsTokens(body string, num int) string { var args []string for pos := 0; len(args) < num && pos < len(body); { - lineEnd := bytes.IndexByte(body[pos:], '\n') + lineEnd := strings.IndexByte(body[pos:], '\n') if lineEnd == -1 { lineEnd = len(body) - pos } - line := strings.TrimSpace(string(body[pos : pos+lineEnd])) + line := strings.TrimSpace(body[pos : pos+lineEnd]) for { line1 := strings.Replace(line, " ", " ", -1) if line == line1 { @@ -276,17 +276,17 @@ func extractArgsTokens(body []byte, num int) string { return strings.TrimSpace(strings.Join(args, " ")) } -func extractArgsLine(body []byte) string { +func extractArgsLine(body string) string { pos := 0 for pos < len(body) && (body[pos] == ' ' || body[pos] == '\t' || body[pos] == '\n' || body[pos] == '\r') { pos++ } - lineEnd := bytes.IndexByte(body[pos:], '\n') + lineEnd := strings.IndexByte(body[pos:], '\n') if lineEnd == -1 { lineEnd = len(body) - pos } - return strings.TrimSpace(string(body[pos : pos+lineEnd])) + return strings.TrimSpace(body[pos : pos+lineEnd]) } func parseBody(r io.Reader, headers mail.Header) ([]byte, [][]byte, error) { diff --git a/pkg/email/parser_test.go b/pkg/email/parser_test.go index 0085adeb9..ba027f8e7 100644 --- a/pkg/email/parser_test.go +++ b/pkg/email/parser_test.go @@ -15,13 +15,13 @@ import ( func TestExtractCommand(t *testing.T) { for i, test := range extractCommandTests { t.Run(fmt.Sprint(i), func(t *testing.T) { - cmd, str, args := extractCommand([]byte(test.body)) + cmd, str, args := extractCommand(test.body) if cmd != test.cmd || str != test.str || !reflect.DeepEqual(args, test.args) { t.Logf("expect: %v %q %q", test.cmd, test.str, test.args) t.Logf("got : %v %q %q", cmd, str, args) t.Fail() } - cmd, str, args = extractCommand([]byte(strings.Replace(test.body, "\n", "\r\n", -1))) + cmd, str, args = extractCommand(strings.Replace(test.body, "\n", "\r\n", -1)) if cmd != test.cmd || str != test.str || !reflect.DeepEqual(args, test.args) { t.Logf("expect: %v %q %q", test.cmd, test.str, test.args) t.Logf("got : %v %q %q", cmd, str, args) @@ -656,4 +656,23 @@ When freeing a lockf struct that already is part of a linked list, make sure to CommandStr: "fix:", CommandArgs: "When freeing a lockf struct that already is part of a linked list, make sure to", }}, + + {`Date: Sun, 7 May 2017 19:54:00 -0700 +Message-ID: <123> +Subject: #syz test: git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git master +From: bob@example.com +To: syzbot + +nothing to see here`, + Email{ + BugID: "4564456", + MessageID: "<123>", + Subject: "#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git master", + From: "", + Cc: []string{"bob@example.com"}, + Body: `nothing to see here`, + Command: CmdTest, + CommandStr: "test:", + CommandArgs: "git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git master", + }}, } -- cgit mrf-deployment