Set actually affected package's name only to vulnInfo.PackageNames
This commit is contained in:
@@ -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
75
oval/debian_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
oval/util.go
51
oval/util.go
@@ -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
98
oval/util_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user