Issues (121)

report/email.go (3 issues)

Severity
1
/* Vuls - Vulnerability Scanner
2
Copyright (C) 2016  Future Corporation , Japan.
3
4
This program is free software: you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation, either version 3 of the License, or
7
(at your option) any later version.
8
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
GNU General Public License for more details.
13
14
You should have received a copy of the GNU General Public License
15
along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
package report
19
20
import (
21
	"fmt"
22
	"net"
23
	"net/mail"
24
	"net/smtp"
25
	"strings"
26
	"time"
27
28
	"github.com/future-architect/vuls/config"
29
	"github.com/future-architect/vuls/models"
30
	"golang.org/x/xerrors"
31
)
32
33
// EMailWriter send mail
34
type EMailWriter struct{}
35
36
func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
37
	conf := config.Conf
38
	var message string
39
	sender := NewEMailSender()
40
41
	m := map[string]int{}
42
	for _, r := range rs {
43
		if conf.FormatOneEMail {
44
			message += formatFullPlainText(r) + "\r\n\r\n"
45
			mm := r.ScannedCves.CountGroupBySeverity()
46
			keys := []string{"High", "Medium", "Low", "Unknown"}
47
			for _, k := range keys {
48
				m[k] += mm[k]
49
			}
50
		} else {
51
			var subject string
52
			if len(r.Errors) != 0 {
53
				subject = fmt.Sprintf("%s%s An error occurred while scanning",
54
					conf.EMail.SubjectPrefix, r.ServerInfo())
55
			} else {
56
				subject = fmt.Sprintf("%s%s %s",
57
					conf.EMail.SubjectPrefix,
58
					r.ServerInfo(),
59
					r.ScannedCves.FormatCveSummary())
60
			}
61
			if conf.FormatList {
62
				message = formatList(r)
63
			} else {
64
				message = formatFullPlainText(r)
65
			}
66
			if conf.FormatOneLineText {
67
				message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(r))
68
			}
69
			if err := sender.Send(subject, message); err != nil {
70
				return err
71
			}
72
		}
73
	}
74
	summary := ""
75
	if config.Conf.IgnoreUnscoredCves {
76
		summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d)",
77
			m["High"]+m["Medium"]+m["Low"], m["High"], m["Medium"], m["Low"])
78
	}
79
	summary = fmt.Sprintf("Total: %d (High:%d Medium:%d Low:%d ?:%d)",
80
		m["High"]+m["Medium"]+m["Low"]+m["Unknown"],
81
		m["High"], m["Medium"], m["Low"], m["Unknown"])
82
	origmessage := message
83
	if conf.FormatOneEMail {
84
		message = fmt.Sprintf("One Line Summary\r\n================\r\n%s", formatOneLineSummary(rs...))
85
		if !conf.FormatOneLineText {
86
			message += fmt.Sprintf("\r\n\r\n%s", origmessage)
87
		}
88
89
		subject := fmt.Sprintf("%s %s",
90
			conf.EMail.SubjectPrefix, summary)
91
		return sender.Send(subject, message)
92
	}
93
	return nil
94
}
95
96
// EMailSender is interface of sending e-mail
97
type EMailSender interface {
98
	Send(subject, body string) error
99
}
100
101
type emailSender struct {
102
	conf config.SMTPConf
103
	send func(string, smtp.Auth, string, []string, []byte) error
104
}
105
106
func (e *emailSender) Send(subject, body string) (err error) {
107
	emailConf := e.conf
108
	to := strings.Join(emailConf.To[:], ", ")
109
	cc := strings.Join(emailConf.Cc[:], ", ")
110
	mailAddresses := append(emailConf.To, emailConf.Cc...)
111
	if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
112
		return xerrors.Errorf("Failed to parse email addresses: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
113
	}
114
115
	headers := make(map[string]string)
116
	headers["From"] = emailConf.From
117
	headers["To"] = to
118
	headers["Cc"] = cc
119
	headers["Subject"] = subject
120
	headers["Date"] = time.Now().Format(time.RFC1123Z)
121
	headers["Content-Type"] = "text/plain; charset=utf-8"
122
123
	var header string
124
	for k, v := range headers {
125
		header += fmt.Sprintf("%s: %s\r\n", k, v)
126
	}
127
	message := fmt.Sprintf("%s\r\n%s", header, body)
128
129
	smtpServer := net.JoinHostPort(emailConf.SMTPAddr, emailConf.SMTPPort)
130
131
	if emailConf.User != "" && emailConf.Password != "" {
132
		err = e.send(
133
			smtpServer,
134
			smtp.PlainAuth(
135
				"",
136
				emailConf.User,
137
				emailConf.Password,
138
				emailConf.SMTPAddr,
139
			),
140
			emailConf.From,
141
			mailAddresses,
142
			[]byte(message),
143
		)
144
		if err != nil {
145
			return xerrors.Errorf("Failed to send emails: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
146
		}
147
		return nil
148
	}
149
	err = e.send(
150
		smtpServer,
151
		nil,
152
		emailConf.From,
153
		mailAddresses,
154
		[]byte(message),
155
	)
156
	if err != nil {
157
		return xerrors.Errorf("Failed to send emails: %w", err)
0 ignored issues
show
unrecognized printf verb 'w'
Loading history...
158
	}
159
	return nil
160
}
161
162
// NewEMailSender creates emailSender
163
func NewEMailSender() EMailSender {
164
	return &emailSender{config.Conf.EMail, smtp.SendMail}
165
}
166