fleet/server/vulnerabilities/msrc/msrc_api.go

84 lines
2 KiB
Go
Raw Normal View History

package msrc
import (
"errors"
"fmt"
"net/http"
"net/url"
"path/filepath"
"time"
"github.com/fleetdm/fleet/v4/pkg/download"
)
const (
// Pre 2020 there are some weirdness around the way the 'Supersedes' field is defined for Vulnerabilities, sometimes
// it does not reference a KBID.
MSRCMinYear = 2020
MSRCBaseURL = `https://api.msrc.microsoft.com`
)
// MSRCAPI allows users to interact with MSRC resources
type MSRCAPI interface {
GetFeed(time.Month, int) (string, error)
}
// FeedNotFound is returned when a MSRC feed was not found.
//
// E.g. September 2024 bulleting was released on the 2nd.
var FeedNotFound = errors.New("feed not found")
type MSRCClient struct {
client *http.Client
workDir string
baseURL string
}
// NewMSRCClient returns a new MSRCClient that will store all downloaded files in 'workDir' and will
// use 'baseURL' for doing http requests.
func NewMSRCClient(
client *http.Client,
workDir string,
baseURL string,
) MSRCClient {
return MSRCClient{client: client, workDir: workDir, baseURL: baseURL}
}
func feedName(date time.Time) string {
return date.Format("2006-Jan")
}
func (msrc MSRCClient) getURL(date time.Time) (*url.URL, error) {
return url.Parse(msrc.baseURL + "/cvrf/v3.0/document/" + feedName(date))
}
// GetFeed downloads the MSRC security feed for 'month' and 'year' into 'workDir', returning the
// path of the downloaded file.
func (msrc MSRCClient) GetFeed(month time.Month, year int) (string, error) {
d := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
minD := time.Date(MSRCMinYear, time.January, 1, 0, 0, 0, 0, time.UTC)
if d.Before(minD) {
return "", fmt.Errorf("min allowed date is %s", minD)
}
if d.After(time.Now().UTC()) {
return "", errors.New("date can't be in the future")
}
dst := filepath.Join(msrc.workDir, fmt.Sprintf("%s.xml", feedName(d)))
u, err := msrc.getURL(d)
if err != nil {
return "", err
}
if err := download.Download(msrc.client, u, dst); err != nil {
if errors.Is(err, download.NotFound) {
return "", FeedNotFound
}
return "", err
}
return dst, nil
}