Change structure of ScanResult.[]VulnInfo to Map

This commit is contained in:
Kota Kanbe
2017-05-09 21:03:54 +09:00
committed by kota kanbe
parent b977558f38
commit cfb848918f
12 changed files with 129 additions and 224 deletions

View File

@@ -504,15 +504,19 @@ func fillCveDetail(r *models.ScanResult) error {
return err
}
for _, d := range ds {
nvd := *r.ConvertNvdToModel(d.CveID, d.Nvd)
jvn := *r.ConvertJvnToModel(d.CveID, d.Jvn)
for i, sc := range r.ScannedCves {
if sc.CveID == d.CveID {
for _, con := range []models.CveContent{nvd, jvn} {
nvd := r.ConvertNvdToModel(d.CveID, d.Nvd)
jvn := r.ConvertJvnToModel(d.CveID, d.Jvn)
for cveID, vinfo := range r.ScannedCves {
if vinfo.CveID == d.CveID {
if vinfo.CveContents == nil {
vinfo.CveContents = models.CveContents{}
}
for _, con := range []models.CveContent{*nvd, *jvn} {
if !con.Empty() {
r.ScannedCves[i].CveContents.Upsert(con)
vinfo.CveContents.Upsert(con)
}
}
r.ScannedCves[cveID] = vinfo
break
}
}
@@ -528,15 +532,10 @@ func fillCveDetail(r *models.ScanResult) error {
}
func fillCveInfoFromCveDB(r *models.ScanResult) error {
var err error
var vs []models.VulnInfo
sInfo := c.Conf.Servers[r.ServerName]
vs, err = scanVulnByCpeNames(sInfo.CpeNames, r.ScannedCves)
if err != nil {
if err := fillVulnByCpeNames(sInfo.CpeNames, r.ScannedCves); err != nil {
return err
}
r.ScannedCves = vs
if err := fillCveDetail(r); err != nil {
return err
}

View File

@@ -188,9 +188,7 @@ func diff(curResults, preResults models.ScanResults) (diffed models.ScanResults,
}
if found {
new, updated := getDiffCves(previous, current)
current.ScannedCves = append(new, updated...)
current.ScannedCves = getDiffCves(previous, current)
packages := models.Packages{}
for _, s := range current.ScannedCves {
for _, name := range s.PackageNames {
@@ -206,22 +204,28 @@ func diff(curResults, preResults models.ScanResults) (diffed models.ScanResults,
return diffed, err
}
func getDiffCves(previous, current models.ScanResult) (new, updated []models.VulnInfo) {
func getDiffCves(previous, current models.ScanResult) models.VulnInfos {
previousCveIDsSet := map[string]bool{}
for _, previousVulnInfo := range previous.ScannedCves {
previousCveIDsSet[previousVulnInfo.CveID] = true
}
new := models.VulnInfos{}
updated := models.VulnInfos{}
for _, v := range current.ScannedCves {
if previousCveIDsSet[v.CveID] {
if isCveInfoUpdated(v.CveID, previous, current) {
updated = append(updated, v)
updated[v.CveID] = v
}
} else {
new = append(new, v)
new[v.CveID] = v
}
}
return
for cveID, vuln := range new {
updated[cveID] = vuln
}
return updated
}
func isCveInfoUpdated(cveID string, previous, current models.ScanResult) bool {
@@ -274,42 +278,32 @@ func overwriteJSONFile(dir string, r models.ScanResult) error {
return nil
}
func scanVulnByCpeNames(cpeNames []string, scannedVulns []models.VulnInfo) ([]models.VulnInfo, error) {
// To remove duplicate
set := map[string]models.VulnInfo{}
for _, v := range scannedVulns {
set[v.CveID] = v
}
func fillVulnByCpeNames(cpeNames []string, scannedVulns models.VulnInfos) error {
for _, name := range cpeNames {
details, err := cveapi.CveClient.FetchCveDetailsByCpeName(name)
if err != nil {
return nil, err
return err
}
for _, detail := range details {
if val, ok := set[detail.CveID]; ok {
if val, ok := scannedVulns[detail.CveID]; ok {
names := val.CpeNames
names = util.AppendIfMissing(names, name)
val.CpeNames = names
val.Confidence = models.CpeNameMatch
set[detail.CveID] = val
scannedVulns[detail.CveID] = val
} else {
v := models.VulnInfo{
CveID: detail.CveID,
CpeNames: []string{name},
Confidence: models.CpeNameMatch,
}
v.NilToEmpty()
set[detail.CveID] = v
//TODO
// v.NilToEmpty()
scannedVulns[detail.CveID] = v
}
}
}
vinfos := []models.VulnInfo{}
for key := range set {
vinfos = append(vinfos, set[key])
}
return vinfos, nil
return nil
}
func needToRefreshCve(r models.ScanResult) bool {

View File

@@ -45,8 +45,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
in: In{
cveID: "CVE-2017-0001",
cur: models.ScanResult{
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
@@ -59,8 +59,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
},
},
prev: models.ScanResult{
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0001": {
CveID: "CVE-2017-0001",
CveContents: models.NewCveContents(
models.CveContent{
@@ -80,8 +80,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
in: In{
cveID: "CVE-2017-0002",
cur: models.ScanResult{
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: models.NewCveContents(
models.CveContent{
@@ -94,8 +94,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
},
},
prev: models.ScanResult{
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0002": {
CveID: "CVE-2017-0002",
CveContents: models.NewCveContents(
models.CveContent{
@@ -116,8 +116,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
cveID: "CVE-2017-0003",
cur: models.ScanResult{
Family: "ubuntu",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
@@ -131,8 +131,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
},
prev: models.ScanResult{
Family: "ubuntu",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0003": {
CveID: "CVE-2017-0003",
CveContents: models.NewCveContents(
models.CveContent{
@@ -153,8 +153,8 @@ func TestIsCveInfoUpdated(t *testing.T) {
cveID: "CVE-2017-0004",
cur: models.ScanResult{
Family: "redhat",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2017-0004": {
CveID: "CVE-2017-0004",
CveContents: models.NewCveContents(
models.CveContent{
@@ -168,7 +168,7 @@ func TestIsCveInfoUpdated(t *testing.T) {
},
prev: models.ScanResult{
Family: "redhat",
ScannedCves: []models.VulnInfo{},
ScannedCves: models.VulnInfos{},
},
},
expected: true,
@@ -197,14 +197,14 @@ func TestDiff(t *testing.T) {
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
PackageNames: []string{"libexpat1"},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
{
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
PackageNames: []string{"libc-bin"},
DistroAdvisories: []models.DistroAdvisory{},
@@ -222,14 +222,14 @@ func TestDiff(t *testing.T) {
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2012-6702": {
CveID: "CVE-2012-6702",
PackageNames: []string{"libexpat1"},
DistroAdvisories: []models.DistroAdvisory{},
CpeNames: []string{},
},
{
"CVE-2014-9761": {
CveID: "CVE-2014-9761",
PackageNames: []string{"libc-bin"},
DistroAdvisories: []models.DistroAdvisory{},
@@ -242,13 +242,14 @@ func TestDiff(t *testing.T) {
},
},
out: models.ScanResult{
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
Packages: models.Packages{},
Errors: []string{},
Optional: [][]interface{}{},
ScannedAt: atCurrent,
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
Packages: models.Packages{},
ScannedCves: models.VulnInfos{},
Errors: []string{},
Optional: [][]interface{}{},
},
},
{
@@ -258,8 +259,8 @@ func TestDiff(t *testing.T) {
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
PackageNames: []string{"mysql-libs"},
DistroAdvisories: []models.DistroAdvisory{},
@@ -288,7 +289,7 @@ func TestDiff(t *testing.T) {
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{},
ScannedCves: models.VulnInfos{},
},
},
out: models.ScanResult{
@@ -296,8 +297,8 @@ func TestDiff(t *testing.T) {
ServerName: "u16",
Family: "ubuntu",
Release: "16.04",
ScannedCves: []models.VulnInfo{
{
ScannedCves: models.VulnInfos{
"CVE-2016-6662": {
CveID: "CVE-2016-6662",
PackageNames: []string{"mysql-libs"},
DistroAdvisories: []models.DistroAdvisory{},

View File

@@ -164,12 +164,10 @@ func (r ScanResult) FilterByCvssOver() ScanResult {
}
// TODO: Filter by ignore cves???
filtered := VulnInfos{}
for _, sc := range r.ScannedCves {
if config.Conf.CvssScoreOver <= sc.CveContents.CvssV2Score() {
filtered = append(filtered, sc)
}
}
filtered := r.ScannedCves.Find(func(v VulnInfo) bool {
return config.Conf.CvssScoreOver <= v.CveContents.CvssV2Score()
})
copiedScanResult := r
copiedScanResult.ScannedCves = filtered
return copiedScanResult
@@ -316,61 +314,17 @@ var ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
var ChangelogLenientMatch = Confidence{50, ChangelogLenientMatchStr}
// VulnInfos is VulnInfo list, getter/setter, sortable methods.
type VulnInfos []VulnInfo
type VulnInfos map[string]VulnInfo
// Find elements that matches the function passed in argument
func (v *VulnInfos) Find(f func(VulnInfo) bool) (filtered VulnInfos) {
for _, vv := range *v {
func (v VulnInfos) Find(f func(VulnInfo) bool) VulnInfos {
filtered := VulnInfos{}
for _, vv := range v {
if f(vv) {
filtered = append(filtered, vv)
filtered[vv.CveID] = vv
}
}
return
}
// Get VulnInfo by cveID
func (v *VulnInfos) Get(cveID string) (VulnInfo, bool) {
for _, vv := range *v {
if vv.CveID == cveID {
return vv, true
}
}
return VulnInfo{}, false
}
// Delete by cveID
func (v *VulnInfos) Delete(cveID string) {
vInfos := *v
for i, vv := range vInfos {
if vv.CveID == cveID {
*v = append(vInfos[:i], vInfos[i+1:]...)
break
}
}
}
// Insert VulnInfo
func (v *VulnInfos) Insert(vinfo VulnInfo) {
*v = append(*v, vinfo)
}
// Update VulnInfo
func (v *VulnInfos) Update(vInfo VulnInfo) (ok bool) {
for i, vv := range *v {
if vv.CveID == vInfo.CveID {
(*v)[i] = vInfo
return true
}
}
return false
}
// Upsert cveInfo
func (v *VulnInfos) Upsert(vInfo VulnInfo) {
ok := v.Update(vInfo)
if !ok {
v.Insert(vInfo)
}
return filtered
}
// VulnInfo holds a vulnerability information and unsecure packages

View File

@@ -58,46 +58,3 @@ func TestMergeNewVersion(t *testing.T) {
t.Errorf("expected %s, actual %s", e, a)
}
}
func TestVulnInfosSetGet(t *testing.T) {
var test = struct {
in []string
out []string
}{
[]string{
"CVE1",
"CVE2",
"CVE3",
"CVE1",
"CVE1",
"CVE2",
"CVE3",
},
[]string{
"CVE1",
"CVE2",
"CVE3",
},
}
// var ps packageCveInfos
var ps VulnInfos
for _, cid := range test.in {
ps.Upsert(VulnInfo{CveID: cid})
}
if len(test.out) != len(ps) {
t.Errorf("length: expected %d, actual %d", len(test.out), len(ps))
}
for i, expectedCid := range test.out {
if expectedCid != ps[i].CveID {
t.Errorf("expected %s, actual %s", expectedCid, ps[i].CveID)
}
}
for _, cid := range test.in {
p, _ := ps.Get(cid)
if p.CveID != cid {
t.Errorf("expected %s, actual %s", cid, p.CveID)
}
}
}

View File

@@ -62,10 +62,9 @@ func (o Debian) FillCveInfoFromOvalDB(r *models.ScanResult) error {
func (o Debian) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
ovalContent := *o.convertToModel(definition)
ovalContent.Type = models.NewCveContentType(r.Family)
vinfo, ok := r.ScannedCves.Get(definition.Debian.CveID)
vinfo, ok := r.ScannedCves[definition.Debian.CveID]
if !ok {
util.Log.Infof("%s is newly detected by OVAL",
definition.Debian.CveID)
util.Log.Infof("%s is newly detected by OVAL", definition.Debian.CveID)
vinfo = models.VulnInfo{
CveID: definition.Debian.CveID,
Confidence: models.OvalMatch,
@@ -73,17 +72,20 @@ func (o Debian) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Defini
CveContents: models.NewCveContents(ovalContent),
}
} else {
if _, ok := vinfo.CveContents.Get(models.NewCveContentType(r.Family)); !ok {
util.Log.Infof("%s is also detected by OVAL", definition.Debian.CveID)
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents.Get(models.NewCveContentType(r.Family)); ok {
util.Log.Infof("%s will be updated by OVAL", definition.Debian.CveID)
} else {
util.Log.Infof("%s is also detected by OVAL", definition.Debian.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
vinfo.CveContents.Upsert(ovalContent)
cveContents.Upsert(ovalContent)
vinfo.CveContents = cveContents
}
r.ScannedCves.Upsert(vinfo)
r.ScannedCves[definition.Debian.CveID] = vinfo
}
func (o Debian) convertToModel(def *ovalmodels.Definition) *models.CveContent {

View File

@@ -59,7 +59,7 @@ func (o Redhat) FillCveInfoFromOvalDB(r *models.ScanResult) error {
func (o Redhat) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
for _, cve := range definition.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, definition)
vinfo, ok := r.ScannedCves.Get(cve.CveID)
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
util.Log.Infof("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
@@ -69,17 +69,21 @@ func (o Redhat) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Defini
CveContents: models.NewCveContents(ovalContent),
}
} else {
if _, ok := vinfo.CveContents.Get(models.RedHat); !ok {
util.Log.Infof("%s is also detected by OVAL", cve.CveID)
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents.Get(models.RedHat); ok {
util.Log.Infof("%s will be updated by OVAL", cve.CveID)
} else {
util.Log.Infof("%s is also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
vinfo.CveContents.Upsert(ovalContent)
cveContents.Upsert(ovalContent)
vinfo.CveContents = cveContents
}
r.ScannedCves.Upsert(vinfo)
r.ScannedCves[cve.CveID] = vinfo
}
}

View File

@@ -291,10 +291,11 @@ func (l *base) convertToModel() models.ScanResult {
errs = append(errs, fmt.Sprintf("%s", e))
}
//TODO Remove
// Avoid null slice being null in JSON
for i := range l.VulnInfos {
l.VulnInfos[i].NilToEmpty()
}
// for cveID := range l.VulnInfos {
// l.VulnInfos[i].NilToEmpty()
// }
return models.ScanResult{
ServerName: l.ServerInfo.ServerName,

View File

@@ -264,8 +264,7 @@ func (o *debian) aptGetUpdate() error {
return nil
}
func (o *debian) scanUnsecurePackages(upgradable models.Packages) ([]models.VulnInfo, error) {
func (o *debian) scanUnsecurePackages(upgradable models.Packages) (models.VulnInfos, error) {
o.aptGetUpdate()
// Setup changelog cache
@@ -491,13 +490,13 @@ func (o *debian) scanVulnInfos(upgradablePacks models.Packages, meta *cache.Meta
cveIDs = append(cveIDs, k)
}
o.log.Debugf("%d Cves are found. cves: %v", len(cveIDs), cveIDs)
var vinfos models.VulnInfos
vinfos := models.VulnInfos{}
for cveID, names := range cvePackages {
vinfos = append(vinfos, models.VulnInfo{
vinfos[cveID.CveID] = models.VulnInfo{
CveID: cveID.CveID,
Confidence: cveID.Confidence,
PackageNames: names,
})
}
}
// Update meta package information of changelog cache to the latest one.

View File

@@ -85,7 +85,7 @@ func (o *bsd) scanPackages() error {
}
o.setPackages(packs)
var vinfos []models.VulnInfo
var vinfos models.VulnInfos
if vinfos, err = o.scanUnsecurePackages(); err != nil {
o.log.Errorf("Failed to scan vulnerable packages")
return err
@@ -103,7 +103,7 @@ func (o *bsd) scanInstalledPackages() (models.Packages, error) {
return o.parsePkgVersion(r.Stdout), nil
}
func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
func (o *bsd) scanUnsecurePackages() (models.VulnInfos, error) {
const vulndbPath = "/tmp/vuln.db"
cmd := "rm -f " + vulndbPath
r := o.exec(cmd, noSudo)
@@ -118,7 +118,7 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
}
if r.ExitStatus == 0 {
// no vulnerabilities
return []models.VulnInfo{}, nil
return nil, nil
}
var packAdtRslt []pkgAuditResult
@@ -149,14 +149,15 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
}
}
for k := range cveIDAdtMap {
vinfos := models.VulnInfos{}
for cveID := range cveIDAdtMap {
packs := models.Packages{}
for _, r := range cveIDAdtMap[k] {
for _, r := range cveIDAdtMap[cveID] {
packs[r.pack.Name] = r.pack
}
disAdvs := []models.DistroAdvisory{}
for _, r := range cveIDAdtMap[k] {
for _, r := range cveIDAdtMap[cveID] {
disAdvs = append(disAdvs, models.DistroAdvisory{
AdvisoryID: r.vulnIDCveIDs.vulnID,
})
@@ -166,14 +167,14 @@ func (o *bsd) scanUnsecurePackages() (vulnInfos []models.VulnInfo, err error) {
for name := range packs {
names = append(names, name)
}
vulnInfos = append(vulnInfos, models.VulnInfo{
CveID: k,
vinfos[cveID] = models.VulnInfo{
CveID: cveID,
PackageNames: names,
DistroAdvisories: disAdvs,
Confidence: models.PkgAuditMatch,
})
}
}
return
return vinfos, nil
}
func (o *bsd) parsePkgVersion(stdout string) models.Packages {

View File

@@ -240,7 +240,7 @@ func (o *redhat) scanPackages() error {
}
o.setPackages(models.NewPackages(packs...))
var vinfos []models.VulnInfo
var vinfos models.VulnInfos
if vinfos, err = o.scanVulnInfos(); err != nil {
o.log.Errorf("Failed to scan vulnerable packages")
return err
@@ -292,7 +292,7 @@ func (o *redhat) parseScannedPackagesLine(line string) (models.Package, error) {
}, nil
}
func (o *redhat) scanVulnInfos() ([]models.VulnInfo, error) {
func (o *redhat) scanVulnInfos() (models.VulnInfos, error) {
if o.Distro.Family != "centos" {
// Amazon, RHEL, Oracle Linux has yum updateinfo as default
// yum updateinfo can collenct vendor advisory information.
@@ -423,7 +423,7 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
}
}
vinfos := []models.VulnInfo{}
vinfos := models.VulnInfos{}
for cveID, packs := range cveIDPackages {
names := []string{}
for name := range packs {
@@ -431,11 +431,11 @@ func (o *redhat) scanUnsecurePackagesUsingYumCheckUpdate() (models.VulnInfos, er
}
// Amazon, RHEL do not use this method, so VendorAdvisory do not set.
vinfos = append(vinfos, models.VulnInfo{
vinfos[cveID] = models.VulnInfo{
CveID: cveID,
PackageNames: names,
Confidence: models.ChangelogExactMatch,
})
}
}
return vinfos, nil
}
@@ -741,36 +741,29 @@ func (o *redhat) scanUnsecurePackagesUsingYumPluginSecurity() (models.VulnInfos,
vinfos := models.VulnInfos{}
for _, advIDCveIDs := range advisoryCveIDsList {
for _, cveID := range advIDCveIDs.CveIDs {
found := false
for i, p := range vinfos {
if cveID == p.CveID {
advAppended := append(p.DistroAdvisories, advIDCveIDs.DistroAdvisory)
vinfos[i].DistroAdvisories = advAppended
vinfo, found := vinfos[cveID]
if found {
advAppended := append(vinfo.DistroAdvisories, advIDCveIDs.DistroAdvisory)
vinfo.DistroAdvisories = advAppended
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
for _, pack := range packs {
vinfos[i].PackageNames = append(vinfos[i].PackageNames, pack.Name)
}
found = true
break
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
for _, pack := range packs {
vinfo.PackageNames = append(vinfo.PackageNames, pack.Name)
}
}
if !found {
} else {
names := []string{}
packs := dict[advIDCveIDs.DistroAdvisory.AdvisoryID]
for _, pack := range packs {
names = append(names, pack.Name)
}
cpinfo := models.VulnInfo{
vinfo = models.VulnInfo{
CveID: cveID,
DistroAdvisories: []models.DistroAdvisory{advIDCveIDs.DistroAdvisory},
PackageNames: names,
Confidence: models.YumUpdateSecurityMatch,
}
vinfos = append(vinfos, cpinfo)
}
vinfos[cveID] = vinfo
}
}
return vinfos, nil

View File

@@ -69,7 +69,7 @@ func (p *osPackages) setPackages(pi models.Packages) {
p.Packages = pi
}
func (p *osPackages) setVulnInfos(vi []models.VulnInfo) {
func (p *osPackages) setVulnInfos(vi models.VulnInfos) {
p.VulnInfos = vi
}