Output changelog in report, TUI and JSON for Ubuntu/Debian/CentOS

This commit is contained in:
Kota Kanbe
2017-02-23 16:50:28 +09:00
parent c66b0f4db4
commit a86035c0bf
15 changed files with 504 additions and 156 deletions

View File

@@ -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 {