Set actually affected package's name only to vulnInfo.PackageNames

This commit is contained in:
kota kanbe
2017-08-13 20:50:26 +09:00
parent ee20cb59a5
commit c66898e608
8 changed files with 326 additions and 48 deletions

View File

@@ -14,9 +14,12 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oval
import (
"sort"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
@@ -30,19 +33,19 @@ type DebianBase struct {
// FillWithOval returns scan result after updating CVE info by OVAL
func (o DebianBase) FillWithOval(r *models.ScanResult) (err error) {
var defs []ovalmodels.Definition
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if defs, err = getDefsByPackNameViaHTTP(r); err != nil {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if defs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
if relatedDefs, err = getDefsByPackNameFromOvalDB(o.family, r.Release, r.Packages); err != nil {
return err
}
}
for _, def := range defs {
o.update(r, &def)
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
for _, vuln := range r.ScannedCves {
@@ -62,25 +65,26 @@ func (o DebianBase) FillWithOval(r *models.ScanResult) (err error) {
return nil
}
func (o DebianBase) update(r *models.ScanResult, definition *ovalmodels.Definition) {
ovalContent := *o.convertToModel(definition)
func (o DebianBase) update(r *models.ScanResult, defPacks defPacks) {
ovalContent := *o.convertToModel(&defPacks.def)
ovalContent.Type = models.NewCveContentType(o.family)
vinfo, ok := r.ScannedCves[definition.Debian.CveID]
vinfo, ok := r.ScannedCves[defPacks.def.Debian.CveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", definition.Debian.CveID)
util.Log.Debugf("%s is newly detected by OVAL", defPacks.def.Debian.CveID)
vinfo = models.VulnInfo{
CveID: definition.Debian.CveID,
Confidence: models.OvalMatch,
PackageNames: getPackages(r, definition),
CveContents: models.NewCveContents(ovalContent),
CveID: defPacks.def.Debian.CveID,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
ctype := models.NewCveContentType(o.family)
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s will be updated by OVAL", definition.Debian.CveID)
util.Log.Debugf("%s OVAL will be overwritten",
defPacks.def.Debian.CveID)
} else {
util.Log.Debugf("%s is also detected by OVAL", definition.Debian.CveID)
util.Log.Debugf("%s is also detected by OVAL",
defPacks.def.Debian.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
@@ -89,7 +93,14 @@ func (o DebianBase) update(r *models.ScanResult, definition *ovalmodels.Definiti
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
r.ScannedCves[definition.Debian.CveID] = vinfo
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, name := range vinfo.PackageNames {
defPacks.actuallyAffectedPackNames[name] = true
}
vinfo.PackageNames = defPacks.packNames()
sort.Strings(vinfo.PackageNames)
r.ScannedCves[defPacks.def.Debian.CveID] = vinfo
}
func (o DebianBase) convertToModel(def *ovalmodels.Definition) *models.CveContent {

75
oval/debian_test.go Normal file
View File

@@ -0,0 +1,75 @@
/* Vuls - Vulnerability Scanner
Copyright (C) 2016 Future Architect, Inc. Japan.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oval
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestPackNamesOfUpdateDebian(t *testing.T) {
var tests = []struct {
in models.ScanResult
defPacks defPacks
out models.ScanResult
}{
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
PackageNames: []string{"packA"},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Debian: ovalmodels.Debian{
CveID: "CVE-2000-1000",
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
PackageNames: []string{
"packA",
"packB",
},
},
},
},
},
}
util.Log = util.NewCustomLogger(config.ServerInfo{})
for i, tt := range tests {
Debian{}.update(&tt.in, tt.defPacks)
e := tt.out.ScannedCves["CVE-2000-1000"].PackageNames
a := tt.in.ScannedCves["CVE-2000-1000"].PackageNames
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}

View File

@@ -19,6 +19,7 @@ package oval
import (
"fmt"
"sort"
"strconv"
"strings"
@@ -35,23 +36,22 @@ type RedHatBase struct {
// FillWithOval returns scan result after updating CVE info by OVAL
func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
var defs []ovalmodels.Definition
var relatedDefs ovalResult
if o.isFetchViaHTTP() {
if defs, err = getDefsByPackNameViaHTTP(r); err != nil {
if relatedDefs, err = getDefsByPackNameViaHTTP(r); err != nil {
return err
}
} else {
if defs, err = getDefsByPackNameFromOvalDB(
if relatedDefs, err = getDefsByPackNameFromOvalDB(
o.family, r.Release, r.Packages); err != nil {
return err
}
}
for _, def := range defs {
o.update(r, &def)
for _, defPacks := range relatedDefs.entries {
o.update(r, defPacks)
}
// TODO merge to VulnInfo.VendorLinks
for _, vuln := range r.ScannedCves {
switch models.NewCveContentType(o.family) {
case models.RedHat:
@@ -69,23 +69,22 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) (err error) {
return nil
}
func (o RedHatBase) update(r *models.ScanResult, definition *ovalmodels.Definition) {
func (o RedHatBase) update(r *models.ScanResult, defPacks defPacks) {
ctype := models.NewCveContentType(o.family)
for _, cve := range definition.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, definition)
for _, cve := range defPacks.def.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, &defPacks.def)
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
util.Log.Debugf("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidence: models.OvalMatch,
PackageNames: getPackages(r, definition),
CveContents: models.NewCveContents(ovalContent),
CveID: cve.CveID,
Confidence: models.OvalMatch,
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents[ctype]; ok {
util.Log.Debugf("%s will be updated by OVAL", cve.CveID)
util.Log.Debugf("%s OVAL will be overwritten", cve.CveID)
} else {
util.Log.Debugf("%s also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
@@ -97,6 +96,13 @@ func (o RedHatBase) update(r *models.ScanResult, definition *ovalmodels.Definiti
cveContents[ctype] = ovalContent
vinfo.CveContents = cveContents
}
// uniq(vinfo.PackNames + defPacks.actuallyAffectedPackNames)
for _, name := range vinfo.PackageNames {
defPacks.actuallyAffectedPackNames[name] = true
}
vinfo.PackageNames = defPacks.packNames()
sort.Strings(vinfo.PackageNames)
r.ScannedCves[cve.CveID] = vinfo
}
}

View File

@@ -16,7 +16,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package oval
import "testing"
import (
"reflect"
"testing"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestParseCvss2(t *testing.T) {
type out struct {
@@ -83,3 +91,55 @@ func TestParseCvss3(t *testing.T) {
}
}
}
func TestPackNamesOfUpdate(t *testing.T) {
var tests = []struct {
in models.ScanResult
defPacks defPacks
out models.ScanResult
}{
{
in: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
PackageNames: []string{"packA"},
},
},
},
defPacks: defPacks{
def: ovalmodels.Definition{
Advisory: ovalmodels.Advisory{
Cves: []ovalmodels.Cve{
{
CveID: "CVE-2000-1000",
},
},
},
},
actuallyAffectedPackNames: map[string]bool{
"packB": true,
},
},
out: models.ScanResult{
ScannedCves: models.VulnInfos{
"CVE-2000-1000": models.VulnInfo{
PackageNames: []string{
"packA",
"packB",
},
},
},
},
},
}
util.Log = util.NewCustomLogger(config.ServerInfo{})
for i, tt := range tests {
RedHat{}.update(&tt.in, tt.defPacks)
e := tt.out.ScannedCves["CVE-2000-1000"].PackageNames
a := tt.in.ScannedCves["CVE-2000-1000"].PackageNames
if !reflect.DeepEqual(a, e) {
t.Errorf("[%d] expected: %v\n actual: %v\n", i, e, a)
}
}
}

View File

@@ -35,6 +35,36 @@ import (
"github.com/parnurzeal/gorequest"
)
type ovalResult struct {
entries []defPacks
}
type defPacks struct {
def ovalmodels.Definition
actuallyAffectedPackNames map[string]bool
}
func (e defPacks) packNames() (names []string) {
for k := range e.actuallyAffectedPackNames {
names = append(names, k)
}
return
}
func (e *ovalResult) upsert(def ovalmodels.Definition, packName string) (upserted bool) {
for i, entry := range e.entries {
if entry.def.DefinitionID == def.DefinitionID {
e.entries[i].actuallyAffectedPackNames[packName] = true
return true
}
}
e.entries = append(e.entries, defPacks{
def: def,
actuallyAffectedPackNames: map[string]bool{packName: true},
})
return false
}
type request struct {
pack models.Package
}
@@ -46,7 +76,7 @@ type response struct {
// getDefsByPackNameViaHTTP fetches OVAL information via HTTP
func getDefsByPackNameViaHTTP(r *models.ScanResult) (
relatedDefs []ovalmodels.Definition, err error) {
relatedDefs ovalResult, err error) {
reqChan := make(chan request, len(r.Packages))
resChan := make(chan response, len(r.Packages))
@@ -102,18 +132,18 @@ func getDefsByPackNameViaHTTP(r *models.ScanResult) (
util.Log.Debugf("%#v\n%#v", *res.pack, p)
}
} else if less {
relatedDefs = append(relatedDefs, def)
relatedDefs.upsert(def, p.Name)
}
}
}
case err := <-errChan:
errs = append(errs, err)
case <-timeout:
return nil, fmt.Errorf("Timeout Fetching OVAL")
return relatedDefs, fmt.Errorf("Timeout Fetching OVAL")
}
}
if len(errs) != 0 {
return nil, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
return relatedDefs, fmt.Errorf("Failed to fetch OVAL. err: %v", errs)
}
return
}
@@ -161,15 +191,8 @@ func httpGet(url string, pack *models.Package, resChan chan<- response, errChan
}
}
func getPackages(r *models.ScanResult, d *ovalmodels.Definition) (names []string) {
for _, affectedPack := range d.AffectedPacks {
names = append(names, affectedPack.Name)
}
return
}
func getDefsByPackNameFromOvalDB(family, osRelease string,
packs models.Packages) (relatedDefs []ovalmodels.Definition, err error) {
packs models.Packages) (relatedDefs ovalResult, err error) {
ovallog.Initialize(config.Conf.LogDir)
path := config.Conf.OvalDBURL
@@ -191,7 +214,7 @@ func getDefsByPackNameFromOvalDB(family, osRelease string,
for _, pack := range packs {
definitions, err := ovaldb.GetByPackName(osRelease, pack.Name)
if err != nil {
return nil, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
return relatedDefs, fmt.Errorf("Failed to get %s OVAL info by package name: %v", family, err)
}
for _, def := range definitions {
for _, p := range def.AffectedPacks {
@@ -204,7 +227,7 @@ func getDefsByPackNameFromOvalDB(family, osRelease string,
util.Log.Debugf("%#v\n%#v", pack, p)
}
} else if less {
relatedDefs = append(relatedDefs, def)
relatedDefs.upsert(def, pack.Name)
}
}
}

98
oval/util_test.go Normal file
View File

@@ -0,0 +1,98 @@
package oval
import (
"reflect"
"testing"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
func TestUpsert(t *testing.T) {
var tests = []struct {
res ovalResult
def ovalmodels.Definition
packName string
upserted bool
out ovalResult
}{
//insert
{
res: ovalResult{},
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack1",
upserted: false,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
},
},
},
},
},
//update
{
res: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
},
},
},
},
def: ovalmodels.Definition{
DefinitionID: "1111",
},
packName: "pack2",
upserted: true,
out: ovalResult{
[]defPacks{
{
def: ovalmodels.Definition{
DefinitionID: "1111",
},
actuallyAffectedPackNames: map[string]bool{
"pack1": true,
"pack2": true,
},
},
{
def: ovalmodels.Definition{
DefinitionID: "2222",
},
actuallyAffectedPackNames: map[string]bool{
"pack3": true,
},
},
},
},
},
}
for i, tt := range tests {
upserted := tt.res.upsert(tt.def, tt.packName)
if tt.upserted != upserted {
t.Errorf("[%d]\nexpected: %t\n actual: %t\n", i, tt.upserted, upserted)
}
if !reflect.DeepEqual(tt.out, tt.res) {
t.Errorf("[%d]\nexpected: %v\n actual: %v\n", i, tt.out, tt.res)
}
}
}

View File

@@ -58,8 +58,14 @@ func (w LocalFileWriter) Write(rs ...models.ScanResult) (err error) {
}
var b []byte
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
if c.Conf.Debug {
if b, err = json.MarshalIndent(r, "", " "); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
} else {
if b, err = json.Marshal(r); err != nil {
return fmt.Errorf("Failed to Marshal to JSON: %s", err)
}
}
if err := writeFile(p, b, 0600); err != nil {
return fmt.Errorf("Failed to write JSON. path: %s, err: %s", p, err)

View File

@@ -218,7 +218,6 @@ No CVE-IDs are found in updatable packages.
packsVer := []string{}
sort.Strings(vuln.PackageNames)
for _, name := range vuln.PackageNames {
// packages detected by OVAL may not be actually installed
if pack, ok := r.Packages[name]; ok {
packsVer = append(packsVer, pack.FormatVersionFromTo())
}