mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 01:18:42 +00:00
feat: add populate software query param (#15661)
# Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [x] Changes file added for user-visible changes in `changes/` or `orbit/changes/`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [x] Added/updated tests - [x] Manual QA for all new/changed functionality
This commit is contained in:
parent
ced538c916
commit
bcb66e8893
6 changed files with 125 additions and 1 deletions
2
changes/12889-faster-software
Normal file
2
changes/12889-faster-software
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
- Adds a `populate_software` flag to the `GET /hosts` endpoint, which will include software for each
|
||||
host returned from that endpoint.
|
||||
|
|
@ -177,6 +177,9 @@ type HostListOptions struct {
|
|||
// Premium feature, Fleet Free ignores the setting (it forces it to nil to
|
||||
// disable it).
|
||||
LowDiskSpaceFilter *int
|
||||
|
||||
// PopulateSoftware adds the `Software` field to all Hosts returned.
|
||||
PopulateSoftware bool
|
||||
}
|
||||
|
||||
// TODO(Sarah): Are we missing any filters here? Should all MDM filters be included?
|
||||
|
|
|
|||
|
|
@ -179,7 +179,20 @@ func (svc *Service) ListHosts(ctx context.Context, opt fleet.HostListOptions) ([
|
|||
opt.MDMBootstrapPackageFilter = nil
|
||||
}
|
||||
|
||||
return svc.ds.ListHosts(ctx, filter, opt)
|
||||
hosts, err := svc.ds.ListHosts(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opt.PopulateSoftware {
|
||||
for _, host := range hosts {
|
||||
if err = svc.ds.LoadHostSoftware(ctx, host, license.IsPremium(ctx)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hosts, nil
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -1670,6 +1670,51 @@ func (s *integrationTestSuite) TestListHosts() {
|
|||
resp = listHostsResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &resp, "os_id", fmt.Sprintf("%d", osID+1337))
|
||||
require.Len(t, resp.Hosts, 0)
|
||||
|
||||
// populate software for hosts
|
||||
now := time.Now()
|
||||
|
||||
inserted, err := s.ds.InsertSoftwareVulnerability(context.Background(), fleet.SoftwareVulnerability{
|
||||
SoftwareID: host2.Software[0].ID,
|
||||
CVE: "cve-123-123-123",
|
||||
}, fleet.NVDSource)
|
||||
require.NoError(t, err)
|
||||
require.True(t, inserted)
|
||||
|
||||
require.NoError(t, s.ds.InsertCVEMeta(context.Background(), []fleet.CVEMeta{{
|
||||
CVE: "cve-123-123-123",
|
||||
CVSSScore: ptr.Float64(5.4),
|
||||
EPSSProbability: ptr.Float64(0.5),
|
||||
CISAKnownExploit: ptr.Bool(true),
|
||||
Published: &now,
|
||||
Description: "a long description of the cve",
|
||||
}}))
|
||||
|
||||
resp = listHostsResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &resp, "populate_software", "true")
|
||||
require.Len(t, resp.Hosts, 4)
|
||||
for _, h := range resp.Hosts {
|
||||
if h.ID == hosts[2].ID {
|
||||
require.NotEmpty(t, h.Software)
|
||||
require.Len(t, h.Software, 1)
|
||||
require.NotEmpty(t, h.Software[0].Vulnerabilities)
|
||||
|
||||
// all these should be nil because this isn't Premium
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].CVSSScore)
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].EPSSProbability)
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].CISAKnownExploit)
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].CVEPublished)
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].Description)
|
||||
require.Nil(t, h.Software[0].Vulnerabilities[0].ResolvedInVersion)
|
||||
}
|
||||
}
|
||||
|
||||
resp = listHostsResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &resp, "populate_software", "false")
|
||||
require.Len(t, resp.Hosts, 4)
|
||||
for _, h := range resp.Hosts {
|
||||
require.Empty(t, h.Software)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *integrationTestSuite) TestInvites() {
|
||||
|
|
|
|||
|
|
@ -3030,6 +3030,59 @@ func (s *integrationEnterpriseTestSuite) TestListHosts() {
|
|||
s.DoJSON("GET", "/api/latest/fleet/host_summary", nil, http.StatusOK, &summaryResp, "team_id", "1", "platform", "linux")
|
||||
require.Equal(t, uint(0), summaryResp.TotalsHostsCount)
|
||||
require.Nil(t, summaryResp.LowDiskSpaceCount)
|
||||
|
||||
// populate software for hosts
|
||||
now := time.Now()
|
||||
|
||||
software := []fleet.Software{
|
||||
{Name: "foo", Version: "0.0.1", Source: "chrome_extensions"},
|
||||
}
|
||||
_, err = s.ds.UpdateHostSoftware(context.Background(), host1.ID, software)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, s.ds.LoadHostSoftware(context.Background(), host1, false))
|
||||
|
||||
inserted, err := s.ds.InsertSoftwareVulnerability(context.Background(), fleet.SoftwareVulnerability{
|
||||
SoftwareID: host1.Software[0].ID,
|
||||
CVE: "cve-123-123-123",
|
||||
}, fleet.NVDSource)
|
||||
require.NoError(t, err)
|
||||
require.True(t, inserted)
|
||||
|
||||
vulnMeta := []fleet.CVEMeta{{
|
||||
CVE: "cve-123-123-123",
|
||||
CVSSScore: ptr.Float64(5.4),
|
||||
EPSSProbability: ptr.Float64(0.5),
|
||||
CISAKnownExploit: ptr.Bool(true),
|
||||
Published: &now,
|
||||
Description: "a long description of the cve",
|
||||
}}
|
||||
|
||||
require.NoError(t, s.ds.InsertCVEMeta(context.Background(), vulnMeta))
|
||||
|
||||
resp = listHostsResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &resp, "populate_software", "true")
|
||||
require.Len(t, resp.Hosts, 3)
|
||||
for _, h := range resp.Hosts {
|
||||
if h.ID == host1.ID {
|
||||
require.NotEmpty(t, h.Software)
|
||||
require.Len(t, h.Software, 1)
|
||||
require.NotEmpty(t, h.Software[0].Vulnerabilities)
|
||||
|
||||
s := &vulnMeta[0].Description
|
||||
require.Equal(t, &vulnMeta[0].CVSSScore, h.Software[0].Vulnerabilities[0].CVSSScore)
|
||||
require.Equal(t, &vulnMeta[0].EPSSProbability, h.Software[0].Vulnerabilities[0].EPSSProbability)
|
||||
require.Equal(t, &vulnMeta[0].CISAKnownExploit, h.Software[0].Vulnerabilities[0].CISAKnownExploit)
|
||||
require.Equal(t, &s, h.Software[0].Vulnerabilities[0].Description)
|
||||
}
|
||||
}
|
||||
|
||||
resp = listHostsResponse{}
|
||||
s.DoJSON("GET", "/api/latest/fleet/hosts", nil, http.StatusOK, &resp, "populate_software", "false")
|
||||
require.Len(t, resp.Hosts, 3)
|
||||
for _, h := range resp.Hosts {
|
||||
require.Empty(t, h.Software)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *integrationEnterpriseTestSuite) TestMDMNotConfiguredEndpoints() {
|
||||
|
|
|
|||
|
|
@ -483,6 +483,14 @@ func hostListOptionsFromRequest(r *http.Request) (fleet.HostListOptions, error)
|
|||
}
|
||||
hopt.LowDiskSpaceFilter = &v
|
||||
}
|
||||
populateSoftware := r.URL.Query().Get("populate_software")
|
||||
if populateSoftware != "" {
|
||||
ps, err := strconv.ParseBool(populateSoftware)
|
||||
if err != nil {
|
||||
return hopt, ctxerr.Wrap(r.Context(), badRequest(fmt.Sprintf("Invalid populate_software: %s", populateSoftware)))
|
||||
}
|
||||
hopt.PopulateSoftware = ps
|
||||
}
|
||||
|
||||
// cannot combine software_id, software_version_id, and software_title_id
|
||||
var softwareErrorLabel []string
|
||||
|
|
|
|||
Loading…
Reference in a new issue