refactor: don't use global Config in private func (#1197)
* refactor: cve_client.go * refactor: don't use global Config in private func * remove import alias for config * refactor: dbclient * refactor: resultDir * refactor: resultsDir * refactor * refactor: gost * refactor: db client * refactor: cveDB * refactor: cvedb * refactor: exploitDB * refactor: remove detector/dbclient.go * refactor: writer * refactor: syslog writer * refactor: ips * refactor: ensureResultDir * refactor: proxy * fix(db): call CloseDB * add integration test * feat(report): sort array in json * sort func for json diff * add build-int to makefile * add int-rds-redis to makefile * fix: test case, makefile * fix makefile * show cve count after diff * make diff * diff -c * sort exploits in json for diff * sort metasploit, exploit
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
storage "github.com/Azure/azure-sdk-for-go/storage"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ type AzureBlobWriter struct {
|
||||
FormatList bool
|
||||
Gzip bool
|
||||
|
||||
c.AzureConf
|
||||
config.AzureConf
|
||||
}
|
||||
|
||||
// Write results to Azure Blob storage
|
||||
|
||||
@@ -15,14 +15,16 @@ import (
|
||||
)
|
||||
|
||||
// ChatWorkWriter send report to ChatWork
|
||||
type ChatWorkWriter struct{}
|
||||
type ChatWorkWriter struct {
|
||||
Cnf config.ChatWorkConf
|
||||
Proxy string
|
||||
}
|
||||
|
||||
func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf.ChatWork
|
||||
|
||||
for _, r := range rs {
|
||||
serverInfo := fmt.Sprintf("%s", r.ServerInfo())
|
||||
if err = chatWorkpostMessage(conf.Room, conf.APIToken, serverInfo); err != nil {
|
||||
if err = w.chatWorkpostMessage(serverInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -40,7 +42,7 @@ func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
severity,
|
||||
vinfo.Summaries(r.Lang, r.Family)[0].Value)
|
||||
|
||||
if err = chatWorkpostMessage(conf.Room, conf.APIToken, message); err != nil {
|
||||
if err = w.chatWorkpostMessage(message); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -49,8 +51,8 @@ func (w ChatWorkWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func chatWorkpostMessage(room, token, message string) error {
|
||||
uri := fmt.Sprintf("https://api.chatwork.com/v2/rooms/%s/messages=%s", room, token)
|
||||
func (w ChatWorkWriter) chatWorkpostMessage(message string) error {
|
||||
uri := fmt.Sprintf("https://api.chatwork.com/v2/rooms/%s/messages=%s", w.Cnf.Room, w.Cnf.APIToken)
|
||||
payload := url.Values{"body": {message}}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
@@ -59,10 +61,9 @@ func chatWorkpostMessage(room, token, message string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("X-ChatWorkToken", token)
|
||||
req.Header.Add("X-ChatWorkToken", w.Cnf.APIToken)
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
|
||||
client, err := util.GetHTTPClient(w.Proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ type EMailWriter struct {
|
||||
FormatOneEMail bool
|
||||
FormatOneLineText bool
|
||||
FormatList bool
|
||||
Cnf config.SMTPConf
|
||||
}
|
||||
|
||||
func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf
|
||||
var message string
|
||||
sender := NewEMailSender()
|
||||
sender := NewEMailSender(w.Cnf)
|
||||
m := map[string]int{}
|
||||
for _, r := range rs {
|
||||
if w.FormatOneEMail {
|
||||
@@ -39,10 +39,10 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
var subject string
|
||||
if len(r.Errors) != 0 {
|
||||
subject = fmt.Sprintf("%s%s An error occurred while scanning",
|
||||
conf.EMail.SubjectPrefix, r.ServerInfo())
|
||||
w.Cnf.SubjectPrefix, r.ServerInfo())
|
||||
} else {
|
||||
subject = fmt.Sprintf("%s%s %s",
|
||||
conf.EMail.SubjectPrefix,
|
||||
w.Cnf.SubjectPrefix,
|
||||
r.ServerInfo(),
|
||||
r.ScannedCves.FormatCveSummary())
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
subject := fmt.Sprintf("%s %s",
|
||||
conf.EMail.SubjectPrefix, summary)
|
||||
w.Cnf.SubjectPrefix, summary)
|
||||
return sender.Send(subject, message)
|
||||
}
|
||||
return nil
|
||||
@@ -196,8 +196,8 @@ func (e *emailSender) Send(subject, body string) (err error) {
|
||||
}
|
||||
|
||||
// NewEMailSender creates emailSender
|
||||
func NewEMailSender() EMailSender {
|
||||
return &emailSender{config.Conf.EMail}
|
||||
func NewEMailSender(cnf config.SMTPConf) EMailSender {
|
||||
return &emailSender{cnf}
|
||||
}
|
||||
|
||||
func (e *emailSender) newSaslClient(authList []string) sasl.Client {
|
||||
|
||||
@@ -5,13 +5,14 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// HTTPRequestWriter writes results to HTTP request
|
||||
type HTTPRequestWriter struct{}
|
||||
type HTTPRequestWriter struct {
|
||||
Proxy string
|
||||
}
|
||||
|
||||
// Write sends results as HTTP response
|
||||
func (w HTTPRequestWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
@@ -20,7 +21,7 @@ func (w HTTPRequestWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
if err := json.NewEncoder(b).Encode(r); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = http.Post(config.Conf.HTTP.URL, "application/json; charset=utf-8", b)
|
||||
_, err = http.Post(w.Proxy, "application/json; charset=utf-8", b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -35,8 +35,9 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
for _, r := range rs {
|
||||
path := filepath.Join(w.CurrentDir, r.ReportFileName())
|
||||
r.SortForJSONOutput()
|
||||
|
||||
path := filepath.Join(w.CurrentDir, r.ReportFileName())
|
||||
if w.FormatJSON {
|
||||
p := path + ".json"
|
||||
if w.DiffPlus || w.DiffMinus {
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
)
|
||||
|
||||
@@ -27,7 +27,7 @@ type S3Writer struct {
|
||||
FormatList bool
|
||||
Gzip bool
|
||||
|
||||
c.AWSConf
|
||||
config.AWSConf
|
||||
}
|
||||
|
||||
func (w S3Writer) getS3() (*s3.S3, error) {
|
||||
|
||||
@@ -21,6 +21,8 @@ type SlackWriter struct {
|
||||
FormatOneLineText bool
|
||||
lang string
|
||||
osFamily string
|
||||
Cnf config.SlackConf
|
||||
Proxy string
|
||||
}
|
||||
|
||||
type message struct {
|
||||
@@ -32,10 +34,8 @@ type message struct {
|
||||
}
|
||||
|
||||
func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf.Slack
|
||||
channel := conf.Channel
|
||||
token := conf.LegacyToken
|
||||
|
||||
channel := w.Cnf.Channel
|
||||
for _, r := range rs {
|
||||
w.lang, w.osFamily = r.Lang, r.Family
|
||||
if channel == "${servername}" {
|
||||
@@ -57,15 +57,15 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
sort.Ints(chunkKeys)
|
||||
|
||||
summary := fmt.Sprintf("%s\n%s",
|
||||
w.getNotifyUsers(config.Conf.Slack.NotifyUsers),
|
||||
w.getNotifyUsers(w.Cnf.NotifyUsers),
|
||||
formatOneLineSummary(r))
|
||||
|
||||
// Send slack by API
|
||||
if 0 < len(token) {
|
||||
api := slack.New(token)
|
||||
if 0 < len(w.Cnf.LegacyToken) {
|
||||
api := slack.New(w.Cnf.LegacyToken)
|
||||
msgPrms := slack.PostMessageParameters{
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Username: w.Cnf.AuthUser,
|
||||
IconEmoji: w.Cnf.IconEmoji,
|
||||
}
|
||||
|
||||
var ts string
|
||||
@@ -83,8 +83,8 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
|
||||
for _, k := range chunkKeys {
|
||||
params := slack.PostMessageParameters{
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Username: w.Cnf.AuthUser,
|
||||
IconEmoji: w.Cnf.IconEmoji,
|
||||
ThreadTimestamp: ts,
|
||||
}
|
||||
if _, _, err = api.PostMessage(
|
||||
@@ -99,8 +99,8 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
} else {
|
||||
msg := message{
|
||||
Text: summary,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Username: w.Cnf.AuthUser,
|
||||
IconEmoji: w.Cnf.IconEmoji,
|
||||
Channel: channel,
|
||||
}
|
||||
if err := w.send(msg); err != nil {
|
||||
@@ -119,8 +119,8 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
|
||||
msg := message{
|
||||
Text: txt,
|
||||
Username: conf.AuthUser,
|
||||
IconEmoji: conf.IconEmoji,
|
||||
Username: w.Cnf.AuthUser,
|
||||
IconEmoji: w.Cnf.IconEmoji,
|
||||
Channel: channel,
|
||||
Attachments: m[k],
|
||||
}
|
||||
@@ -134,14 +134,13 @@ func (w SlackWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
}
|
||||
|
||||
func (w SlackWriter) send(msg message) error {
|
||||
conf := config.Conf.Slack
|
||||
count, retryMax := 0, 10
|
||||
|
||||
count, retryMax := 0, 10
|
||||
bytes, _ := json.Marshal(msg)
|
||||
jsonBody := string(bytes)
|
||||
|
||||
f := func() (err error) {
|
||||
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Proxy(config.Conf.HTTPProxy).Post(conf.HookURL).Send(string(jsonBody)).End()
|
||||
resp, body, errs := gorequest.New().Timeout(10 * time.Second).Proxy(w.Proxy).Post(w.Cnf.HookURL).Send(string(jsonBody)).End()
|
||||
if 0 < len(errs) || resp == nil || resp.StatusCode != 200 {
|
||||
count++
|
||||
if count == retryMax {
|
||||
@@ -149,7 +148,7 @@ func (w SlackWriter) send(msg message) error {
|
||||
}
|
||||
return xerrors.Errorf(
|
||||
"HTTP POST error. url: %s, resp: %v, body: %s, err: %+v",
|
||||
conf.HookURL, resp, body, errs)
|
||||
w.Cnf.HookURL, resp, body, errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,15 +12,16 @@ import (
|
||||
)
|
||||
|
||||
// SyslogWriter send report to syslog
|
||||
type SyslogWriter struct{}
|
||||
type SyslogWriter struct {
|
||||
Cnf config.SyslogConf
|
||||
}
|
||||
|
||||
func (w SyslogWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf.Syslog
|
||||
facility, _ := conf.GetFacility()
|
||||
severity, _ := conf.GetSeverity()
|
||||
raddr := fmt.Sprintf("%s:%s", conf.Host, conf.Port)
|
||||
facility, _ := w.Cnf.GetFacility()
|
||||
severity, _ := w.Cnf.GetSeverity()
|
||||
raddr := fmt.Sprintf("%s:%s", w.Cnf.Host, w.Cnf.Port)
|
||||
|
||||
sysLog, err := syslog.Dial(conf.Protocol, raddr, severity|facility, conf.Tag)
|
||||
sysLog, err := syslog.Dial(w.Cnf.Protocol, raddr, severity|facility, w.Cnf.Tag)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Failed to initialize syslog client: %w", err)
|
||||
}
|
||||
@@ -72,7 +73,7 @@ func (w SyslogWriter) encodeSyslog(result models.ScanResult) (messages []string)
|
||||
if content, ok := vinfo.CveContents[models.Nvd]; ok {
|
||||
cwes := strings.Join(content.CweIDs, ",")
|
||||
kvPairs = append(kvPairs, fmt.Sprintf(`cwe_ids="%s"`, cwes))
|
||||
if config.Conf.Syslog.Verbose {
|
||||
if w.Cnf.Verbose {
|
||||
kvPairs = append(kvPairs, fmt.Sprintf(`source_link="%s"`, content.SourceLink))
|
||||
kvPairs = append(kvPairs, fmt.Sprintf(`summary="%s"`, content.Summary))
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ import (
|
||||
)
|
||||
|
||||
// TelegramWriter sends report to Telegram
|
||||
type TelegramWriter struct{}
|
||||
type TelegramWriter struct {
|
||||
Cnf config.TelegramConf
|
||||
Proxy string
|
||||
}
|
||||
|
||||
func (w TelegramWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
conf := config.Conf.Telegram
|
||||
for _, r := range rs {
|
||||
msgs := []string{fmt.Sprintf("*%s*\n%s\n%s\n%s",
|
||||
r.ServerInfo(),
|
||||
@@ -40,14 +42,14 @@ func (w TelegramWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
maxCvss.Value.Vector,
|
||||
vinfo.Summaries(r.Lang, r.Family)[0].Value))
|
||||
if len(msgs) == 5 {
|
||||
if err = sendMessage(conf.ChatID, conf.Token, strings.Join(msgs, "\n\n")); err != nil {
|
||||
if err = w.sendMessage(w.Cnf.ChatID, w.Cnf.Token, strings.Join(msgs, "\n\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
msgs = []string{}
|
||||
}
|
||||
}
|
||||
if len(msgs) != 0 {
|
||||
if err = sendMessage(conf.ChatID, conf.Token, strings.Join(msgs, "\n\n")); err != nil {
|
||||
if err = w.sendMessage(w.Cnf.ChatID, w.Cnf.Token, strings.Join(msgs, "\n\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -55,7 +57,7 @@ func (w TelegramWriter) Write(rs ...models.ScanResult) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendMessage(chatID, token, message string) error {
|
||||
func (w TelegramWriter) sendMessage(chatID, token, message string) error {
|
||||
uri := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", token)
|
||||
payload := `{"text": "` + strings.Replace(message, `"`, `\"`, -1) + `", "chat_id": "` + chatID + `", "parse_mode": "Markdown" }`
|
||||
|
||||
@@ -67,7 +69,7 @@ func sendMessage(chatID, token, message string) error {
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
|
||||
client, err := util.GetHTTPClient(config.Conf.HTTPProxy)
|
||||
client, err := util.GetHTTPClient(w.Proxy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -85,16 +85,15 @@ var jsonDirPattern = regexp.MustCompile(
|
||||
|
||||
// ListValidJSONDirs returns valid json directory as array
|
||||
// Returned array is sorted so that recent directories are at the head
|
||||
func ListValidJSONDirs() (dirs []string, err error) {
|
||||
func ListValidJSONDirs(resultsDir string) (dirs []string, err error) {
|
||||
var dirInfo []os.FileInfo
|
||||
if dirInfo, err = ioutil.ReadDir(config.Conf.ResultsDir); err != nil {
|
||||
err = xerrors.Errorf("Failed to read %s: %w",
|
||||
config.Conf.ResultsDir, err)
|
||||
if dirInfo, err = ioutil.ReadDir(resultsDir); err != nil {
|
||||
err = xerrors.Errorf("Failed to read %s: %w", resultsDir, err)
|
||||
return
|
||||
}
|
||||
for _, d := range dirInfo {
|
||||
if d.IsDir() && jsonDirPattern.MatchString(d.Name()) {
|
||||
jsonDir := filepath.Join(config.Conf.ResultsDir, d.Name())
|
||||
jsonDir := filepath.Join(resultsDir, d.Name())
|
||||
dirs = append(dirs, jsonDir)
|
||||
}
|
||||
}
|
||||
@@ -105,19 +104,17 @@ func ListValidJSONDirs() (dirs []string, err error) {
|
||||
}
|
||||
|
||||
// JSONDir returns
|
||||
// If there is an arg, check if it is a valid format and return the corresponding path under results.
|
||||
// If there is args, check if it is a valid format and return the corresponding path under results.
|
||||
// If arg passed via PIPE (such as history subcommand), return that path.
|
||||
// Otherwise, returns the path of the latest directory
|
||||
func JSONDir(args []string) (string, error) {
|
||||
var err error
|
||||
func JSONDir(resultsDir string, args []string) (path string, err error) {
|
||||
var dirs []string
|
||||
|
||||
if 0 < len(args) {
|
||||
if dirs, err = ListValidJSONDirs(); err != nil {
|
||||
if dirs, err = ListValidJSONDirs(resultsDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path := filepath.Join(config.Conf.ResultsDir, args[0])
|
||||
path = filepath.Join(resultsDir, args[0])
|
||||
for _, d := range dirs {
|
||||
ss := strings.Split(d, string(os.PathSeparator))
|
||||
timedir := ss[len(ss)-1]
|
||||
@@ -125,11 +122,10 @@ func JSONDir(args []string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", xerrors.Errorf("Invalid path: %s", path)
|
||||
}
|
||||
|
||||
// PIPE
|
||||
// TODO remove Pipe flag
|
||||
if config.Conf.Pipe {
|
||||
bytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
@@ -137,18 +133,17 @@ func JSONDir(args []string) (string, error) {
|
||||
}
|
||||
fields := strings.Fields(string(bytes))
|
||||
if 0 < len(fields) {
|
||||
return filepath.Join(config.Conf.ResultsDir, fields[0]), nil
|
||||
return filepath.Join(resultsDir, fields[0]), nil
|
||||
}
|
||||
return "", xerrors.Errorf("Stdin is invalid: %s", string(bytes))
|
||||
}
|
||||
|
||||
// returns latest dir when no args or no PIPE
|
||||
if dirs, err = ListValidJSONDirs(); err != nil {
|
||||
if dirs, err = ListValidJSONDirs(resultsDir); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(dirs) == 0 {
|
||||
return "", xerrors.Errorf("No results under %s",
|
||||
config.Conf.ResultsDir)
|
||||
return "", xerrors.Errorf("No results under %s", resultsDir)
|
||||
}
|
||||
return dirs[0], nil
|
||||
}
|
||||
@@ -224,6 +219,7 @@ func formatOneLineSummary(rs ...models.ScanResult) string {
|
||||
}
|
||||
}
|
||||
// We don't want warning message to the summary file
|
||||
// TODO Don't use global variable
|
||||
if config.Conf.Quiet {
|
||||
return fmt.Sprintf("%s\n", table)
|
||||
}
|
||||
@@ -483,7 +479,7 @@ No CVE-IDs are found in updatable packages.
|
||||
}
|
||||
|
||||
for _, alert := range vuln.AlertDict.En {
|
||||
data = append(data, []string{"USCERT Alert", alert.URL})
|
||||
data = append(data, []string{"US-CERT Alert", alert.URL})
|
||||
}
|
||||
|
||||
// for _, rr := range vuln.CveContents.References(r.Family) {
|
||||
|
||||
Reference in New Issue
Block a user