Output confidence ranking of detection accuracy to JSON or Reporting

This commit is contained in:
Kota Kanbe
2017-02-20 17:32:58 +09:00
parent 1d3ee6a241
commit 1b9aafbbaf
12 changed files with 386 additions and 120 deletions

View File

@@ -421,10 +421,9 @@ func (o *debian) parseAptGetUpgrade(stdout string) (upgradableNames []string, er
}
func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache.Meta) (models.VulnInfos, error) {
type strarray []string
resChan := make(chan struct {
models.PackageInfo
strarray
DetectedCveIDs
}, len(upgradablePacks))
errChan := make(chan error, len(upgradablePacks))
reqChan := make(chan models.PackageInfo, len(upgradablePacks))
@@ -448,10 +447,10 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
func(p models.PackageInfo) {
changelog := o.getChangelogCache(meta, p)
if 0 < len(changelog) {
cveIDs := o.getCveIDFromChangelog(changelog, p.Name, p.Version)
cveIDs := o.getCveIDsFromChangelog(changelog, p.Name, p.Version)
resChan <- struct {
models.PackageInfo
strarray
DetectedCveIDs
}{p, cveIDs}
return
}
@@ -464,7 +463,7 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
} else {
resChan <- struct {
models.PackageInfo
strarray
DetectedCveIDs
}{p, cveIDs}
}
}(pack)
@@ -472,14 +471,14 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
}
}
// { CVE ID: [packageInfo] }
cvePackages := make(map[string][]models.PackageInfo)
// { DetectedCveID{} : [packageInfo] }
cvePackages := make(map[DetectedCveID][]models.PackageInfo)
errs := []error{}
for i := 0; i < len(upgradablePacks); i++ {
select {
case pair := <-resChan:
pack := pair.PackageInfo
cveIDs := pair.strarray
cveIDs := pair.DetectedCveIDs
for _, cveID := range cveIDs {
cvePackages[cveID] = appendPackIfMissing(cvePackages[cveID], pack)
}
@@ -495,7 +494,7 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
return nil, fmt.Errorf("%v", errs)
}
var cveIDs []string
var cveIDs []DetectedCveID
for k := range cvePackages {
cveIDs = append(cveIDs, k)
}
@@ -503,8 +502,9 @@ func (o *debian) scanVulnInfos(upgradablePacks []models.PackageInfo, meta *cache
var vinfos models.VulnInfos
for k, v := range cvePackages {
vinfos = append(vinfos, models.VulnInfo{
CveID: k,
Packages: v,
CveID: k.CveID,
Confidence: k.Confidence,
Packages: v,
})
}
@@ -545,7 +545,7 @@ func (o *debian) getChangelogCache(meta *cache.Meta, pack models.PackageInfo) st
return changelog
}
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]DetectedCveID, error) {
cmd := ""
switch o.Distro.Family {
case "ubuntu", "raspbian":
@@ -569,11 +569,11 @@ func (o *debian) scanPackageCveIDs(pack models.PackageInfo) ([]string, error) {
}
}
// No error will be returned. Only logging.
return o.getCveIDFromChangelog(r.Stdout, pack.Name, pack.Version), nil
return o.getCveIDsFromChangelog(r.Stdout, pack.Name, pack.Version), nil
}
func (o *debian) getCveIDFromChangelog(changelog string,
packName string, versionOrLater string) []string {
func (o *debian) getCveIDsFromChangelog(changelog string,
packName string, versionOrLater string) []DetectedCveID {
if cveIDs, err := o.parseChangelog(changelog, packName, versionOrLater); err == nil {
return cveIDs
@@ -595,31 +595,43 @@ func (o *debian) getCveIDFromChangelog(changelog string,
// Only logging the error.
o.log.Error(err)
return []string{}
return []DetectedCveID{}
}
// DetectedCveID has CveID, Confidence and DetectionMethod fields
// LenientMatching will be true if this vulnerability is not detected by accurate version matching.
// see https://github.com/future-architect/vuls/pull/328
type DetectedCveID struct {
CveID string
Confidence models.Confidence
}
// DetectedCveIDs is a slice of DetectedCveID
type DetectedCveIDs []DetectedCveID
// 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) (cveIDs []string, err error) {
packName string, versionOrLater string) (cves []DetectedCveID, err error) {
cveIDs := []string{}
cveRe := regexp.MustCompile(`(CVE-\d{4}-\d{4,})`)
stopRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLater)))
stopLineFound := false
leniantStopLineFound := false
versionOrLaterLeniant := versionOrLater
if i := strings.IndexRune(versionOrLaterLeniant, '+'); i >= 0 {
versionOrLaterLeniant = versionOrLaterLeniant[:i]
lenientStopLineFound := false
versionOrLaterlenient := versionOrLater
if i := strings.IndexRune(versionOrLaterlenient, '+'); i >= 0 {
versionOrLaterlenient = versionOrLaterlenient[:i]
}
leniantRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLaterLeniant)))
lenientRe := regexp.MustCompile(fmt.Sprintf(`\(%s\)`, regexp.QuoteMeta(versionOrLaterlenient)))
lines := strings.Split(changelog, "\n")
for _, line := range lines {
if matche := stopRe.MatchString(line); matche {
if match := stopRe.MatchString(line); match {
// o.log.Debugf("Found the stop line: %s", line)
stopLineFound = true
break
} else if matchel := leniantRe.MatchString(line); matchel {
leniantStopLineFound = true
} else if matchl := lenientRe.MatchString(line); matchl {
lenientStopLineFound = true
break
} else if matches := cveRe.FindAllString(line, -1); 0 < len(matches) {
for _, m := range matches {
@@ -627,13 +639,21 @@ func (o *debian) parseChangelog(changelog string,
}
}
}
if !stopLineFound && !leniantStopLineFound {
return []string{}, fmt.Errorf(
if !stopLineFound && !lenientStopLineFound {
return cves, 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
}
cves = append(cves, DetectedCveID{id, confidence})
}
return
}

View File

@@ -62,7 +62,7 @@ func TestGetCveIDParsingChangelog(t *testing.T) {
var tests = []struct {
in []string
expected []string
expected []DetectedCveID
}{
{
// verubuntu1
@@ -84,10 +84,10 @@ systemd (227-3) unstable; urgency=medium
systemd (227-2) unstable; urgency=medium
systemd (227-1) unstable; urgency=medium`,
},
[]string{
"CVE-2015-2325",
"CVE-2015-2326",
"CVE-2015-3210",
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
},
},
{
@@ -107,10 +107,10 @@ 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`,
},
[]string{
"CVE-2015-2325",
"CVE-2015-2326",
"CVE-2015-3210",
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
},
},
{
@@ -138,10 +138,10 @@ sysvinit (2.88dsf-59) unstable; urgency=medium
sysvinit (2.88dsf-58) unstable; urgency=low
sysvinit (2.88dsf-57) unstable; urgency=low`,
},
[]string{
"CVE-2015-2325",
"CVE-2015-2326",
"CVE-2015-3210",
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
},
},
{
@@ -176,26 +176,39 @@ 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`,
},
[]DetectedCveID{
{"CVE-2015-2325", models.ChangelogExactMatch},
{"CVE-2015-2326", models.ChangelogExactMatch},
{"CVE-2015-3210", models.ChangelogExactMatch},
{"CVE-2016-1000000", models.ChangelogExactMatch},
},
},
{
// https://github.com/future-architect/vuls/pull/350
[]string{
"CVE-2015-2325",
"CVE-2015-2326",
"CVE-2015-3210",
"CVE-2016-1000000",
"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`,
},
[]DetectedCveID{
{"CVE-2016-6321", models.ChangelogLenientMatch},
},
},
}
d := newDebian(config.ServerInfo{})
for _, tt := range tests {
actual := d.getCveIDFromChangelog(tt.in[2], tt.in[0], tt.in[1])
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)
t.Errorf(pp.Sprintf("%s", tt.in))
continue
}
for i := range tt.expected {
if actual[i] != tt.expected[i] {
t.Errorf("expected %s, actual %s", tt.expected[i], actual[i])
if !reflect.DeepEqual(tt.expected[i], actual[i]) {
t.Errorf("expected %v, actual %v", tt.expected[i], actual[i])
}
}
}

View File

@@ -167,6 +167,7 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
CveID: k,
Packages: packs,
DistroAdvisories: disAdvs,
Confidence: models.PkgAuditMatch,
})
}
return

View File

@@ -352,8 +352,9 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
for k, v := range cveIDPackInfoMap {
// Amazon, RHEL do not use this method, so VendorAdvisory do not set.
vinfos = append(vinfos, models.VulnInfo{
CveID: k,
Packages: v,
CveID: k,
Packages: v,
Confidence: models.ChangelogExactMatch,
})
}
return vinfos, nil
@@ -670,6 +671,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
CveID: cveID,
DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory},
Packages: dict[advIDCveIDs.DistroAdvisory.AdvisoryID],
Confidence: models.YumUpdateSecurityMatch,
}
vinfos = append(vinfos, cpinfo)
}