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 {