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:
Kota Kanbe
2021-04-01 13:36:24 +09:00
committed by GitHub
parent 0179f4299a
commit 9bfe0627ae
70 changed files with 48982 additions and 1274 deletions

View File

@@ -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

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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
}

View File

@@ -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) {