From c6ad9ea57a0e224ebc1e47326a47f7e5ca12da6f Mon Sep 17 00:00:00 2001 From: Kota Kanbe Date: Wed, 31 May 2017 20:42:20 +0900 Subject: [PATCH] Fix tui --- commands/tui.go | 53 +++---- models/cvecontents.go | 8 -- models/packages.go | 32 ++++- report/report.go | 13 +- report/tui.go | 324 ++++++++++++++++++------------------------ report/util.go | 174 +---------------------- util/util.go | 12 ++ 7 files changed, 210 insertions(+), 406 deletions(-) diff --git a/commands/tui.go b/commands/tui.go index cd661a50..ca2f002a 100644 --- a/commands/tui.go +++ b/commands/tui.go @@ -24,6 +24,8 @@ import ( "path/filepath" c "github.com/future-architect/vuls/config" + "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/report" "github.com/future-architect/vuls/util" "github.com/google/subcommands" ) @@ -144,41 +146,22 @@ func (p *TuiCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) s } c.Conf.Pipe = p.pipe - // jsonDir, err := report.JSONDir(f.Args()) - // if err != nil { - // log.Errorf("Failed to read json dir under results: %s", err) - // return subcommands.ExitFailure - // } - // results, err := report.LoadScanResults(jsonDir) - // if err != nil { - // log.Errorf("Failed to read from JSON: %s", err) - // return subcommands.ExitFailure - // } + dir, err := report.JSONDir(f.Args()) + if err != nil { + util.Log.Errorf("Failed to read from JSON: %s", err) + return subcommands.ExitFailure + } + var res models.ScanResults + if res, err = report.LoadScanResults(dir); err != nil { + util.Log.Error(err) + return subcommands.ExitFailure + } + util.Log.Infof("Loaded: %s", dir) - // var filledResults []models.ScanResult - // for _, r := range results { - // if p.refreshCve || needToRefreshCve(r) { - // if c.Conf.CveDBType == "sqlite3" { - // if _, err := os.Stat(c.Conf.CveDBPath); os.IsNotExist(err) { - // log.Errorf("SQLite3 DB(CVE-Dictionary) is not exist: %s", - // c.Conf.CveDBPath) - // return subcommands.ExitFailure - // } - // } - - // if err := fillCveInfoFromCveDB(&r); err != nil { - // log.Errorf("Failed to fill CVE information: %s", err) - // return subcommands.ExitFailure - // } - - // if err := overwriteJSONFile(jsonDir, r); err != nil { - // log.Errorf("Failed to write JSON: %s", err) - // return subcommands.ExitFailure - // } - // } - // filledResults = append(filledResults, r) - // } - // return report.RunTui(filledResults) - return subcommands.ExitFailure + if res, err = report.FillCveInfos(res, dir); err != nil { + util.Log.Error(err) + return subcommands.ExitFailure + } + return report.RunTui(res) } diff --git a/models/cvecontents.go b/models/cvecontents.go index 69126f5d..573a666e 100644 --- a/models/cvecontents.go +++ b/models/cvecontents.go @@ -596,14 +596,6 @@ type Cpe struct { // References is a slice of Reference type References []Reference -// Find elements that matches the function passed in argument -func (r References) Find(f func(r Reference) bool) (refs []Reference) { - for _, rr := range r { - refs = append(refs, rr) - } - return -} - // Reference has a related link of the CVE type Reference struct { Source string diff --git a/models/packages.go b/models/packages.go index 8439f90a..19ed3c5f 100644 --- a/models/packages.go +++ b/models/packages.go @@ -18,6 +18,7 @@ along with this program. If not, see . package models import ( + "bytes" "fmt" "strings" ) @@ -110,10 +111,39 @@ func (p Package) FormatNewVer() string { // FormatVersionFromTo formats installed and new package version func (p Package) FormatVersionFromTo() string { - return fmt.Sprintf("%s-%s -> %s", + return fmt.Sprintf("%s-%s - %s", p.Name, p.FormatVer(), p.FormatNewVer()) } +// FormatChangelog formats the changelog +func (p Package) FormatChangelog() string { + buf := []string{} + if p.NewVersion == "" { + return "" + } + + packVer := fmt.Sprintf("%s-%s -> %s", + p.Name, p.FormatVer(), p.FormatNewVer()) + var delim bytes.Buffer + for i := 0; i < len(packVer); i++ { + delim.WriteString("-") + } + + clog := p.Changelog.Contents + if lines := strings.Split(clog, "\n"); len(lines) != 0 { + clog = strings.Join(lines[0:len(lines)-1], "\n") + } + + switch p.Changelog.Method { + case FailedToGetChangelog: + clog = "No changelogs" + case FailedToFindVersionInChangelog: + clog = "Failed to parse changelogs. For detials, check yourself" + } + buf = append(buf, packVer, delim.String(), clog) + return strings.Join(buf, "\n") +} + // Changelog has contents of changelog and how to get it. // Method: modesl.detectionMethodStr type Changelog struct { diff --git a/report/report.go b/report/report.go index 17619a2b..1b05770c 100644 --- a/report/report.go +++ b/report/report.go @@ -25,7 +25,6 @@ import ( "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/oval" "github.com/future-architect/vuls/util" - "github.com/k0kubun/pp" ) const ( @@ -72,9 +71,9 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro } //TODO remove debug code - for _, r := range filled { - pp.Printf("filled: %d\n", len(r.ScannedCves)) - } + // for _, r := range filled { + // pp.Printf("filled: %d\n", len(r.ScannedCves)) + // } filtered := []models.ScanResult{} for _, r := range filled { @@ -82,9 +81,9 @@ func FillCveInfos(rs []models.ScanResult, dir string) ([]models.ScanResult, erro } //TODO remove debug code - for _, r := range filtered { - pp.Printf("filtered: %d\n", len(r.ScannedCves)) - } + // for _, r := range filtered { + // pp.Printf("filtered: %d\n", len(r.ScannedCves)) + // } return filtered, nil } diff --git a/report/tui.go b/report/tui.go index 2a3c8fdc..afe638ca 100644 --- a/report/tui.go +++ b/report/tui.go @@ -20,21 +20,25 @@ package report import ( "bytes" "fmt" + "html/template" "os" + "sort" "strings" "time" log "github.com/Sirupsen/logrus" + "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" + "github.com/future-architect/vuls/util" "github.com/google/subcommands" "github.com/gosuri/uitable" "github.com/jroimartin/gocui" - cve "github.com/kotakanbe/go-cve-dictionary/models" ) var scanResults models.ScanResults var currentScanResult models.ScanResult -var currentCveInfo int +var vinfos []models.VulnInfo +var currentVinfo int var currentDetailLimitY int // RunTui execute main logic @@ -220,8 +224,7 @@ func movable(v *gocui.View, nextY int) (ok bool, yLimit int) { } return true, yLimit case "summary": - //TODO - // yLimit = len(currentScanResult.AllCves()) - 1 + yLimit = len(currentScanResult.ScannedCves) - 1 if yLimit < nextY { return false, yLimit } @@ -279,7 +282,7 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error { // ok, := movable(v, oy+cy+1) // _, maxY := v.Size() ok, _ := movable(v, oy+cy+1) - // log.Info(cy, oy, maxY, yLimit) + // log.Info(cy, oy) if !ok { return nil } @@ -290,6 +293,10 @@ func cursorDown(g *gocui.Gui, v *gocui.View) error { } onMovingCursorRedrawView(g, v) } + + cx, cy := v.Cursor() + ox, oy := v.Origin() + debug(g, fmt.Sprintf("%v, %v, %v, %v", cx, cy, ox, oy)) return nil } @@ -441,6 +448,7 @@ func changeHost(g *gocui.Gui, v *gocui.View) error { for _, r := range scanResults { if serverName == strings.TrimSpace(r.ServerInfoTui()) { currentScanResult = r + vinfos = r.ScannedCves.ToSortedSlice() break } } @@ -509,7 +517,8 @@ func showMsg(g *gocui.Gui, v *gocui.View) error { // maxX, maxY := v.Size() _, maxY := v.Size() - l := fmt.Sprintf("cy: %d, oy: %d, maxY: %d, yLimit: %d, curCve %d, ok: %v", cy, oy, maxY, yLimit, currentCveInfo, ok) + l := fmt.Sprintf("cy: %d, oy: %d, maxY: %d, yLimit: %d, curCve %d, ok: %v", + cy, oy, maxY, yLimit, currentVinfo, ok) // if v, err := g.SetView("msg", maxX/2-30, maxY/2, maxX/2+30, maxY/2+2); err != nil { if v, err := g.SetView("msg", 10, maxY/2, 10+50, maxY/2+2); err != nil { if err != gocui.ErrUnknownView { @@ -550,6 +559,20 @@ func layout(g *gocui.Gui) error { if err := setChangelogLayout(g); err != nil { return err } + + return nil +} + +func debug(g *gocui.Gui, str string) error { + if config.Conf.Debug { + maxX, maxY := g.Size() + if _, err := g.View("debug"); err != gocui.ErrUnknownView { + g.DeleteView("debug") + } + if v, err := g.SetView("debug", maxX/2-7, maxY/2, maxX/2+7, maxY/2+2); err != nil { + fmt.Fprintf(v, str) + } + } return nil } @@ -568,6 +591,7 @@ func setSideLayout(g *gocui.Gui) error { return fmt.Errorf("No scan results") } currentScanResult = scanResults[0] + vinfos = scanResults[0].ScannedCves.ToSortedSlice() if _, err := g.SetCurrentView("side"); err != nil { return err } @@ -601,72 +625,35 @@ func summaryLines() string { return "Error: Scan with --debug to view the details" } - //TODO - // indexFormat := "" - // if len(currentScanResult.AllCves()) < 10 { - // indexFormat = "[%1d]" - // } else if len(currentScanResult.AllCves()) < 100 { - // indexFormat = "[%2d]" - // } else { - // indexFormat = "[%3d]" - // } + indexFormat := "" + if len(currentScanResult.ScannedCves) < 10 { + indexFormat = "[%1d]" + } else if len(currentScanResult.ScannedCves) < 100 { + indexFormat = "[%2d]" + } else { + indexFormat = "[%3d]" + } - // for i, d := range currentScanResult.AllCves() { - // var cols []string - // //TODO - // var summary string - // if cont, found := d.Get(models.NVD); found { - // summary = cont.Summary - // } - // var cvssScore string - // if d.CvssV2Score() <= 0 { - // cvssScore = "| ?" - // } else { - // cvssScore = fmt.Sprintf("| %4.1f", d.CvssV2Score()) - // } - // cols = []string{ - // fmt.Sprintf(indexFormat, i+1), - // d.VulnInfo.CveID, - // cvssScore, - // fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), - // summary, - // } - // // if config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore() { - // // summary := d.CveDetail.Jvn.CveTitle() - // // cols = []string{ - // // fmt.Sprintf(indexFormat, i+1), - // // d.CveDetail.CveID, - // // fmt.Sprintf("| %4.1f", - // // d.CveDetail.CvssScore(config.Conf.Lang)), - // // fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), - // // summary, - // // } - // // } else { - // // summary := d.CveDetail.Nvd.CveSummary() + for i, vinfo := range vinfos { + summary := vinfo.CveContents.Summaries( + config.Conf.Lang, currentScanResult.Family)[0].Value + cvssScore := fmt.Sprintf("| %4.1f", + vinfo.CveContents.MaxCvssScore().Value.Score) - // // var cvssScore string - // // if d.CveDetail.CvssScore("en") <= 0 { - // // cvssScore = "| ?" - // // } else { - // // cvssScore = fmt.Sprintf("| %4.1f", - // // d.CveDetail.CvssScore(config.Conf.Lang)) - // // } - - // // cols = []string{ - // // fmt.Sprintf(indexFormat, i+1), - // // d.CveDetail.CveID, - // // cvssScore, - // // fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), - // // summary, - // // } - // // } - - // icols := make([]interface{}, len(cols)) - // for j := range cols { - // icols[j] = cols[j] - // } - // stable.AddRow(icols...) - // } + var cols []string + cols = []string{ + fmt.Sprintf(indexFormat, i+1), + vinfo.CveID, + cvssScore, + fmt.Sprintf("| %3d |", vinfo.Confidence.Score), + summary, + } + icols := make([]interface{}, len(cols)) + for j := range cols { + icols[j] = cols[j] + } + stable.AddRow(icols...) + } return fmt.Sprintf("%s", stable) } @@ -679,7 +666,7 @@ func setDetailLayout(g *gocui.Gui) error { } _, cy := summaryView.Cursor() _, oy := summaryView.Origin() - currentCveInfo = cy + oy + currentVinfo = cy + oy if v, err := g.SetView("detail", -1, int(float64(maxY)*0.2), int(float64(maxX)*0.5), maxY); err != nil { if err != gocui.ErrUnknownView { @@ -707,27 +694,26 @@ func setChangelogLayout(g *gocui.Gui) error { } _, cy := summaryView.Cursor() _, oy := summaryView.Origin() - currentCveInfo = cy + oy + currentVinfo = cy + oy if v, err := g.SetView("changelog", int(float64(maxX)*0.5), int(float64(maxY)*0.2), maxX, maxY); err != nil { if err != gocui.ErrUnknownView { return err } - //TODO - // if len(currentScanResult.Errors) != 0 || len(currentScanResult.AllCves()) == 0 { - // return nil - // } + if len(currentScanResult.Errors) != 0 || len(currentScanResult.ScannedCves) == 0 { + return nil + } lines := []string{} - //TODO - // cveInfo := currentScanResult.AllCves()[currentCveInfo] - // for _, pack := range cveInfo.Packages { - // for _, p := range currentScanResult.Packages { - // if pack.Name == p.Name { - // lines = append(lines, formatOneChangelog(p), "\n") - // } - // } - // } + vinfo := vinfos[currentVinfo] + for _, name := range vinfo.PackageNames { + pack := currentScanResult.Packages[name] + for _, p := range currentScanResult.Packages { + if pack.Name == p.Name { + lines = append(lines, p.FormatChangelog(), "\n") + } + } + } text := strings.Join(lines, "\n") fmt.Fprint(v, text) v.Editable = false @@ -740,14 +726,12 @@ func setChangelogLayout(g *gocui.Gui) error { type dataForTmpl struct { CveID string - CvssScore string - CvssVector string - CvssSeverity string + Cvsses []models.CveContentCvss Summary string Confidence models.Confidence - CweURL string - VulnSiteLinks []string - References []cve.Reference + Cwes []models.CveContentStr + Links []string + References []models.Reference Packages []string CpeNames []string PublishedDate time.Time @@ -755,123 +739,99 @@ type dataForTmpl struct { } func detailLines() (string, error) { - if len(currentScanResult.Errors) != 0 { + r := currentScanResult + if len(r.Errors) != 0 { return "", nil } - //TODO - // if len(currentScanResult.AllCves()) == 0 { - // return "No vulnerable packages", nil - // } - // cveInfo := currentScanResult.AllCves()[currentCveInfo] - // cveID := cveInfo.VulnInfo.CveID - - // tmpl, err := template.New("detail").Parse(detailTemplate()) - // if err != nil { - // return "", err - // } - - // var cvssSeverity, cvssVector, summary string - // var refs []cve.Reference - switch { - //TODO - // case config.Conf.Lang == "ja" && - // 0 < cveInfo.CveDetail.Jvn.CvssScore(): - // jvn := cveInfo.CveDetail.Jvn - // cvssSeverity = jvn.CvssSeverity() - // cvssVector = jvn.CvssVector() - // summary = fmt.Sprintf("%s\n%s", jvn.CveTitle(), jvn.CveSummary()) - // refs = jvn.VulnSiteReferences() - default: - // var nvd *models.CveContent - //TODO - // if cont, found := cveInfo.Get(models.NVD); found { - // nvd = cont - // } - // cvssSeverity = nvd.CvssSeverity() - // cvssVector = nvd.CvssVector() - // summary = nvd.Summary - // refs = nvd.VulnSiteReferences() + if len(r.ScannedCves) == 0 { + return "No vulnerable packages", nil } - //TODO - // cweURL := cweURL(cveInfo.CveDetail.CweID()) - // links := []string{ - // fmt.Sprintf("[NVD]( %s )", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)), - // fmt.Sprintf("[MITRE]( %s )", fmt.Sprintf("%s%s", mitreBaseURL, cveID)), - // fmt.Sprintf("[CveDetais]( %s )", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)), - // fmt.Sprintf("[CVSSv2 Calc]( %s )", fmt.Sprintf(cvssV2CalcBaseURL, cveID)), - // fmt.Sprintf("[CVSSv3 Calc]( %s )", fmt.Sprintf(cvssV3CalcBaseURL, cveID)), - // } - // dlinks := distroLinks(cveInfo, currentScanResult.Family) - // for _, link := range dlinks { - // links = append(links, fmt.Sprintf("[%s]( %s )", link.title, link.url)) - // } + tmpl, err := template.New("detail").Parse(mdTemplate) + if err != nil { + return "", err + } - //TODO - // var cvssScore string - // if cveInfo.CvssV2Score() == -1 { - // cvssScore = "?" - // // } else { - // // cvssScore = fmt.Sprintf("%4.1f", cveInfo.CveDetail.CvssScore(config.Conf.Lang)) - // } + vinfo := vinfos[currentVinfo] - // packages := []string{} - // for _, pack := range cveInfo.Packages { - // packages = append(packages, - // fmt.Sprintf( - // "%s -> %s", - // pack.FormatCurrentVer(), - // pack.FormatNewVer())) - // } + packsVer := []string{} + sort.Strings(vinfo.PackageNames) + for _, name := range vinfo.PackageNames { + // packages detected by OVAL may not be actually installed + if pack, ok := r.Packages[name]; ok { + packsVer = append(packsVer, pack.FormatVersionFromTo()) + } + } + sort.Strings(vinfo.CpeNames) + for _, name := range vinfo.CpeNames { + packsVer = append(packsVer, name) + } - // data := dataForTmpl{ - // CveID: cveID, - // CvssScore: cvssScore, - // CvssSeverity: cvssSeverity, - // CvssVector: cvssVector, - // Summary: summary, - // Confidence: cveInfo.VulnInfo.Confidence, - // //TODO - // // CweURL: cweURL, - // VulnSiteLinks: links, - // References: refs, - // Packages: packages, - // CpeNames: cveInfo.CpeNames, - // } + links := []string{vinfo.CveContents.SourceLinks( + config.Conf.Lang, r.Family, vinfo.CveID)[0].Value, + vinfo.Cvss2CalcURL(), + vinfo.Cvss3CalcURL()} + for _, url := range vinfo.VendorLinks(r.Family) { + links = append(links, url) + } + // links = util.Distinct(links) + + refs := []models.Reference{} + for _, rr := range vinfo.CveContents.References(r.Family) { + for _, ref := range rr.Value { + refs = append(refs, ref) + } + } + + data := dataForTmpl{ + CveID: vinfo.CveID, + Cvsses: append(vinfo.CveContents.Cvss3Scores(), vinfo.CveContents.MaxCvss2Score()), + Summary: vinfo.CveContents.Summaries(r.Lang, r.Family)[0].Value, + Confidence: vinfo.Confidence, + Cwes: vinfo.CveContents.CweIDs(r.Family), + Links: util.Distinct(links), + Packages: packsVer, + References: refs, + } buf := bytes.NewBuffer(nil) // create empty buffer - // if err := tmpl.Execute(buf, data); err != nil { - // return "", err - // } + if err := tmpl.Execute(buf, data); err != nil { + return "", err + } return string(buf.Bytes()), nil } -func detailTemplate() string { - return ` +const mdTemplate = ` {{.CveID}} ============== -CVSS Score +CVSS Scores -------------- -{{.CvssScore}} ({{.CvssSeverity}}) {{.CvssVector}} +{{range .Cvsses -}} +* {{.Value.Format}} ({{.Type}}) +{{end}} Summary -------------- {{.Summary }} -Confidence +Links -------------- - {{.Confidence }} +{{range $link := .Links -}} +* {{$link}} +{{end}} CWE -------------- - {{.CweURL }} +{{range .Cwes -}} +* {{.Value}} ({{.Type}}) +{{end}} Package/CPE -------------- @@ -882,12 +842,13 @@ Package/CPE {{range $name := .CpeNames -}} * {{$name}} {{end}} -Links + +Confidence -------------- -{{range $link := .VulnSiteLinks -}} -* {{$link}} -{{end}} + {{.Confidence }} + + References -------------- @@ -896,4 +857,3 @@ References {{end}} ` -} diff --git a/report/util.go b/report/util.go index db909bcd..7f898f84 100644 --- a/report/util.go +++ b/report/util.go @@ -18,7 +18,6 @@ along with this program. If not, see . package report import ( - "bytes" "encoding/json" "fmt" "io/ioutil" @@ -238,149 +237,6 @@ func formatFullPlainText(r models.ScanResult) string { return fmt.Sprintf("%s\n%s", header, table) } -//TODO -func formatPlainTextDetails(r models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) { - // for _, cve := range r.KnownCves { - // switch config.Conf.Lang { - // case "en": - // if 0 < cve.CveDetail.Nvd.CvssScore() { - // scoredReport = append( - // scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) - // } else { - // scoredReport = append( - // scoredReport, formatPlainTextUnknownCve(cve, osFamily)) - // } - // case "ja": - // if 0 < cve.CveDetail.Jvn.CvssScore() { - // scoredReport = append( - // scoredReport, formatPlainTextDetailsLangJa(cve, osFamily)) - // } else if 0 < cve.CveDetail.Nvd.CvssScore() { - // scoredReport = append( - // scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) - // } else { - // scoredReport = append( - // scoredReport, formatPlainTextUnknownCve(cve, osFamily)) - // } - // } - // } - // for _, cve := range r.UnknownCves { - // unscoredReport = append( - // unscoredReport, formatPlainTextUnknownCve(cve, osFamily)) - // } - return -} - -// func formatPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { -// cveID := cveInfo.VulnInfo.CveID -// dtable := uitable.New() -// dtable.MaxColWidth = maxColWidth -// dtable.Wrap = true -// dtable.AddRow(cveID) -// dtable.AddRow("-------------") -// dtable.AddRow("Score", "?") -// dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) -// dlinks := distroLinks(cveInfo, osFamily) -// for _, link := range dlinks { -// dtable.AddRow(link.title, link.url) -// } -// dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) -// dtable = addPackageInfos(dtable, cveInfo.Packages) -// dtable = addCpeNames(dtable, cveInfo.CpeNames) -// dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) - -// return fmt.Sprintf("%s", dtable) -// } - -//TODO -// func formatPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { -// return "TODO" -// cveDetail := cveInfo.CveDetail -// cveID := cveDetail.CveID -// jvn := cveDetail.Jvn - -// dtable := uitable.New() -// dtable.MaxColWidth = maxColWidth -// dtable.Wrap = true -// dtable.AddRow(cveID) -// dtable.AddRow("-------------") -// if score := cveDetail.Jvn.CvssScore(); 0 < score { -// dtable.AddRow("Score", -// fmt.Sprintf("%4.1f (%s)", -// cveDetail.Jvn.CvssScore(), -// jvn.CvssSeverity(), -// )) -// } else { -// dtable.AddRow("Score", "?") -// } -// dtable.AddRow("Vector", jvn.CvssVector()) -// dtable.AddRow("Title", jvn.CveTitle()) -// dtable.AddRow("Description", jvn.CveSummary()) -// dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID())) -// dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID())) - -// dtable.AddRow("JVN", jvn.Link()) -// dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) -// dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) -// dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) -// dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) -// dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) - -// dlinks := distroLinks(cveInfo, osFamily) -// for _, link := range dlinks { -// dtable.AddRow(link.title, link.url) -// } - -// dtable = addPackageInfos(dtable, cveInfo.Packages) -// dtable = addCpeNames(dtable, cveInfo.CpeNames) -// dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) - -// return fmt.Sprintf("%s", dtable) -// } - -//TODO -// func formatPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string { -// return "" -// cveDetail := d.CveDetail -// cveID := cveDetail.CveID -// nvd := cveDetail.Nvd - -// dtable := uitable.New() -// dtable.MaxColWidth = maxColWidth -// dtable.Wrap = true -// dtable.AddRow(cveID) -// dtable.AddRow("-------------") - -// if score := cveDetail.Nvd.CvssScore(); 0 < score { -// dtable.AddRow("Score", -// fmt.Sprintf("%4.1f (%s)", -// cveDetail.Nvd.CvssScore(), -// nvd.CvssSeverity(), -// )) -// } else { -// dtable.AddRow("Score", "?") -// } - -// dtable.AddRow("Vector", nvd.CvssVector()) -// dtable.AddRow("Summary", nvd.CveSummary()) -// dtable.AddRow("CWE", cweURL(cveDetail.CweID())) - -// dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) -// dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) -// dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) -// dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) -// dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) - -// links := distroLinks(d, osFamily) -// for _, link := range links { -// dtable.AddRow(link.title, link.url) -// } -// dtable = addPackageInfos(dtable, d.Packages) -// dtable = addCpeNames(dtable, d.CpeNames) -// dtable.AddRow("Confidence", d.VulnInfo.Confidence) - -// return fmt.Sprintf("%s\n", dtable) -// } - // type distroLink struct { // title string // url string @@ -481,40 +337,12 @@ func formatChangelogs(r models.ScanResult) string { if p.NewVersion == "" { continue } - clog := formatOneChangelog(p) + clog := p.FormatChangelog() buf = append(buf, clog, "\n\n") } return strings.Join(buf, "\n") } -func formatOneChangelog(p models.Package) string { - buf := []string{} - if p.NewVersion == "" { - return "" - } - - packVer := fmt.Sprintf("%s-%s -> %s", - p.Name, p.FormatVer(), p.FormatNewVer()) - var delim bytes.Buffer - for i := 0; i < len(packVer); i++ { - delim.WriteString("-") - } - - clog := p.Changelog.Contents - if lines := strings.Split(clog, "\n"); len(lines) != 0 { - clog = strings.Join(lines[0:len(lines)-1], "\n") - } - - switch p.Changelog.Method { - case models.FailedToGetChangelog: - clog = "No changelogs" - case models.FailedToFindVersionInChangelog: - clog = "Failed to parse changelogs. For detials, check yourself" - } - buf = append(buf, packVer, delim.String(), clog) - return strings.Join(buf, "\n") -} - func needToRefreshCve(r models.ScanResult) bool { if r.Lang != config.Conf.Lang { return true diff --git a/util/util.go b/util/util.go index 31f27d9e..b5707bac 100644 --- a/util/util.go +++ b/util/util.go @@ -136,6 +136,18 @@ func Truncate(str string, length int) string { return str } +// Distinct a slice +func Distinct(ss []string) (distincted []string) { + m := map[string]bool{} + for _, s := range ss { + if _, found := m[s]; !found { + m[s] = true + distincted = append(distincted, s) + } + } + return +} + // VendorLink returns a URL of the given OS family and CVEID //TODO // func VendorLink(family, cveID string) string {