// Copyright 2017 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. package email import ( "bufio" "bytes" "fmt" "net/mail" "regexp" "strings" "text/template" "github.com/google/syzkaller/pkg/aflow/ai" ) func ParsePatch(message []byte) (diff string) { s := bufio.NewScanner(bytes.NewReader(message)) diffStarted := false for s.Scan() { ln := s.Text() if lineMatchesDiffStart(ln) { diffStarted = true diff += ln + "\n" continue } if diffStarted { if ln == "--" || ln == "-- " || ln != "" && ln[0] == '>' { diffStarted = false continue } if ln == "" || strings.HasPrefix(ln, " ") || strings.HasPrefix(ln, "+") || strings.HasPrefix(ln, "-") || strings.HasPrefix(ln, "@") || strings.HasPrefix(ln, "================") { diff += ln + "\n" continue } diffStarted = false } } if diff != "" { diff = strings.TrimRight(diff, "\n") + "\n" } err := s.Err() if err == bufio.ErrTooLong { // It's a problem of the incoming patch, rather than anything else. // Anyway, if a patch contains too long lines, we're probably not // interested in it, so let's pretent we didn't see it. diff = "" return } else if err != nil { panic("error while scanning from memory: " + err.Error()) } return } func FormatPatchDescription(description string, authors []string, recipients []ai.Recipient) string { buf := new(bytes.Buffer) var to, cc []mail.Address for _, recipient := range recipients { addr := mail.Address{Name: recipient.Name, Address: recipient.Email} if recipient.To { to = append(to, addr) } else { cc = append(cc, addr) } } err := patchTemplate.Execute(buf, map[string]any{ "description": strings.TrimSpace(description), "authors": authors, "to": to, "cc": cc, }) if err != nil { panic(err) } return buf.String() } func FormatPatch(description, diff, baseCommit string, authors []string, recipients []ai.Recipient) string { return FormatPatchDescription(description, authors, recipients) + fmt.Sprintf("%v\nbase-commit: %v\n", diff, baseCommit) } var patchTemplate = template.Must(template.New("").Parse(`{{.description}} {{range $addr := .authors}} Signed-off-by: {{$addr}}{{end}} {{- range $addr := .to}} To: {{$addr.String}}{{end}} {{- range $addr := .cc}} Cc: {{$addr.String}}{{end}} --- This patch was generated by Google Gemini LLM model. It was pre-reviewed and Signed-off-by a human, but please review carefully. `)) var diffRegexps = []*regexp.Regexp{ regexp.MustCompile(`^(---|\+\+\+) [^\s]`), regexp.MustCompile(`^diff --git`), regexp.MustCompile(`^index [0-9a-f]+\.\.[0-9a-f]+`), regexp.MustCompile(`^new file mode [0-9]+`), regexp.MustCompile(`^Index: [^\s]`), } func lineMatchesDiffStart(ln string) bool { for _, re := range diffRegexps { if re.MatchString(ln) { return true } } return false }