diff --git a/README.ja.md b/README.ja.md index 38f705b1..0b08dcc5 100644 --- a/README.ja.md +++ b/README.ja.md @@ -1022,6 +1022,27 @@ Confidence 100 / YumUpdateSecurityMatch | CpeNameMatch | 100 | All |Search for NVD information with CPE name specified in config.toml| +### Changelog Part + +The scan results of Ubuntu, Debian, Raspbian or CentOS are also output Changelog in TUI or report with -format-full-text. +(RHEL, Amazon or FreeBSD will be available in the near future) + +The output change log includes only the difference between the currently installed version and candidate version. + +``` +tar-1.28-2.1 -> tar-1.28-2.1ubuntu0.1 +------------------------------------- +tar (1.28-2.1ubuntu0.1) xenial-security; urgency=medium + + * SECURITY UPDATE: extract pathname bypass + - debian/patches/CVE-2016-6321.patch: skip members whose names contain + ".." in src/extract.c. + - CVE-2016-6321 + + -- Marc Deslauriers Thu, 17 Nov 2016 11:06:07 -0500 +``` + + ## Example: Send scan results to Slack ``` $ vuls report \ diff --git a/README.md b/README.md index a3fd671b..2684fc1b 100644 --- a/README.md +++ b/README.md @@ -1028,6 +1028,27 @@ Confidence 100 / YumUpdateSecurityMatch | PkgAuditMatch | 100 | FreeBSD |Detection using pkg audit| | CpeNameMatch | 100 | All |Search for NVD information with CPE name specified in config.toml| + +### Changelog Part + +The scan results of Ubuntu, Debian, Raspbian or CentOS are also output Changelog in TUI or report with -format-full-text. +(RHEL, Amazon or FreeBSD will be available in the near future) + +The output change log includes only the difference between the currently installed version and candidate version. + +``` +tar-1.28-2.1 -> tar-1.28-2.1ubuntu0.1 +------------------------------------- +tar (1.28-2.1ubuntu0.1) xenial-security; urgency=medium + + * SECURITY UPDATE: extract pathname bypass + - debian/patches/CVE-2016-6321.patch: skip members whose names contain + ".." in src/extract.c. + - CVE-2016-6321 + + -- Marc Deslauriers Thu, 17 Nov 2016 11:06:07 -0500 +``` + ## Example: Send scan results to Slack ``` $ vuls report \ diff --git a/models/models.go b/models/models.go index 09cf8e33..4d92fddb 100644 --- a/models/models.go +++ b/models/models.go @@ -250,20 +250,43 @@ func (c Confidence) String() string { return fmt.Sprintf("%d / %s", c.Score, c.DetectionMethod) } +const ( + // CpeNameMatchStr is a String representation of CpeNameMatch + CpeNameMatchStr = "CpeNameMatch" + + // YumUpdateSecurityMatchStr is a String representation of YumUpdateSecurityMatch + YumUpdateSecurityMatchStr = "YumUpdateSecurityMatch" + + // PkgAuditMatchStr is a String representation of PkgAuditMatch + PkgAuditMatchStr = "PkgAuditMatch" + + // ChangelogExactMatchStr is a String representation of ChangelogExactMatch + ChangelogExactMatchStr = "ChangelogExactMatch" + + // ChangelogLenientMatchStr is a String representation of ChangelogLenientMatch + ChangelogLenientMatchStr = "ChangelogLenientMatch" + + // FailedToGetChangelog is a String representation of FailedToGetChangelog + FailedToGetChangelog = "FailedToGetChangelog" + + // FailedToFindVersionInChangelog is a String representation of FailedToFindVersionInChangelog + FailedToFindVersionInChangelog = "FailedToFindVersionInChangelog" +) + // CpeNameMatch is a ranking how confident the CVE-ID was deteted correctly -var CpeNameMatch = Confidence{100, "CpeNameMatch"} +var CpeNameMatch = Confidence{100, CpeNameMatchStr} // YumUpdateSecurityMatch is a ranking how confident the CVE-ID was deteted correctly -var YumUpdateSecurityMatch = Confidence{100, "YumUpdateSecurityMatch"} +var YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr} // PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly -var PkgAuditMatch = Confidence{100, "PkgAuditMatch"} +var PkgAuditMatch = Confidence{100, PkgAuditMatchStr} // ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly -var ChangelogExactMatch = Confidence{95, "ChangelogExactMatch"} +var ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr} // ChangelogLenientMatch is a ranking how confident the CVE-ID was deteted correctly -var ChangelogLenientMatch = Confidence{50, "ChangelogLenientMatch"} +var ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr} // VulnInfos is VulnInfo list, getter/setter, sortable methods. type VulnInfos []VulnInfo @@ -285,6 +308,9 @@ func (v *VulnInfo) NilSliceToEmpty() { if v.DistroAdvisories == nil { v.DistroAdvisories = []DistroAdvisory{} } + if v.Packages == nil { + v.Packages = PackageInfoList{} + } } // FindByCveID find by CVEID @@ -466,6 +492,14 @@ type PackageInfo struct { NewVersion string NewRelease string Repository string + Changelog Changelog +} + +// Changelog has contents of changelog and how to get it. +// Method: modesl.detectionMethodStr +type Changelog struct { + Contents string + Method string } // ToStringCurrentVersion returns package name-version-release diff --git a/report/azureblob.go b/report/azureblob.go index 2d84a820..85aa290e 100644 --- a/report/azureblob.go +++ b/report/azureblob.go @@ -47,7 +47,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatOneLineText { timestr := rs[0].ScannedAt.Format(time.RFC3339) k := fmt.Sprintf(timestr + "/summary.txt") - text := toOneLineSummary(rs...) + text := formatOneLineSummary(rs...) b := []byte(text) if err := createBlockBlob(cli, k, b); err != nil { return err @@ -69,7 +69,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatShortText { k := key + "_short.txt" - b := []byte(toShortPlainText(r)) + b := []byte(formatShortPlainText(r)) if err := createBlockBlob(cli, k, b); err != nil { return err } @@ -77,7 +77,7 @@ func (w AzureBlobWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatFullText { k := key + "_full.txt" - b := []byte(toFullPlainText(r)) + b := []byte(formatFullPlainText(r)) if err := createBlockBlob(cli, k, b); err != nil { return err } diff --git a/report/email.go b/report/email.go index 6449225f..48d7449c 100644 --- a/report/email.go +++ b/report/email.go @@ -40,7 +40,7 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) { for _, r := range rs { if conf.FormatOneEMail { - message += toFullPlainText(r) + "\r\n\r\n" + message += formatFullPlainText(r) + "\r\n\r\n" totalResult.KnownCves = append(totalResult.KnownCves, r.KnownCves...) totalResult.UnknownCves = append(totalResult.UnknownCves, r.UnknownCves...) } else { @@ -52,7 +52,7 @@ func (w EMailWriter) Write(rs ...models.ScanResult) (err error) { subject = fmt.Sprintf("%s%s %s", conf.EMail.SubjectPrefix, r.ServerInfo(), r.CveSummary()) } - message = toFullPlainText(r) + message = formatFullPlainText(r) if err := sender.Send(subject, message); err != nil { return err } @@ -68,7 +68,7 @@ One Line Summary %s`, - toOneLineSummary(rs...), message) + formatOneLineSummary(rs...), message) subject := fmt.Sprintf("%s %s", conf.EMail.SubjectPrefix, diff --git a/report/localfile.go b/report/localfile.go index f8d6e202..5571d109 100644 --- a/report/localfile.go +++ b/report/localfile.go @@ -38,7 +38,7 @@ type LocalFileWriter struct { func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatOneLineText { path := filepath.Join(w.CurrentDir, "summary.txt") - text := toOneLineSummary(rs...) + text := formatOneLineSummary(rs...) if err := writeFile(path, []byte(text), 0600); err != nil { return fmt.Errorf( "Failed to write to file. path: %s, err: %s", @@ -63,7 +63,7 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatShortText { p := path + "_short.txt" if err := writeFile( - p, []byte(toShortPlainText(r)), 0600); err != nil { + p, []byte(formatShortPlainText(r)), 0600); err != nil { return fmt.Errorf( "Failed to write text files. path: %s, err: %s", p, err) } @@ -72,7 +72,7 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatFullText { p := path + "_full.txt" if err := writeFile( - p, []byte(toFullPlainText(r)), 0600); err != nil { + p, []byte(formatFullPlainText(r)), 0600); err != nil { return fmt.Errorf( "Failed to write text files. path: %s, err: %s", p, err) } diff --git a/report/s3.go b/report/s3.go index 754d7577..abf1f98c 100644 --- a/report/s3.go +++ b/report/s3.go @@ -55,7 +55,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatOneLineText { timestr := rs[0].ScannedAt.Format(time.RFC3339) k := fmt.Sprintf(timestr + "/summary.txt") - text := toOneLineSummary(rs...) + text := formatOneLineSummary(rs...) if err := putObject(svc, k, []byte(text)); err != nil { return err } @@ -76,7 +76,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatShortText { k := key + "_short.txt" - text := toShortPlainText(r) + text := formatShortPlainText(r) if err := putObject(svc, k, []byte(text)); err != nil { return err } @@ -84,7 +84,7 @@ func (w S3Writer) Write(rs ...models.ScanResult) (err error) { if c.Conf.FormatFullText { k := key + "_full.txt" - text := toFullPlainText(r) + text := formatFullPlainText(r) if err := putObject(svc, k, []byte(text)); err != nil { return err } diff --git a/report/stdout.go b/report/stdout.go index 21c65565..730e8247 100644 --- a/report/stdout.go +++ b/report/stdout.go @@ -32,7 +32,7 @@ func (w StdoutWriter) WriteScanSummary(rs ...models.ScanResult) { fmt.Printf("\n\n") fmt.Println("One Line Summary") fmt.Println("================") - fmt.Printf("%s\n", toScanSummary(rs...)) + fmt.Printf("%s\n", formatScanSummary(rs...)) } func (w StdoutWriter) Write(rs ...models.ScanResult) error { @@ -40,19 +40,19 @@ func (w StdoutWriter) Write(rs ...models.ScanResult) error { fmt.Print("\n\n") fmt.Println("One Line Summary") fmt.Println("================") - fmt.Println(toOneLineSummary(rs...)) + fmt.Println(formatOneLineSummary(rs...)) fmt.Print("\n") } if c.Conf.FormatShortText { for _, r := range rs { - fmt.Println(toShortPlainText(r)) + fmt.Println(formatShortPlainText(r)) } } if c.Conf.FormatFullText { for _, r := range rs { - fmt.Println(toFullPlainText(r)) + fmt.Println(formatFullPlainText(r)) } } return nil diff --git a/report/tui.go b/report/tui.go index f6fbfeda..bb9ff5c4 100644 --- a/report/tui.go +++ b/report/tui.go @@ -133,6 +133,27 @@ func keybindings(g *gocui.Gui) (err error) { errs = append(errs, g.SetKeybinding("detail", gocui.KeyCtrlP, gocui.ModNone, previousSummary)) errs = append(errs, g.SetKeybinding("detail", gocui.KeyEnter, gocui.ModNone, nextView)) + // changelog + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyTab, gocui.ModNone, nextView)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlQ, gocui.ModNone, previousView)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlH, gocui.ModNone, nextView)) + // errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlL, gocui.ModNone, nextView)) + // errs = append(errs, g.SetKeybinding("changelog", gocui.KeyArrowUp, gocui.ModAlt, previousView)) + // errs = append(errs, g.SetKeybinding("changelog", gocui.KeyArrowLeft, gocui.ModAlt, nextView)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyArrowDown, gocui.ModNone, cursorDown)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyArrowUp, gocui.ModNone, cursorUp)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlJ, gocui.ModNone, cursorDown)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlK, gocui.ModNone, cursorUp)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlD, gocui.ModNone, cursorPageDown)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlU, gocui.ModNone, cursorPageUp)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeySpace, gocui.ModNone, cursorPageDown)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyBackspace, gocui.ModNone, cursorPageUp)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyBackspace2, gocui.ModNone, cursorPageUp)) + // errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlM, gocui.ModNone, cursorMoveMiddle)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlN, gocui.ModNone, nextSummary)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyCtrlP, gocui.ModNone, previousSummary)) + errs = append(errs, g.SetKeybinding("changelog", gocui.KeyEnter, gocui.ModNone, nextView)) + // errs = append(errs, g.SetKeybinding("msg", gocui.KeyEnter, gocui.ModNone, delMsg)) // errs = append(errs, g.SetKeybinding("detail", gocui.KeyEnter, gocui.ModNone, showMsg)) @@ -162,6 +183,8 @@ func nextView(g *gocui.Gui, v *gocui.View) error { case "summary": _, err = g.SetCurrentView("detail") case "detail": + _, err = g.SetCurrentView("changelog") + case "changelog": _, err = g.SetCurrentView("side") default: _, err = g.SetCurrentView("summary") @@ -182,6 +205,8 @@ func previousView(g *gocui.Gui, v *gocui.View) error { _, err = g.SetCurrentView("side") case "detail": _, err = g.SetCurrentView("summary") + case "changelog": + _, err = g.SetCurrentView("detail") default: _, err = g.SetCurrentView("side") } @@ -207,6 +232,11 @@ func movable(v *gocui.View, nextY int) (ok bool, yLimit int) { return false, currentDetailLimitY } return true, currentDetailLimitY + case "changelog": + if currentDetailLimitY < nextY { + return false, currentDetailLimitY + } + return true, currentDetailLimitY default: return true, 0 } @@ -217,7 +247,7 @@ func pageUpDownJumpCount(v *gocui.View) int { switch v.Name() { case "side", "summary": jump = 8 - case "detail": + case "detail", "changelog": jump = 30 default: jump = 8 @@ -232,6 +262,9 @@ func onMovingCursorRedrawView(g *gocui.Gui, v *gocui.View) error { if err := redrawDetail(g); err != nil { return err } + if err := redrawChangelog(g); err != nil { + return err + } case "side": if err := changeHost(g, v); err != nil { return err @@ -395,6 +428,9 @@ func changeHost(g *gocui.Gui, v *gocui.View) error { if err := g.DeleteView("detail"); err != nil { return err } + if err := g.DeleteView("changelog"); err != nil { + return err + } _, cy := v.Cursor() l, err := v.Line(cy) @@ -416,6 +452,9 @@ func changeHost(g *gocui.Gui, v *gocui.View) error { if err := setDetailLayout(g); err != nil { return err } + if err := setChangelogLayout(g); err != nil { + return err + } return nil } @@ -430,6 +469,17 @@ func redrawDetail(g *gocui.Gui) error { return nil } +func redrawChangelog(g *gocui.Gui) error { + if err := g.DeleteView("changelog"); err != nil { + return err + } + + if err := setChangelogLayout(g); err != nil { + return err + } + return nil +} + func getLine(g *gocui.Gui, v *gocui.View) error { var l string var err error @@ -498,12 +548,15 @@ func layout(g *gocui.Gui) error { if err := setDetailLayout(g); err != nil { return err } + if err := setChangelogLayout(g); err != nil { + return err + } return nil } func setSideLayout(g *gocui.Gui) error { _, maxY := g.Size() - if v, err := g.SetView("side", -1, -1, 40, maxY); err != nil { + if v, err := g.SetView("side", -1, -1, 40, int(float64(maxY)*0.2)); err != nil { if err != gocui.ErrUnknownView { return err } @@ -614,14 +667,10 @@ func setDetailLayout(g *gocui.Gui) error { _, oy := summaryView.Origin() currentCveInfo = cy + oy - if v, err := g.SetView("detail", 40, int(float64(maxY)*0.2), maxX, maxY); err != nil { + if v, err := g.SetView("detail", -1, int(float64(maxY)*0.2), int(float64(maxX)*0.5), maxY); err != nil { if err != gocui.ErrUnknownView { return err } - // text := report.ToPlainTextDetailsLangEn( - // currentScanResult.KnownCves[currentCveInfo], - // currentScanResult.Family) - text, err := detailLines() if err != nil { return err @@ -635,6 +684,44 @@ func setDetailLayout(g *gocui.Gui) error { return nil } +func setChangelogLayout(g *gocui.Gui) error { + maxX, maxY := g.Size() + + summaryView, err := g.View("summary") + if err != nil { + return err + } + _, cy := summaryView.Cursor() + _, oy := summaryView.Origin() + currentCveInfo = 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 + } + if len(currentScanResult.Errors) != 0 || len(currentScanResult.AllCves()) == 0 { + return nil + } + + lines := []string{} + 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") + } + } + } + text := strings.Join(lines, "\n") + fmt.Fprint(v, text) + v.Editable = false + v.Wrap = true + + currentDetailLimitY = len(strings.Split(text, "\n")) - 1 + } + return nil +} + type dataForTmpl struct { CveID string CvssScore string diff --git a/report/util.go b/report/util.go index dee86b15..a2643c26 100644 --- a/report/util.go +++ b/report/util.go @@ -29,7 +29,7 @@ import ( const maxColWidth = 80 -func toScanSummary(rs ...models.ScanResult) string { +func formatScanSummary(rs ...models.ScanResult) string { table := uitable.New() table.MaxColWidth = maxColWidth table.Wrap = true @@ -55,7 +55,7 @@ func toScanSummary(rs ...models.ScanResult) string { return fmt.Sprintf("%s\n", table) } -func toOneLineSummary(rs ...models.ScanResult) string { +func formatOneLineSummary(rs ...models.ScanResult) string { table := uitable.New() table.MaxColWidth = maxColWidth table.Wrap = true @@ -79,7 +79,7 @@ func toOneLineSummary(rs ...models.ScanResult) string { return fmt.Sprintf("%s\n", table) } -func toShortPlainText(r models.ScanResult) string { +func formatShortPlainText(r models.ScanResult) string { stable := uitable.New() stable.MaxColWidth = maxColWidth stable.Wrap = true @@ -181,7 +181,7 @@ No CVE-IDs are found in updatable packages. return fmt.Sprintf("%s\n%s\n", header, stable) } -func toFullPlainText(r models.ScanResult) string { +func formatFullPlainText(r models.ScanResult) string { serverInfo := r.ServerInfo() var buf bytes.Buffer @@ -210,7 +210,7 @@ No CVE-IDs are found in updatable packages. } scoredReport, unscoredReport := []string{}, []string{} - scoredReport, unscoredReport = toPlainTextDetails(r, r.Family) + scoredReport, unscoredReport = formatPlainTextDetails(r, r.Family) unscored := "" if !config.Conf.IgnoreUnscoredCves { @@ -226,41 +226,41 @@ No CVE-IDs are found in updatable packages. scored, unscored, ) - return fmt.Sprintf("%s\n%s\n", header, detail) + return fmt.Sprintf("%s\n%s\n%s", header, detail, formatChangelogs(r)) } -func toPlainTextDetails(r models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) { +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, toPlainTextDetailsLangEn(cve, osFamily)) + scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) } else { scoredReport = append( - scoredReport, toPlainTextUnknownCve(cve, osFamily)) + scoredReport, formatPlainTextUnknownCve(cve, osFamily)) } case "ja": if 0 < cve.CveDetail.Jvn.CvssScore() { scoredReport = append( - scoredReport, toPlainTextDetailsLangJa(cve, osFamily)) + scoredReport, formatPlainTextDetailsLangJa(cve, osFamily)) } else if 0 < cve.CveDetail.Nvd.CvssScore() { scoredReport = append( - scoredReport, toPlainTextDetailsLangEn(cve, osFamily)) + scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) } else { scoredReport = append( - scoredReport, toPlainTextUnknownCve(cve, osFamily)) + scoredReport, formatPlainTextUnknownCve(cve, osFamily)) } } } for _, cve := range r.UnknownCves { unscoredReport = append( - unscoredReport, toPlainTextUnknownCve(cve, osFamily)) + unscoredReport, formatPlainTextUnknownCve(cve, osFamily)) } return } -func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { +func formatPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { cveID := cveInfo.CveDetail.CveID dtable := uitable.New() dtable.MaxColWidth = maxColWidth @@ -284,7 +284,7 @@ func toPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { return fmt.Sprintf("%s", dtable) } -func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { +func formatPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { cveDetail := cveInfo.CveDetail cveID := cveDetail.CveID jvn := cveDetail.Jvn @@ -327,7 +327,7 @@ func toPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { return fmt.Sprintf("%s", dtable) } -func toPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string { +func formatPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string { cveDetail := d.CveDetail cveID := cveDetail.CveID nvd := cveDetail.Nvd @@ -467,3 +467,43 @@ func cweURL(cweID string) string { func cweJvnURL(cweID string) string { return fmt.Sprintf("http://jvndb.jvn.jp/ja/cwe/%s.html", cweID) } + +func formatChangelogs(r models.ScanResult) string { + buf := []string{} + for _, p := range r.Packages { + if p.NewVersion == "" { + continue + } + clog := formatOneChangelog(p) + buf = append(buf, clog, "\n\n") + } + return strings.Join(buf, "\n") +} + +func formatOneChangelog(p models.PackageInfo) string { + buf := []string{} + if p.NewVersion == "" { + return "" + } + + packVer := fmt.Sprintf("%s -> %s", + p.ToStringCurrentVersion(), p.ToStringNewVersion()) + 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") +} diff --git a/scan/debian.go b/scan/debian.go index 110b900d..70658e5c 100644 --- a/scan/debian.go +++ b/scan/debian.go @@ -311,7 +311,8 @@ func (o *debian) ensureChangelogCache(current cache.Meta) (*cache.Meta, error) { // Search from cache cached, found, err := cache.DB.GetMeta(current.Name) if err != nil { - return nil, fmt.Errorf("Failed to get meta. err: %s", err) + return nil, fmt.Errorf( + "Failed to get meta. Please remove cache.db and then try again. err: %s", err) } if !found { @@ -447,7 +448,7 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache func(p models.PackageInfo) { changelog := o.getChangelogCache(meta, p) if 0 < len(changelog) { - cveIDs := o.getCveIDsFromChangelog(changelog, p.Name, p.Version) + cveIDs, _ := o.getCveIDsFromChangelog(changelog, p.Name, p.Version) resChan <- struct { models.PackageInfo DetectedCveIDs @@ -549,9 +550,9 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, er cmd := "" switch o.Distro.Family { case "ubuntu", "raspbian": - cmd = fmt.Sprintf(`apt-get changelog %s | grep '\(urgency\|CVE\)'`, pack.Name) + cmd = fmt.Sprintf(`PAGER=cat apt-get -q=2 changelog %s`, pack.Name) case "debian": - cmd = fmt.Sprintf(`env PAGER=cat aptitude changelog %s | grep '\(urgency\|CVE\)'`, pack.Name) + cmd = fmt.Sprintf(`PAGER=cat aptitude -q=2 changelog %s`, pack.Name) } cmd = util.PrependProxyEnv(cmd) @@ -562,40 +563,65 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, er return nil, nil } - if 0 < len(strings.TrimSpace(r.Stdout)) { - err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, r.Stdout) + stdout := strings.Replace(r.Stdout, "\r", "", -1) + cveIDs, clog := o.getCveIDsFromChangelog( + stdout, pack.Name, pack.Version) + + if clog.Method != models.FailedToGetChangelog { + err := cache.DB.PutChangelog(o.getServerInfo().GetServerName(), pack.Name, clog.Contents) if err != nil { return nil, fmt.Errorf("Failed to put changelog into cache") } } + // No error will be returned. Only logging. - return o.getCveIDsFromChangelog(r.Stdout, pack.Name, pack.Version), nil + return cveIDs, nil } func (o *debian) getCveIDsFromChangelog(changelog string, - packName string, versionOrLater string) []DetectedCveID { + packName string, versionOrLater string) ([]DetectedCveID, models.Changelog) { - if cveIDs, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil { - return cveIDs + if cveIDs, relevantChangelog, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil { + return cveIDs, relevantChangelog } + //TODO switch case ubuntu, debian ver := strings.Split(versionOrLater, "ubuntu")[0] - if cveIDs, err := o.parseChangelog(changelog, packName, ver); err == nil { - return cveIDs + if cveIDs, relevantChangelog, err := o.parseChangelog(changelog, packName, ver); err == nil { + return cveIDs, relevantChangelog } splittedByColon := strings.Split(versionOrLater, ":") if 1 < len(splittedByColon) { ver = splittedByColon[1] } - cveIDs, err := o.parseChangelog(changelog, packName, ver) + cveIDs, relevantChangelog, err := o.parseChangelog(changelog, packName, ver) if err == nil { - return cveIDs + return cveIDs, relevantChangelog + } + + ver = strings.Split(ver, "ubuntu")[0] + if cveIDs, relevantChangelog, err := o.parseChangelog(changelog, packName, ver); err == nil { + return cveIDs, relevantChangelog } // Only logging the error. o.log.Error(err) - return []DetectedCveID{} + + for i, p := range o.Packages { + if p.Name == packName { + o.Packages[i].Changelog = models.Changelog{ + Contents: "", + Method: models.FailedToFindVersionInChangelog, + } + } + } + + // If the version is not in changelog, return entire changelog to put into cache + return []DetectedCveID{}, models.Changelog{ + Contents: changelog, + Method: models.FailedToFindVersionInChangelog, + } } // DetectedCveID has CveID, Confidence and DetectionMethod fields @@ -609,13 +635,13 @@ type DetectedCveID struct { // DetectedCveIDs is a slice of DetectedCveID type DetectedCveIDs []DetectedCveID +var cveRe = regexp.MustCompile(`(CVE-\d{4}-\d{4,})`) + // Collect CVE-IDs included in the changelog. // The version which specified in argument(versionOrLater) is excluded. -func (o *debian) parseChangelog(changelog string, - packName string, versionOrLater string) (cves []DetectedCveID, err error) { +func (o *debian) parseChangelog(changelog string, packName string, versionOrLater string) ([]DetectedCveID, models.Changelog, error) { - cveIDs := []string{} - cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`) + buf, cveIDs := []string{}, []string{} stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater))) stopLineFound := false lenientStopLineFound := false @@ -626,6 +652,7 @@ func (o *debian) parseChangelog(changelog string, lenientRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLaterlenient))) lines := strings.Split(changelog, "\n") for _, line := range lines { + buf = append(buf, line) if match := stopRe.MatchString(line); match { // o.log.Debugf("Found the stop line: %s", line) stopLineFound = true @@ -640,21 +667,38 @@ func (o *debian) parseChangelog(changelog string, } } if !stopLineFound && !lenientStopLineFound { - return cves, fmt.Errorf( - "Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s", - packName, - versionOrLater, - ) + return nil, models.Changelog{ + Contents: "", + Method: models.FailedToFindVersionInChangelog, + }, fmt.Errorf( + "Failed to scan CVE IDs. The version is not in changelog. name: %s, version: %s", + packName, + versionOrLater, + ) } - for _, id := range cveIDs { - confidence := models.ChangelogExactMatch - if lenientStopLineFound { - confidence = models.ChangelogLenientMatch + confidence := models.ChangelogExactMatch + if lenientStopLineFound { + confidence = models.ChangelogLenientMatch + } + + clog := models.Changelog{ + Contents: strings.Join(buf, "\n"), + Method: string(confidence.DetectionMethod), + } + + for i, p := range o.Packages { + if p.Name == packName { + o.Packages[i].Changelog = clog } + } + + cves := []DetectedCveID{} + for _, id := range cveIDs { cves = append(cves, DetectedCveID{id, confidence}) } - return + + return cves, clog, nil } func (o *debian) splitAptCachePolicy(stdout string) map[string]string { diff --git a/scan/debian_test.go b/scan/debian_test.go index 4b6c343f..b011a2dc 100644 --- a/scan/debian_test.go +++ b/scan/debian_test.go @@ -61,8 +61,9 @@ func TestParseScannedPackagesLineDebian(t *testing.T) { func TestGetCveIDParsingChangelog(t *testing.T) { var tests = []struct { - in []string - expected []DetectedCveID + in []string + cveIDs []DetectedCveID + changelog models.Changelog }{ { // verubuntu1 @@ -77,18 +78,25 @@ CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) CVE-2015-3210: heap buffer overflow in pcre_compile2() / systemd (228-5) unstable; urgency=medium systemd (228-4) unstable; urgency=medium -systemd (228-3) unstable; urgency=medium -systemd (228-2) unstable; urgency=medium -systemd (228-1) unstable; urgency=medium -systemd (227-3) unstable; urgency=medium -systemd (227-2) unstable; urgency=medium -systemd (227-1) unstable; urgency=medium`, +systemd (228-3) unstable; urgency=medium`, }, []DetectedCveID{ {"CVE-2015-2325", models.ChangelogExactMatch}, {"CVE-2015-2326", models.ChangelogExactMatch}, {"CVE-2015-3210", models.ChangelogExactMatch}, }, + models.Changelog{ + Contents: `systemd (229-2) unstable; urgency=medium +systemd (229-1) unstable; urgency=medium +systemd (228-6) unstable; urgency=medium +CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) +CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) +CVE-2015-3210: heap buffer overflow in pcre_compile2() / +systemd (228-5) unstable; urgency=medium +systemd (228-4) unstable; urgency=medium`, + //TODO + Method: models.ChangelogExactMatchStr, + }, }, { // ver @@ -96,22 +104,36 @@ systemd (227-1) unstable; urgency=medium`, "libpcre3", "2:8.35-7.1ubuntu1", `pcre3 (2:8.38-2) unstable; urgency=low -pcre3 (2:8.38-1) unstable; urgency=low -pcre3 (2:8.35-8) unstable; urgency=low -pcre3 (2:8.35-7.4) unstable; urgency=medium -pcre3 (2:8.35-7.3) unstable; urgency=medium -pcre3 (2:8.35-7.2) unstable; urgency=low -CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) -CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) -CVE-2015-3210: heap buffer overflow in pcre_compile2() / -pcre3 (2:8.35-7.1) unstable; urgency=medium -pcre3 (2:8.35-7) unstable; urgency=medium`, + pcre3 (2:8.38-1) unstable; urgency=low + pcre3 (2:8.35-8) unstable; urgency=low + pcre3 (2:8.35-7.4) unstable; urgency=medium + pcre3 (2:8.35-7.3) unstable; urgency=medium + pcre3 (2:8.35-7.2) unstable; urgency=low + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: heap buffer overflow in pcre_compile2() / + pcre3 (2:8.35-7.1) unstable; urgency=medium + pcre3 (2:8.35-7) unstable; urgency=medium`, }, []DetectedCveID{ {"CVE-2015-2325", models.ChangelogExactMatch}, {"CVE-2015-2326", models.ChangelogExactMatch}, {"CVE-2015-3210", models.ChangelogExactMatch}, }, + models.Changelog{ + Contents: `pcre3 (2:8.38-2) unstable; urgency=low + pcre3 (2:8.38-1) unstable; urgency=low + pcre3 (2:8.35-8) unstable; urgency=low + pcre3 (2:8.35-7.4) unstable; urgency=medium + pcre3 (2:8.35-7.3) unstable; urgency=medium + pcre3 (2:8.35-7.2) unstable; urgency=low + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: heap buffer overflow in pcre_compile2() / + pcre3 (2:8.35-7.1) unstable; urgency=medium`, + //TODO + Method: models.ChangelogExactMatchStr, + }, }, { // ver-ubuntu3 @@ -119,62 +141,58 @@ pcre3 (2:8.35-7) unstable; urgency=medium`, "sysvinit", "2.88dsf-59.2ubuntu3", `sysvinit (2.88dsf-59.3ubuntu1) xenial; urgency=low -sysvinit (2.88dsf-59.3) unstable; urgency=medium -CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) -CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) -CVE-2015-3210: heap buffer overflow in pcre_compile2() / -sysvinit (2.88dsf-59.2ubuntu3) xenial; urgency=medium -sysvinit (2.88dsf-59.2ubuntu2) wily; urgency=medium -sysvinit (2.88dsf-59.2ubuntu1) wily; urgency=medium -CVE-2015-2321: heap buffer overflow in pcre_compile2(). (Closes: #783285) -sysvinit (2.88dsf-59.2) unstable; urgency=medium -sysvinit (2.88dsf-59.1ubuntu3) wily; urgency=medium -CVE-2015-2322: heap buffer overflow in pcre_compile2(). (Closes: #783285) -sysvinit (2.88dsf-59.1ubuntu2) wily; urgency=medium -sysvinit (2.88dsf-59.1ubuntu1) wily; urgency=medium -sysvinit (2.88dsf-59.1) unstable; urgency=medium -CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) -sysvinit (2.88dsf-59) unstable; urgency=medium -sysvinit (2.88dsf-58) unstable; urgency=low -sysvinit (2.88dsf-57) unstable; urgency=low`, + sysvinit (2.88dsf-59.3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: heap buffer overflow in pcre_compile2() / + sysvinit (2.88dsf-59.2ubuntu3) xenial; urgency=medium + sysvinit (2.88dsf-59.2ubuntu2) wily; urgency=medium + sysvinit (2.88dsf-59.2ubuntu1) wily; urgency=medium + CVE-2015-2321: heap buffer overflow in pcre_compile2(). (Closes: #783285) + sysvinit (2.88dsf-59.2) unstable; urgency=medium + sysvinit (2.88dsf-59.1ubuntu3) wily; urgency=medium + CVE-2015-2322: heap buffer overflow in pcre_compile2(). (Closes: #783285) + sysvinit (2.88dsf-59.1ubuntu2) wily; urgency=medium + sysvinit (2.88dsf-59.1ubuntu1) wily; urgency=medium + sysvinit (2.88dsf-59.1) unstable; urgency=medium + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + sysvinit (2.88dsf-59) unstable; urgency=medium + sysvinit (2.88dsf-58) unstable; urgency=low + sysvinit (2.88dsf-57) unstable; urgency=low`, }, []DetectedCveID{ {"CVE-2015-2325", models.ChangelogExactMatch}, {"CVE-2015-2326", models.ChangelogExactMatch}, {"CVE-2015-3210", models.ChangelogExactMatch}, }, + models.Changelog{ + Contents: `sysvinit (2.88dsf-59.3ubuntu1) xenial; urgency=low + sysvinit (2.88dsf-59.3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: heap buffer overflow in pcre_compile2() / + sysvinit (2.88dsf-59.2ubuntu3) xenial; urgency=medium`, + //TODO + Method: models.ChangelogExactMatchStr, + }, }, { // 1:ver-ubuntu3 []string{ "bsdutils", "1:2.27.1-1ubuntu3", - ` util-linux (2.27.1-3ubuntu1) xenial; urgency=medium -util-linux (2.27.1-3) unstable; urgency=medium -CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) -CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) -CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / -util-linux (2.27.1-2) unstable; urgency=medium -util-linux (2.27.1-1ubuntu4) xenial; urgency=medium -util-linux (2.27.1-1ubuntu3) xenial; urgency=medium -util-linux (2.27.1-1ubuntu2) xenial; urgency=medium -util-linux (2.27.1-1ubuntu1) xenial; urgency=medium -util-linux (2.27.1-1) unstable; urgency=medium -util-linux (2.27-3ubuntu1) xenial; urgency=medium -util-linux (2.27-3) unstable; urgency=medium -util-linux (2.27-2) unstable; urgency=medium -util-linux (2.27-1) unstable; urgency=medium -util-linux (2.27~rc2-2) experimental; urgency=medium -util-linux (2.27~rc2-1) experimental; urgency=medium -util-linux (2.27~rc1-1) experimental; urgency=medium -util-linux (2.26.2-9) unstable; urgency=medium -util-linux (2.26.2-8) experimental; urgency=medium -util-linux (2.26.2-7) experimental; urgency=medium -util-linux (2.26.2-6ubuntu3) wily; urgency=medium -CVE-2015-2329: heap buffer overflow in compile_branch(). (Closes: #781795) -util-linux (2.26.2-6ubuntu2) wily; urgency=medium -util-linux (2.26.2-6ubuntu1) wily; urgency=medium -util-linux (2.26.2-6) unstable; urgency=medium`, + `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium + util-linux (2.27.1-3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / + util-linux (2.27.1-2) unstable; urgency=medium + util-linux (2.27.1-1ubuntu4) xenial; urgency=medium + util-linux (2.27.1-1ubuntu3) xenial; urgency=medium + util-linux (2.27.1-1ubuntu2) xenial; urgency=medium + util-linux (2.27.1-1ubuntu1) xenial; urgency=medium + util-linux (2.27.1-1) unstable; urgency=medium + util-linux (2.27-3ubuntu1) xenial; urgency=medium`, }, []DetectedCveID{ {"CVE-2015-2325", models.ChangelogExactMatch}, @@ -182,6 +200,59 @@ util-linux (2.26.2-6) unstable; urgency=medium`, {"CVE-2015-3210", models.ChangelogExactMatch}, {"CVE-2016-1000000", models.ChangelogExactMatch}, }, + models.Changelog{ + Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium + util-linux (2.27.1-3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / + util-linux (2.27.1-2) unstable; urgency=medium + util-linux (2.27.1-1ubuntu4) xenial; urgency=medium + util-linux (2.27.1-1ubuntu3) xenial; urgency=medium`, + //TODO + Method: models.ChangelogExactMatchStr, + }, + }, + { + // 1:ver-ubuntu3 + []string{ + "bsdutils", + "1:2.27-3ubuntu3", + `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium + util-linux (2.27.1-3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / + util-linux (2.27.1-2) unstable; urgency=medium + util-linux (2.27.1-1ubuntu4) xenial; urgency=medium + util-linux (2.27.1-1ubuntu3) xenial; urgency=medium + util-linux (2.27.1-1ubuntu2) xenial; urgency=medium + util-linux (2.27.1-1ubuntu1) xenial; urgency=medium + util-linux (2.27.1-1) unstable; urgency=medium + util-linux (2.27-3) xenial; urgency=medium`, + }, + []DetectedCveID{ + {"CVE-2015-2325", models.ChangelogExactMatch}, + {"CVE-2015-2326", models.ChangelogExactMatch}, + {"CVE-2015-3210", models.ChangelogExactMatch}, + {"CVE-2016-1000000", models.ChangelogExactMatch}, + }, + models.Changelog{ + Contents: `util-linux (2.27.1-3ubuntu1) xenial; urgency=medium + util-linux (2.27.1-3) unstable; urgency=medium + CVE-2015-2325: heap buffer overflow in compile_branch(). (Closes: #781795) + CVE-2015-2326: heap buffer overflow in pcre_compile2(). (Closes: #783285) + CVE-2015-3210: CVE-2016-1000000heap buffer overflow in pcre_compile2() / + util-linux (2.27.1-2) unstable; urgency=medium + util-linux (2.27.1-1ubuntu4) xenial; urgency=medium + util-linux (2.27.1-1ubuntu3) xenial; urgency=medium + util-linux (2.27.1-1ubuntu2) xenial; urgency=medium + util-linux (2.27.1-1ubuntu1) xenial; urgency=medium + util-linux (2.27.1-1) unstable; urgency=medium + util-linux (2.27-3) xenial; urgency=medium`, + //TODO + Method: models.ChangelogExactMatchStr, + }, }, { // https://github.com/future-architect/vuls/pull/350 @@ -189,28 +260,42 @@ util-linux (2.26.2-6) unstable; urgency=medium`, "tar", "1.27.1-2+b1", `tar (1.27.1-2+deb8u1) jessie-security; urgency=high - * CVE-2016-6321: Bypassing the extract path name. -tar (1.27.1-2) unstable; urgency=low`, + * CVE-2016-6321: Bypassing the extract path name. + tar (1.27.1-2) unstable; urgency=low`, }, []DetectedCveID{ {"CVE-2016-6321", models.ChangelogLenientMatch}, }, + models.Changelog{ + Contents: `tar (1.27.1-2+deb8u1) jessie-security; urgency=high + * CVE-2016-6321: Bypassing the extract path name. + tar (1.27.1-2) unstable; urgency=low`, + Method: models.ChangelogLenientMatchStr, + }, }, } d := newDebian(config.ServerInfo{}) for _, tt := range tests { - actual := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1]) - if len(actual) != len(tt.expected) { - t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.expected, actual) + aCveIDs, aClog := d.getCveIDsFromChangelog(tt.in[2], tt.in[0], tt.in[1]) + if len(aCveIDs) != len(tt.cveIDs) { + t.Errorf("Len of return array are'nt same. expected %#v, actual %#v", tt.cveIDs, aCveIDs) t.Errorf(pp.Sprintf("%s", tt.in)) continue } - for i := range tt.expected { - if !reflect.DeepEqual(tt.expected[i], actual[i]) { - t.Errorf("expected %v, actual %v", tt.expected[i], actual[i]) + for i := range tt.cveIDs { + if !reflect.DeepEqual(tt.cveIDs[i], aCveIDs[i]) { + t.Errorf("expected %v, actual %v", tt.cveIDs[i], aCveIDs[i]) } } + + if aClog.Contents != tt.changelog.Contents { + t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Contents, aClog.Contents)) + } + + if aClog.Method != tt.changelog.Method { + t.Errorf(pp.Sprintf("expected: %s, actual: %s", tt.changelog.Method, aClog.Method)) + } } } diff --git a/scan/executil.go b/scan/executil.go index e4b0c4c5..38413492 100644 --- a/scan/executil.go +++ b/scan/executil.go @@ -332,11 +332,14 @@ func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string { cmd = strings.Replace(cmd, "sudo -S echo", "echo", -1) } - if c.Distro.Family != "FreeBSD" { - // set pipefail option. Bash only - // http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another - cmd = fmt.Sprintf("set -o pipefail; %s", cmd) - } + // If you are using pipe and you want to detect preprocessing errors, remove comment out + // switch c.Distro.Family { + // case "FreeBSD", "ubuntu", "debian", "raspbian": + // default: + // // set pipefail option. Bash only + // // http://unix.stackexchange.com/questions/14270/get-exit-status-of-process-thats-piped-to-another + // cmd = fmt.Sprintf("set -o pipefail; %s", cmd) + // } if c.IsContainer() { switch c.Container.Type { diff --git a/scan/redhat.go b/scan/redhat.go index 0d0bbbee..c422b7c1 100644 --- a/scan/redhat.go +++ b/scan/redhat.go @@ -257,7 +257,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er cmd = fmt.Sprintf(cmd, "") } - r := o.exec(util.PrependProxyEnv(cmd), sudo) + r := o.exec(util.PrependProxyEnv(cmd), noSudo) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf("Failed to SSH: %s", r) @@ -287,11 +287,25 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er // { packageName: changelog-lines } var rpm2changelog map[string]*string - rpm2changelog, err = o.parseAllChangelog(allChangelog) + rpm2changelog, err = o.divideChangelogByPackage(allChangelog) if err != nil { return nil, fmt.Errorf("Failed to parseAllChangelog. err: %s", err) } + for name, clog := range rpm2changelog { + for i, p := range o.Packages { + n := fmt.Sprintf("%s-%s-%s", + p.Name, p.NewVersion, p.NewRelease) + if name == n { + o.Packages[i].Changelog = models.Changelog{ + Contents: *clog, + Method: models.ChangelogExactMatchStr, + } + break + } + } + } + var results []PackInfoCveIDs for i, packInfo := range packInfoList { changelog := o.getChangelogCVELines(rpm2changelog, packInfo) @@ -452,7 +466,7 @@ func (o *redhat) getChangelogCVELines(rpm2changelog map[string]*string, packInfo return retLine } -func (o *redhat) parseAllChangelog(allChangelog string) (map[string]*string, error) { +func (o *redhat) divideChangelogByPackage(allChangelog string) (map[string]*string, error) { var majorVersion int var err error if o.Distro.Family == "centos" { @@ -559,7 +573,7 @@ func (o *redhat) getAllChangelog(packInfoList models.PackageInfoList) (stdout st "Failed to get changelog. status: %d, stdout: %s, stderr: %s", r.ExitStatus, r.Stdout, r.Stderr) } - return r.Stdout, nil + return strings.Replace(r.Stdout, "\r", "", -1), nil } type distroAdvisoryCveIDs struct { @@ -601,9 +615,8 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos, advIDPackNamesList, err := o.parseYumUpdateinfoListAvailable(r.Stdout) // get package name, version, rel to be upgrade. - // cmd = "yum check-update --security" cmd = "LANGUAGE=en_US.UTF-8 yum --color=never check-update" - r = o.exec(util.PrependProxyEnv(cmd), o.sudo()) + r = o.exec(util.PrependProxyEnv(cmd), noSudo) if !r.isSuccess(0, 100) { //returns an exit code of 100 if there are available updates. return nil, fmt.Errorf("Failed to SSH: %s", r) diff --git a/scan/redhat_test.go b/scan/redhat_test.go index d1616dd9..f43c7d46 100644 --- a/scan/redhat_test.go +++ b/scan/redhat_test.go @@ -1071,7 +1071,7 @@ func TestGetChangelogCVELines(t *testing.T) { Release: "6.7", } for _, tt := range testsCentos6 { - rpm2changelog, err := r.parseAllChangelog(stdoutCentos6) + rpm2changelog, err := r.divideChangelogByPackage(stdoutCentos6) if err != nil { t.Errorf("err: %s", err) } @@ -1157,7 +1157,7 @@ func TestGetChangelogCVELines(t *testing.T) { Release: "5.6", } for _, tt := range testsCentos5 { - rpm2changelog, err := r.parseAllChangelog(stdoutCentos5) + rpm2changelog, err := r.divideChangelogByPackage(stdoutCentos5) if err != nil { t.Errorf("err: %s", err) }