mirror of
https://github.com/empayre/fleet.git
synced 2024-11-06 17:05:18 +00:00
4c2ddba2e4
Keep the vulnerabilities detected via NVD and stored in the DB in sync. with the results from the NVD vulnerability process.
159 lines
3.7 KiB
Go
159 lines
3.7 KiB
Go
package oval
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
oval_parsed "github.com/fleetdm/fleet/v4/server/vulnerabilities/oval/parsed"
|
|
utils "github.com/fleetdm/fleet/v4/server/vulnerabilities/utils"
|
|
)
|
|
|
|
const (
|
|
hostsBatchSize = 500
|
|
vulnBatchSize = 500
|
|
)
|
|
|
|
// Analyze scans all hosts for vulnerabilities based on the OVAL definitions for their platform,
|
|
// inserting any new vulnerabilities and deleting anything patched.
|
|
func Analyze(
|
|
ctx context.Context,
|
|
ds fleet.Datastore,
|
|
ver fleet.OSVersion,
|
|
vulnPath string,
|
|
collectVulns bool,
|
|
) ([]fleet.SoftwareVulnerability, error) {
|
|
platform := NewPlatform(ver.Platform, ver.Name)
|
|
|
|
source := fleet.UbuntuOVALSource
|
|
if platform.IsRedHat() {
|
|
source = fleet.RHELOVALSource
|
|
}
|
|
|
|
if !platform.IsSupported() {
|
|
return nil, nil
|
|
}
|
|
|
|
defs, err := loadDef(platform, vulnPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Since hosts and software have a M:N relationship, the following sets are used to
|
|
// avoid doing duplicated inserts/delete operations (a vulnerable software might be
|
|
// present in many hosts).
|
|
toInsertSet := make(map[string]fleet.SoftwareVulnerability)
|
|
toDeleteSet := make(map[string]fleet.SoftwareVulnerability)
|
|
|
|
var offset int
|
|
for {
|
|
hostIDs, err := ds.HostIDsByOSVersion(ctx, ver, offset, hostsBatchSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(hostIDs) == 0 {
|
|
break
|
|
}
|
|
offset += hostsBatchSize
|
|
|
|
foundInBatch := make(map[uint][]fleet.SoftwareVulnerability)
|
|
for _, hostID := range hostIDs {
|
|
software, err := ds.ListSoftwareForVulnDetection(ctx, hostID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
evalR, err := defs.Eval(ver, software)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
foundInBatch[hostID] = evalR
|
|
}
|
|
|
|
existingInBatch, err := ds.ListSoftwareVulnerabilitiesByHostIDsSource(ctx, hostIDs, source)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, hostID := range hostIDs {
|
|
insrt, del := utils.VulnsDelta(foundInBatch[hostID], existingInBatch[hostID])
|
|
for _, i := range insrt {
|
|
toInsertSet[i.Key()] = i
|
|
}
|
|
for _, d := range del {
|
|
toDeleteSet[d.Key()] = d
|
|
}
|
|
}
|
|
}
|
|
|
|
err = utils.BatchProcess(toDeleteSet, func(v []fleet.SoftwareVulnerability) error {
|
|
return ds.DeleteSoftwareVulnerabilities(ctx, v)
|
|
}, vulnBatchSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var inserted []fleet.SoftwareVulnerability
|
|
if collectVulns {
|
|
inserted = make([]fleet.SoftwareVulnerability, 0, len(toInsertSet))
|
|
}
|
|
|
|
err = utils.BatchProcess(toInsertSet, func(vulns []fleet.SoftwareVulnerability) error {
|
|
for _, v := range vulns {
|
|
ok, err := ds.InsertSoftwareVulnerability(ctx, v, source)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collectVulns && ok {
|
|
inserted = append(inserted, v)
|
|
}
|
|
}
|
|
return nil
|
|
}, vulnBatchSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return inserted, nil
|
|
}
|
|
|
|
// loadDef returns the latest oval Definition for the given platform.
|
|
func loadDef(platform Platform, vulnPath string) (oval_parsed.Result, error) {
|
|
if !platform.IsSupported() {
|
|
return nil, fmt.Errorf("platform %q not supported", platform)
|
|
}
|
|
|
|
fileName := platform.ToFilename(time.Now(), "json")
|
|
latest, err := utils.LatestFile(fileName, vulnPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
payload, err := ioutil.ReadFile(latest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if platform.IsUbuntu() {
|
|
result := oval_parsed.UbuntuResult{}
|
|
if err := json.Unmarshal(payload, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
if platform.IsRedHat() {
|
|
result := oval_parsed.RhelResult{}
|
|
if err := json.Unmarshal(payload, &result); err != nil {
|
|
return nil, err
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
return nil, fmt.Errorf("don't know how to parse file %q for %q platform", latest, platform)
|
|
}
|