From ebd3834a35a202473fc72e7d3422b247193e5dfc Mon Sep 17 00:00:00 2001 From: gy741 Date: Thu, 5 Nov 2020 20:56:19 +0900 Subject: [PATCH] add(report) -format-csv option (#1034) --- commands/report.go | 3 ++- config/config.go | 1 + report/localfile.go | 16 ++++++++++++++++ report/stdout.go | 2 +- report/util.go | 46 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 2 deletions(-) diff --git a/commands/report.go b/commands/report.go index 9c0c2077..ae1d6eac 100644 --- a/commands/report.go +++ b/commands/report.go @@ -143,6 +143,7 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) { f.BoolVar(&c.Conf.FormatJSON, "format-json", false, "JSON format") f.BoolVar(&c.Conf.FormatXML, "format-xml", false, "XML format") + f.BoolVar(&c.Conf.FormatCsvList, "format-csv", false, "CSV format") f.BoolVar(&c.Conf.FormatOneEMail, "format-one-email", false, "Send all the host report via only one EMail (Specify with -to-email)") f.BoolVar(&c.Conf.FormatOneLineText, "format-one-line-text", false, @@ -241,7 +242,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} } if !(c.Conf.FormatJSON || c.Conf.FormatOneLineText || - c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML) { + c.Conf.FormatList || c.Conf.FormatFullText || c.Conf.FormatXML || c.Conf.FormatCsvList) { c.Conf.FormatList = true } diff --git a/config/config.go b/config/config.go index a4770e53..6454944f 100644 --- a/config/config.go +++ b/config/config.go @@ -151,6 +151,7 @@ type Config struct { FormatOneLineText bool `json:"formatOneLineText,omitempty"` FormatList bool `json:"formatList,omitempty"` FormatFullText bool `json:"formatFullText,omitempty"` + FormatCsvList bool `json:"formatCsvList,omitempty"` GZIP bool `json:"gzip,omitempty"` Diff bool `json:"diff,omitempty"` WpIgnoreInactive bool `json:"wpIgnoreInactive,omitempty"` diff --git a/report/localfile.go b/report/localfile.go index 43cea153..668c21be 100644 --- a/report/localfile.go +++ b/report/localfile.go @@ -96,6 +96,22 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) { return xerrors.Errorf("Failed to write XML. path: %s, err: %w", p, err) } } + + if c.Conf.FormatCsvList { + var p string + if c.Conf.Diff { + p = path + "_short_diff.csv" + } else { + p = path + "_short.csv" + } + + err := formatCsvList(r, p) + + if err == "" { + return xerrors.Errorf("Failed to write CSV. path: %s", p) + } + } + } return nil } diff --git a/report/stdout.go b/report/stdout.go index 2cb3b4b9..01bf1f7d 100644 --- a/report/stdout.go +++ b/report/stdout.go @@ -27,7 +27,7 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error { fmt.Print("\n") } - if c.Conf.FormatList { + if c.Conf.FormatList || c.Conf.FormatCsvList { for _, r := range rs { fmt.Println(formatList(r)) } diff --git a/report/util.go b/report/util.go index d059b28f..408ef7ea 100644 --- a/report/util.go +++ b/report/util.go @@ -2,6 +2,7 @@ package report import ( "bytes" + "encoding/csv" "encoding/json" "fmt" "io/ioutil" @@ -382,6 +383,51 @@ No CVE-IDs are found in updatable packages. return } +func formatCsvList(r models.ScanResult, path string) string { + data := [][]string{{"CVE-ID", "CVSS", "Attack", "PoC", "CERT", "Fixed", "NVD"}} + + for _, vinfo := range r.ScannedCves.ToSortedSlice() { + max := vinfo.MaxCvssScore().Value.Score + + exploits := "" + if 0 < len(vinfo.Exploits) || 0 < len(vinfo.Metasploits) { + exploits = "POC" + } + + link := "" + if strings.HasPrefix(vinfo.CveID, "CVE-") { + link = fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", vinfo.CveID) + } else if strings.HasPrefix(vinfo.CveID, "WPVDBID-") { + link = fmt.Sprintf("https://wpvulndb.com/vulnerabilities/%s", strings.TrimPrefix(vinfo.CveID, "WPVDBID-")) + } + + data = append(data, []string{ + vinfo.CveID, + fmt.Sprintf("%4.1f", max), + fmt.Sprintf("%s", vinfo.AttackVector()), + exploits, + vinfo.AlertDict.FormatSource(), + fmt.Sprintf("%s", vinfo.PatchStatus(r.Packages)), + link, + }) + + } + file, err := os.Create(path) + if err != nil { + return fmt.Sprintf("Unable to create file: %s", err) + } + + defer file.Close() + + writer := csv.NewWriter(file) + err = writer.WriteAll(data) + + if err != nil { + return fmt.Sprintf("Cannot write to file: %s", err) + } + return fmt.Sprintf("%s", data) +} + func cweURL(cweID string) string { return fmt.Sprintf("https://cwe.mitre.org/data/definitions/%s.html", strings.TrimPrefix(cweID, "CWE-"))