fleet/server/webhooks/vulnerabilities.go

94 lines
2.4 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 map[string][]string,
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
for cve, cpes := range recentVulns {
hosts, err := ds.HostsByCPEs(ctx, cpes)
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, 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
}