mirror of
https://github.com/fleetdm/fleet
synced 2026-05-06 14:58:33 +00:00
Feature: Improve our capability to detect vulnerable software on Ubuntu hosts To improve the capability of detecting vulnerable software on Ubuntu, we are now using OVAL definitions to detect vulnerable software on Ubuntu hosts. If data sync is enabled (disable_data_sync=false) OVAL definitions are automatically kept up to date (they are 'refreshed' once per day) - there's also the option to manually download the OVAL definitions using the 'fleetctl vulnerability-data-stream' command. Downloaded definitions are then parsed into an intermediary format and then used to identify vulnerable software on Ubuntu hosts. Finally, any 'recent' detected vulnerabilities are sent to any third-party integrations.
101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
package webhooks
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"path"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/server"
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
kitlog "github.com/go-kit/kit/log"
|
|
"github.com/go-kit/kit/log/level"
|
|
)
|
|
|
|
// TriggerVulnerabilitiesWebhook performs the webhook requests for vulnerabilities.
|
|
func TriggerVulnerabilitiesWebhook(
|
|
ctx context.Context,
|
|
ds fleet.Datastore,
|
|
logger kitlog.Logger,
|
|
recentVulns []fleet.SoftwareVulnerability,
|
|
appConfig *fleet.AppConfig,
|
|
now time.Time,
|
|
) error {
|
|
vulnConfig := appConfig.WebhookSettings.VulnerabilitiesWebhook
|
|
if !vulnConfig.Enable {
|
|
return nil
|
|
}
|
|
|
|
level.Debug(logger).Log("enabled", "true", "recentVulns", len(recentVulns))
|
|
|
|
serverURL, err := url.Parse(appConfig.ServerSettings.ServerURL)
|
|
if err != nil {
|
|
return ctxerr.Wrap(ctx, err, "invalid server url")
|
|
}
|
|
|
|
targetURL := vulnConfig.DestinationURL
|
|
batchSize := vulnConfig.HostBatchSize
|
|
|
|
softwareIDsGroupedByCVE := make(map[string][]uint)
|
|
for _, v := range recentVulns {
|
|
softwareIDsGroupedByCVE[v.CVE] = append(softwareIDsGroupedByCVE[v.CVE], v.SoftwareID)
|
|
}
|
|
|
|
for _, v := range recentVulns {
|
|
softwareIDs := softwareIDsGroupedByCVE[v.CVE]
|
|
|
|
hosts, err := ds.HostsBySoftwareIDs(ctx, softwareIDs)
|
|
if err != nil {
|
|
return ctxerr.Wrap(ctx, err, "get hosts by CPE")
|
|
}
|
|
|
|
for len(hosts) > 0 {
|
|
limit := len(hosts)
|
|
if batchSize > 0 && len(hosts) > batchSize {
|
|
limit = batchSize
|
|
}
|
|
if err := sendVulnerabilityHostBatch(ctx, targetURL, v.CVE, serverURL, hosts[:limit], now); err != nil {
|
|
return ctxerr.Wrap(ctx, err, "send vulnerability host batch")
|
|
}
|
|
hosts = hosts[limit:]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type vulnHostPayload struct {
|
|
ID uint `json:"id"`
|
|
Hostname string `json:"hostname"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
func sendVulnerabilityHostBatch(ctx context.Context, targetURL, cve string, hostBaseURL *url.URL, hosts []*fleet.HostShort, now time.Time) error {
|
|
shortHosts := make([]*vulnHostPayload, len(hosts))
|
|
for i, h := range hosts {
|
|
hostURL := *hostBaseURL
|
|
hostURL.Path = path.Join(hostURL.Path, "hosts", strconv.Itoa(int(h.ID)))
|
|
shortHosts[i] = &vulnHostPayload{
|
|
ID: h.ID,
|
|
Hostname: h.Hostname,
|
|
URL: hostURL.String(),
|
|
}
|
|
}
|
|
|
|
payload := map[string]interface{}{
|
|
"timestamp": now,
|
|
"vulnerability": map[string]interface{}{
|
|
"cve": cve,
|
|
"details_link": fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", cve),
|
|
"hosts_affected": shortHosts,
|
|
},
|
|
}
|
|
|
|
if err := server.PostJSONWithTimeout(ctx, targetURL, &payload); err != nil {
|
|
return ctxerr.Wrapf(ctx, err, "posting to %s", targetURL)
|
|
}
|
|
return nil
|
|
}
|