Add report subcommand, change scan options. Bump up ver #239
This commit is contained in:
214
report/util.go
214
report/util.go
@@ -20,89 +20,49 @@ package report
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
"github.com/gosuri/uitable"
|
||||
)
|
||||
|
||||
func ensureResultDir(scannedAt time.Time) (path string, err error) {
|
||||
jsonDirName := scannedAt.Format(time.RFC3339)
|
||||
const maxColWidth = 80
|
||||
|
||||
resultsDir := config.Conf.ResultsDir
|
||||
if len(resultsDir) == 0 {
|
||||
wd, _ := os.Getwd()
|
||||
resultsDir = filepath.Join(wd, "results")
|
||||
}
|
||||
jsonDir := filepath.Join(resultsDir, jsonDirName)
|
||||
|
||||
if err := os.MkdirAll(jsonDir, 0700); err != nil {
|
||||
return "", fmt.Errorf("Failed to create dir: %s", err)
|
||||
}
|
||||
|
||||
symlinkPath := filepath.Join(resultsDir, "current")
|
||||
if _, err := os.Lstat(symlinkPath); err == nil {
|
||||
if err := os.Remove(symlinkPath); err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to remove symlink. path: %s, err: %s", symlinkPath, err)
|
||||
func toScanSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
for _, r := range rs {
|
||||
cols := []interface{}{
|
||||
r.ServerName,
|
||||
fmt.Sprintf("%s%s", r.Family, r.Release),
|
||||
fmt.Sprintf("%d CVEs", len(r.ScannedCves)),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
}
|
||||
|
||||
if err := os.Symlink(jsonDir, symlinkPath); err != nil {
|
||||
return "", fmt.Errorf(
|
||||
"Failed to create symlink: path: %s, err: %s", symlinkPath, err)
|
||||
}
|
||||
return jsonDir, nil
|
||||
return fmt.Sprintf("%s\n", table)
|
||||
}
|
||||
|
||||
func toPlainText(scanResult models.ScanResult) (string, error) {
|
||||
serverInfo := scanResult.ServerInfo()
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for i := 0; i < len(serverInfo); i++ {
|
||||
buffer.WriteString("=")
|
||||
func toOneLineSummary(rs ...models.ScanResult) string {
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
for _, r := range rs {
|
||||
cols := []interface{}{
|
||||
r.ServerName,
|
||||
r.CveSummary(),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
}
|
||||
header := fmt.Sprintf("%s\n%s", serverInfo, buffer.String())
|
||||
|
||||
if len(scanResult.KnownCves) == 0 && len(scanResult.UnknownCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No unsecure packages.
|
||||
`, header), nil
|
||||
}
|
||||
|
||||
summary := ToPlainTextSummary(scanResult)
|
||||
scoredReport, unscoredReport := []string{}, []string{}
|
||||
scoredReport, unscoredReport = toPlainTextDetails(scanResult, scanResult.Family)
|
||||
|
||||
scored := strings.Join(scoredReport, "\n\n")
|
||||
|
||||
unscored := ""
|
||||
if !config.Conf.IgnoreUnscoredCves {
|
||||
unscored = strings.Join(unscoredReport, "\n\n")
|
||||
}
|
||||
|
||||
detail := fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
%s
|
||||
`,
|
||||
scored,
|
||||
unscored,
|
||||
)
|
||||
text := fmt.Sprintf("%s\n%s\n%s\n", header, summary, detail)
|
||||
|
||||
return text, nil
|
||||
return fmt.Sprintf("%s\n", table)
|
||||
}
|
||||
|
||||
// ToPlainTextSummary format summary for plain text.
|
||||
func ToPlainTextSummary(r models.ScanResult) string {
|
||||
func toShortPlainText(r models.ScanResult) string {
|
||||
stable := uitable.New()
|
||||
stable.MaxColWidth = 84
|
||||
stable.MaxColWidth = maxColWidth
|
||||
stable.Wrap = true
|
||||
|
||||
cves := r.KnownCves
|
||||
@@ -110,14 +70,45 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
cves = append(cves, r.UnknownCves...)
|
||||
}
|
||||
|
||||
for _, d := range cves {
|
||||
var scols []string
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(r.ServerInfo()); i++ {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
header := fmt.Sprintf("%s\n%s\n%s\t%s\n\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.CveSummary(),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
if len(cves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
`, header, r.Packages.ToUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
for _, d := range cves {
|
||||
var packsVer string
|
||||
for _, p := range d.Packages {
|
||||
packsVer += fmt.Sprintf(
|
||||
"%s -> %s\n", p.ToStringCurrentVersion(), p.ToStringNewVersion())
|
||||
}
|
||||
for _, n := range d.CpeNames {
|
||||
packsVer += n
|
||||
}
|
||||
|
||||
var scols []string
|
||||
switch {
|
||||
case config.Conf.Lang == "ja" &&
|
||||
0 < d.CveDetail.Jvn.CvssScore():
|
||||
|
||||
summary := d.CveDetail.Jvn.CveTitle()
|
||||
summary := fmt.Sprintf("%s\n%s\n%s\n%s",
|
||||
d.CveDetail.Jvn.CveTitle(),
|
||||
d.CveDetail.Jvn.Link(),
|
||||
distroLinks(d, r.Family)[0].url,
|
||||
packsVer,
|
||||
)
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
fmt.Sprintf("%-4.1f (%s)",
|
||||
@@ -126,8 +117,15 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
),
|
||||
summary,
|
||||
}
|
||||
|
||||
case 0 < d.CveDetail.CvssScore("en"):
|
||||
summary := d.CveDetail.Nvd.CveSummary()
|
||||
summary := fmt.Sprintf("%s\n%s/%s\n%s\n%s",
|
||||
d.CveDetail.Nvd.CveSummary(),
|
||||
cveDetailsBaseURL,
|
||||
d.CveDetail.CveID,
|
||||
distroLinks(d, r.Family)[0].url,
|
||||
packsVer,
|
||||
)
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
fmt.Sprintf("%-4.1f (%s)",
|
||||
@@ -137,10 +135,12 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
summary,
|
||||
}
|
||||
default:
|
||||
summary := fmt.Sprintf("%s\n%s",
|
||||
distroLinks(d, r.Family)[0].url, packsVer)
|
||||
scols = []string{
|
||||
d.CveDetail.CveID,
|
||||
"?",
|
||||
d.CveDetail.Nvd.CveSummary(),
|
||||
summary,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,12 +149,55 @@ func ToPlainTextSummary(r models.ScanResult) string {
|
||||
cols[i] = scols[i]
|
||||
}
|
||||
stable.AddRow(cols...)
|
||||
stable.AddRow("")
|
||||
}
|
||||
return fmt.Sprintf("%s", stable)
|
||||
return fmt.Sprintf("%s\n%s\n", header, stable)
|
||||
}
|
||||
|
||||
func toPlainTextDetails(data models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) {
|
||||
for _, cve := range data.KnownCves {
|
||||
func toFullPlainText(r models.ScanResult) string {
|
||||
serverInfo := r.ServerInfo()
|
||||
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(serverInfo); i++ {
|
||||
buf.WriteString("=")
|
||||
}
|
||||
header := fmt.Sprintf("%s\n%s\n%s\t%s\n",
|
||||
r.ServerInfo(),
|
||||
buf.String(),
|
||||
r.CveSummary(),
|
||||
r.Packages.ToUpdatablePacksSummary(),
|
||||
)
|
||||
|
||||
if len(r.KnownCves) == 0 && len(r.UnknownCves) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
`, header, r.Packages.ToUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
scoredReport, unscoredReport := []string{}, []string{}
|
||||
scoredReport, unscoredReport = toPlainTextDetails(r, r.Family)
|
||||
|
||||
unscored := ""
|
||||
if !config.Conf.IgnoreUnscoredCves {
|
||||
unscored = strings.Join(unscoredReport, "\n\n")
|
||||
}
|
||||
|
||||
scored := strings.Join(scoredReport, "\n\n")
|
||||
detail := fmt.Sprintf(`
|
||||
%s
|
||||
|
||||
%s
|
||||
`,
|
||||
scored,
|
||||
unscored,
|
||||
)
|
||||
return fmt.Sprintf("%s\n%s\n", header, detail)
|
||||
}
|
||||
|
||||
func toPlainTextDetails(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() {
|
||||
@@ -177,7 +220,7 @@ func toPlainTextDetails(data models.ScanResult, osFamily string) (scoredReport,
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, cve := range data.UnknownCves {
|
||||
for _, cve := range r.UnknownCves {
|
||||
unscoredReport = append(
|
||||
unscoredReport, toPlainTextUnknownCve(cve, osFamily))
|
||||
}
|
||||
@@ -187,7 +230,7 @@ func toPlainTextDetails(data models.ScanResult, osFamily string) (scoredReport,
|
||||
func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
|
||||
cveID := cveInfo.CveDetail.CveID
|
||||
dtable := uitable.New()
|
||||
dtable.MaxColWidth = 100
|
||||
dtable.MaxColWidth = maxColWidth
|
||||
dtable.Wrap = true
|
||||
dtable.AddRow(cveID)
|
||||
dtable.AddRow("-------------")
|
||||
@@ -201,6 +244,8 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string {
|
||||
for _, link := range dlinks {
|
||||
dtable.AddRow(link.title, link.url)
|
||||
}
|
||||
dtable = addPackageInfos(dtable, cveInfo.Packages)
|
||||
dtable = addCpeNames(dtable, cveInfo.CpeNames)
|
||||
|
||||
return fmt.Sprintf("%s", dtable)
|
||||
}
|
||||
@@ -211,7 +256,7 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string {
|
||||
jvn := cveDetail.Jvn
|
||||
|
||||
dtable := uitable.New()
|
||||
dtable.MaxColWidth = 100
|
||||
dtable.MaxColWidth = maxColWidth
|
||||
dtable.Wrap = true
|
||||
dtable.AddRow(cveID)
|
||||
dtable.AddRow("-------------")
|
||||
@@ -253,7 +298,7 @@ func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string {
|
||||
nvd := cveDetail.Nvd
|
||||
|
||||
dtable := uitable.New()
|
||||
dtable.MaxColWidth = 100
|
||||
dtable.MaxColWidth = maxColWidth
|
||||
dtable.Wrap = true
|
||||
dtable.AddRow(cveID)
|
||||
dtable.AddRow("-------------")
|
||||
@@ -357,13 +402,12 @@ func distroLinks(cveInfo models.CveInfo, osFamily string) []distroLink {
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
// addPackageInfos add package information related the CVE to table
|
||||
func addPackageInfos(table *uitable.Table, packs []models.PackageInfo) *uitable.Table {
|
||||
for i, p := range packs {
|
||||
var title string
|
||||
if i == 0 {
|
||||
title = "Package/CPE"
|
||||
title = "Package"
|
||||
}
|
||||
ver := fmt.Sprintf(
|
||||
"%s -> %s", p.ToStringCurrentVersion(), p.ToStringNewVersion())
|
||||
@@ -372,9 +416,9 @@ func addPackageInfos(table *uitable.Table, packs []models.PackageInfo) *uitable.
|
||||
return table
|
||||
}
|
||||
|
||||
func addCpeNames(table *uitable.Table, names []models.CpeName) *uitable.Table {
|
||||
for _, p := range names {
|
||||
table.AddRow("CPE", fmt.Sprintf("%s", p.Name))
|
||||
func addCpeNames(table *uitable.Table, names []string) *uitable.Table {
|
||||
for _, n := range names {
|
||||
table.AddRow("CPE", fmt.Sprintf("%s", n))
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user