mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #37244 # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Internal MySQL utility package reorganized and all internal imports updated to the new platform location; no changes to end-user functionality or behavior. * **Documentation** * Added platform package documentation describing infrastructure responsibilities and architectural boundaries to guide maintainers. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1402 lines
40 KiB
Go
1402 lines
40 KiB
Go
package mysql
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
common_mysql "github.com/fleetdm/fleet/v4/server/platform/mysql"
|
|
"github.com/fleetdm/fleet/v4/server/ptr"
|
|
"github.com/fleetdm/fleet/v4/server/test"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestVulnerabilities(t *testing.T) {
|
|
ds := CreateMySQLDS(t)
|
|
|
|
cases := []struct {
|
|
name string
|
|
fn func(t *testing.T, ds *Datastore)
|
|
}{
|
|
{"TestListVulnerabilities", testListVulnerabilities},
|
|
{"TestVulnerabilityWithOS", testVulnerabilityWithOS},
|
|
{"TestVulnerabilityWithSoftware", testVulnerabilityWithSoftware},
|
|
{"TestOSVersionsByCVEFailsGracefullyWithNoOSVersionRows", testOSVersionsByCVEFailsGracefullyWithNoOSVersionRows},
|
|
{"TestOSVersionsByCVE", testOSVersionsByCVE},
|
|
{"TestSoftwareByCVE", testSoftwareByCVE},
|
|
{"TestVulnerabilitiesPagination", testVulnerabilitiesPagination},
|
|
{"TestVulnerabilitiesTeamFilter", testVulnerabilitiesTeamFilter},
|
|
{"TestListVulnerabilitiesSort", testListVulnerabilitiesSort},
|
|
{"TestVulnerabilitiesFilters", testVulnerabilitiesFilters},
|
|
{"TestCountVulnerabilities", testCountVulnerabilities},
|
|
{"TestInsertVulnerabilityCounts", testInsertVulnerabilityCounts},
|
|
{"TestVulnerabilityHostCountBatchInserts", testVulnerabilityHostCountBatchInserts},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
t.Helper()
|
|
defer TruncateTables(t, ds)
|
|
c.fn(t, ds)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testListVulnerabilities(t *testing.T, ds *Datastore) {
|
|
mockTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
opts := fleet.VulnListOptions{}
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Empty(t, list)
|
|
|
|
// Insert Host Count
|
|
insertStmt := `
|
|
INSERT INTO vulnerability_host_counts (cve, team_id, host_count, global_stats)
|
|
VALUES (?, ?, ?, ?)
|
|
`
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1234", 0, 10, 1)
|
|
require.NoError(t, err)
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1235", 0, 15, 1)
|
|
require.NoError(t, err)
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1236", 0, 20, 1)
|
|
require.NoError(t, err)
|
|
|
|
// No Vulns unless OS or Software Vulns are inserted
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 0)
|
|
|
|
// insert OS Vuln
|
|
_, err = ds.InsertOSVulnerabilities(context.Background(), []fleet.OSVulnerability{
|
|
{
|
|
OSID: 1,
|
|
CVE: "CVE-2020-1234",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
{
|
|
OSID: 1,
|
|
CVE: "CVE-2020-1235",
|
|
},
|
|
}, fleet.MSRCSource)
|
|
require.NoError(t, err)
|
|
|
|
// insert Software Vuln
|
|
_, err = ds.InsertSoftwareVulnerability(context.Background(), fleet.SoftwareVulnerability{
|
|
SoftwareID: 1,
|
|
CVE: "CVE-2020-1236",
|
|
}, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
// insert CVEMeta
|
|
err = ds.InsertCVEMeta(context.Background(), []fleet.CVEMeta{
|
|
{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64(7.5),
|
|
EPSSProbability: ptr.Float64(0.5),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime),
|
|
Description: "Test CVE 2020-1234",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expected := map[string]fleet.VulnerabilityWithMetadata{
|
|
"CVE-2020-1234": {
|
|
CVE: fleet.CVE{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64Ptr(7.5),
|
|
EPSSProbability: ptr.Float64Ptr(0.5),
|
|
CISAKnownExploit: ptr.BoolPtr(true),
|
|
CVEPublished: ptr.TimePtr(mockTime),
|
|
Description: ptr.StringPtr("Test CVE 2020-1234"),
|
|
},
|
|
HostsCount: 10,
|
|
Source: fleet.MSRCSource,
|
|
},
|
|
"CVE-2020-1235": {
|
|
CVE: fleet.CVE{CVE: "CVE-2020-1235"},
|
|
HostsCount: 15,
|
|
Source: fleet.MSRCSource,
|
|
},
|
|
"CVE-2020-1236": {
|
|
CVE: fleet.CVE{CVE: "CVE-2020-1236"},
|
|
HostsCount: 20,
|
|
Source: fleet.NVDSource,
|
|
},
|
|
}
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{IsEE: true})
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 3)
|
|
for _, vuln := range list {
|
|
expectedVuln, ok := expected[vuln.CVE.CVE]
|
|
require.True(t, ok)
|
|
require.Equal(t, expectedVuln.CVE, vuln.CVE)
|
|
require.Equal(t, expectedVuln.HostsCount, vuln.HostsCount)
|
|
}
|
|
|
|
// Test Fleet Free
|
|
expected = map[string]fleet.VulnerabilityWithMetadata{
|
|
"CVE-2020-1234": {
|
|
CVE: fleet.CVE{CVE: "CVE-2020-1234"},
|
|
HostsCount: 10,
|
|
Source: fleet.MSRCSource,
|
|
},
|
|
"CVE-2020-1235": {
|
|
CVE: fleet.CVE{CVE: "CVE-2020-1235"},
|
|
HostsCount: 15,
|
|
Source: fleet.MSRCSource,
|
|
},
|
|
"CVE-2020-1236": {
|
|
CVE: fleet.CVE{CVE: "CVE-2020-1236"},
|
|
HostsCount: 20,
|
|
Source: fleet.NVDSource,
|
|
},
|
|
}
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 3)
|
|
for _, vuln := range list {
|
|
expectedVuln, ok := expected[vuln.CVE.CVE]
|
|
require.True(t, ok)
|
|
require.Equal(t, expectedVuln.CVE, vuln.CVE)
|
|
require.Equal(t, expectedVuln.HostsCount, vuln.HostsCount)
|
|
}
|
|
}
|
|
|
|
func testVulnerabilityWithOS(t *testing.T, ds *Datastore) {
|
|
mockTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ctx := context.Background()
|
|
|
|
v, err := ds.Vulnerability(ctx, "CVE-2020-1234", nil, false)
|
|
require.Nil(t, v)
|
|
require.Error(t, err)
|
|
var nfe *common_mysql.NotFoundError
|
|
require.ErrorAs(t, err, &nfe)
|
|
|
|
// Insert Host Count
|
|
insertStmt := `
|
|
INSERT INTO vulnerability_host_counts (cve, team_id, host_count, global_stats)
|
|
VALUES (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?)
|
|
`
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt,
|
|
"CVE-2020-1234", 0, 10, 1, // global
|
|
"CVE-2020-1234", 1, 4, 0, // team 1
|
|
"CVE-2020-1234", 0, 6, 0, // no team
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// // insert OS Vuln
|
|
_, err = ds.InsertOSVulnerabilities(context.Background(), []fleet.OSVulnerability{
|
|
{
|
|
OSID: 1,
|
|
CVE: "CVE-2020-1234",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
}, fleet.MSRCSource)
|
|
require.NoError(t, err)
|
|
|
|
// // insert CVEMeta
|
|
err = ds.InsertCVEMeta(context.Background(), []fleet.CVEMeta{
|
|
{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64(7.5),
|
|
EPSSProbability: ptr.Float64(0.5),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime),
|
|
Description: "Test CVE 2020-1234",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
expected := fleet.VulnerabilityWithMetadata{
|
|
CVE: fleet.CVE{
|
|
CVE: "CVE-2020-1234",
|
|
},
|
|
HostsCount: 10,
|
|
Source: fleet.MSRCSource,
|
|
CreatedAt: mockTime,
|
|
}
|
|
|
|
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
|
// Mock the time to make it easier to check
|
|
_, err := q.ExecContext(ctx, "UPDATE operating_system_vulnerabilities SET created_at = ? WHERE cve = ?", mockTime, expected.CVE.CVE)
|
|
return err
|
|
})
|
|
|
|
// No CVSSScores
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", nil, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
// Team 1
|
|
expected.HostsCount = 4
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", ptr.Uint(1), false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
// No Team
|
|
expected.HostsCount = 6
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", ptr.Uint(0), false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
expected = fleet.VulnerabilityWithMetadata{
|
|
CVE: fleet.CVE{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64Ptr(7.5),
|
|
EPSSProbability: ptr.Float64Ptr(0.5),
|
|
CISAKnownExploit: ptr.BoolPtr(true),
|
|
CVEPublished: ptr.TimePtr(mockTime),
|
|
Description: ptr.StringPtr("Test CVE 2020-1234"),
|
|
},
|
|
HostsCount: 10,
|
|
Source: fleet.MSRCSource,
|
|
CreatedAt: mockTime,
|
|
}
|
|
|
|
// With CVSSScores
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", nil, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
}
|
|
|
|
func testVulnerabilityWithSoftware(t *testing.T, ds *Datastore) {
|
|
mockTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
ctx := context.Background()
|
|
|
|
v, err := ds.Vulnerability(ctx, "CVE-2020-1234", nil, false)
|
|
require.Nil(t, v)
|
|
require.Error(t, err)
|
|
var nfe *common_mysql.NotFoundError
|
|
require.ErrorAs(t, err, &nfe)
|
|
|
|
// Insert Host Count
|
|
insertStmt := `
|
|
INSERT INTO vulnerability_host_counts (cve, team_id, host_count, global_stats)
|
|
VALUES (?, ?, ?, ?)
|
|
`
|
|
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1234", 0, 10, 1)
|
|
require.NoError(t, err)
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1234", 1, 4, 0)
|
|
require.NoError(t, err)
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, "CVE-2020-1234", 0, 6, 0)
|
|
require.NoError(t, err)
|
|
|
|
// insert Software Vuln
|
|
_, err = ds.InsertSoftwareVulnerability(context.Background(), fleet.SoftwareVulnerability{
|
|
SoftwareID: 1,
|
|
CVE: "CVE-2020-1234",
|
|
}, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
// insert CVEMeta
|
|
err = ds.InsertCVEMeta(context.Background(), []fleet.CVEMeta{
|
|
{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64(7.5),
|
|
EPSSProbability: ptr.Float64(0.5),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime),
|
|
Description: "Test CVE 2020-1234",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// No CVSSScores
|
|
expected := fleet.VulnerabilityWithMetadata{
|
|
CVE: fleet.CVE{
|
|
CVE: "CVE-2020-1234",
|
|
},
|
|
HostsCount: 10,
|
|
Source: fleet.NVDSource,
|
|
CreatedAt: mockTime,
|
|
}
|
|
|
|
ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error {
|
|
// Mock the time to make it easier to check
|
|
_, err := q.ExecContext(ctx, "UPDATE software_cve SET created_at = ? WHERE cve = ?", mockTime, expected.CVE.CVE)
|
|
return err
|
|
})
|
|
|
|
// Global (all teams)
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", nil, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
// Team 1
|
|
expected.HostsCount = 4
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", ptr.Uint(1), false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
// No Team
|
|
expected.HostsCount = 6
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", ptr.Uint(0), false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
|
|
// With CVSSScores
|
|
expected = fleet.VulnerabilityWithMetadata{
|
|
CVE: fleet.CVE{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64Ptr(7.5),
|
|
EPSSProbability: ptr.Float64Ptr(0.5),
|
|
CISAKnownExploit: ptr.BoolPtr(true),
|
|
CVEPublished: ptr.TimePtr(mockTime),
|
|
Description: ptr.StringPtr("Test CVE 2020-1234"),
|
|
},
|
|
HostsCount: 10,
|
|
Source: fleet.NVDSource,
|
|
CreatedAt: mockTime,
|
|
}
|
|
|
|
v, err = ds.Vulnerability(ctx, "CVE-2020-1234", nil, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, expected.CVE, v.CVE)
|
|
require.Equal(t, expected.HostsCount, v.HostsCount)
|
|
require.Equal(t, expected.Source, v.Source)
|
|
require.Equal(t, expected.CreatedAt, v.CreatedAt)
|
|
}
|
|
|
|
func testVulnerabilitiesPagination(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
opts := fleet.VulnListOptions{
|
|
ListOptions: fleet.ListOptions{
|
|
Page: 0,
|
|
PerPage: 5,
|
|
},
|
|
}
|
|
|
|
list, meta, err := ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 5)
|
|
require.NotNil(t, meta)
|
|
require.False(t, meta.HasPreviousResults)
|
|
require.True(t, meta.HasNextResults)
|
|
|
|
opts.ListOptions.Page = 1
|
|
list, meta, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 2)
|
|
require.NotNil(t, meta)
|
|
require.True(t, meta.HasPreviousResults)
|
|
require.False(t, meta.HasNextResults)
|
|
}
|
|
|
|
func testVulnerabilitiesTeamFilter(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
//
|
|
// Team 1
|
|
//
|
|
opts := fleet.VulnListOptions{
|
|
TeamID: ptr.Uint(1),
|
|
}
|
|
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 6)
|
|
|
|
checkCounts := map[string]int{
|
|
"CVE-2020-1234": 20,
|
|
"CVE-2020-1235": 19,
|
|
"CVE-2020-1236": 18,
|
|
"CVE-2020-1238": 16,
|
|
"CVE-2020-1239": 15,
|
|
// No team host counts for CVE-2020-1240
|
|
"CVE-2020-1241": 14,
|
|
}
|
|
|
|
for _, vuln := range list {
|
|
require.EqualValues(t, checkCounts[vuln.CVE.CVE], vuln.HostsCount, vuln.CVE)
|
|
}
|
|
|
|
//
|
|
// Team 0 (no team)
|
|
//
|
|
opts = fleet.VulnListOptions{
|
|
TeamID: ptr.Uint(0),
|
|
}
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 6)
|
|
|
|
checkCounts = map[string]int{
|
|
"CVE-2020-1234": 80,
|
|
"CVE-2020-1235": 71,
|
|
"CVE-2020-1236": 62,
|
|
"CVE-2020-1238": 44,
|
|
"CVE-2020-1239": 35,
|
|
// No team host counts for CVE-2020-1240
|
|
"CVE-2020-1241": 26,
|
|
}
|
|
|
|
for _, vuln := range list {
|
|
require.EqualValues(t, checkCounts[vuln.CVE.CVE], vuln.HostsCount, vuln.CVE)
|
|
}
|
|
|
|
//
|
|
// Global (all teams)
|
|
//
|
|
opts = fleet.VulnListOptions{}
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 7)
|
|
|
|
checkCounts = map[string]int{
|
|
"CVE-2020-1234": 100,
|
|
"CVE-2020-1235": 90,
|
|
"CVE-2020-1236": 80,
|
|
"CVE-2020-1237": 70,
|
|
"CVE-2020-1238": 60,
|
|
"CVE-2020-1239": 50,
|
|
// No team host counts for CVE-2020-1240
|
|
"CVE-2020-1241": 40,
|
|
}
|
|
|
|
for _, vuln := range list {
|
|
require.EqualValues(t, checkCounts[vuln.CVE.CVE], vuln.HostsCount, vuln.CVE)
|
|
}
|
|
}
|
|
|
|
func testListVulnerabilitiesSort(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
opts := fleet.VulnListOptions{
|
|
IsEE: true,
|
|
ListOptions: fleet.ListOptions{
|
|
Page: 0,
|
|
PerPage: 5,
|
|
OrderKey: "cve",
|
|
OrderDirection: fleet.OrderDescending,
|
|
},
|
|
}
|
|
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 5)
|
|
require.Equal(t, "CVE-2020-1241", list[0].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1239", list[1].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1238", list[2].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1237", list[3].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1236", list[4].CVE.CVE)
|
|
|
|
opts.ListOptions.OrderKey = "published"
|
|
opts.ListOptions.OrderDirection = fleet.OrderAscending
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 5)
|
|
require.Equal(t, "CVE-2020-1241", list[0].CVE.CVE) // NULL dates are sorted first
|
|
require.Equal(t, "CVE-2020-1234", list[1].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1236", list[2].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1235", list[3].CVE.CVE)
|
|
require.Equal(t, "CVE-2020-1237", list[4].CVE.CVE)
|
|
}
|
|
|
|
func testVulnerabilitiesFilters(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
// Test KnownExploit filter
|
|
opts := fleet.VulnListOptions{
|
|
IsEE: true,
|
|
KnownExploit: true,
|
|
}
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, list, 3)
|
|
expected := []string{"CVE-2020-1234", "CVE-2020-1236", "CVE-2020-1238"}
|
|
for _, vuln := range list {
|
|
require.Contains(t, expected, vuln.CVE.CVE)
|
|
}
|
|
|
|
// Test CVE LIKE filter
|
|
opts = fleet.VulnListOptions{
|
|
ListOptions: fleet.ListOptions{
|
|
MatchQuery: "2020-1234",
|
|
},
|
|
}
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), opts)
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 1)
|
|
require.Equal(t, "CVE-2020-1234", list[0].CVE.CVE)
|
|
}
|
|
|
|
func testCountVulnerabilities(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
// global count
|
|
count, err := ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(7), count)
|
|
|
|
// global count with exploit filter
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{KnownExploit: true})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(3), count)
|
|
|
|
// global count with match query
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{ListOptions: fleet.ListOptions{MatchQuery: "2020-1234"}})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(1), count)
|
|
|
|
// team count
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(1)})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(6), count)
|
|
|
|
// team count with exploit filter
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(1), KnownExploit: true})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(3), count)
|
|
|
|
// team count with match query
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(1), ListOptions: fleet.ListOptions{MatchQuery: "2020-1234"}})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(1), count)
|
|
|
|
// team count with exploit filter and match query
|
|
count, err = ds.CountVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(1), KnownExploit: true, ListOptions: fleet.ListOptions{MatchQuery: "2020-1234"}})
|
|
require.NoError(t, err)
|
|
require.Equal(t, uint(1), count)
|
|
}
|
|
|
|
func testInsertVulnerabilityCounts(t *testing.T, ds *Datastore) {
|
|
windowsOS := fleet.OperatingSystem{
|
|
Name: "Windows 11 Pro",
|
|
Version: "10.0.22000.3007",
|
|
Arch: "x86_64",
|
|
Platform: "windows",
|
|
}
|
|
|
|
macOS := fleet.OperatingSystem{
|
|
Name: "macOS",
|
|
Version: "14.1.2",
|
|
Arch: "arm64",
|
|
Platform: "darwin",
|
|
}
|
|
|
|
// create Windows host1
|
|
host1 := test.NewHost(t, ds, "host1", "192.168.0.1", "1111", "1111", time.Now())
|
|
|
|
err := ds.UpdateHostOperatingSystem(context.Background(), host1.ID, windowsOS)
|
|
require.NoError(t, err)
|
|
|
|
// assert no vulns
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
require.Empty(t, list)
|
|
|
|
// insert Windows OS vulnerability
|
|
_, err = ds.InsertOSVulnerability(context.Background(), fleet.OSVulnerability{
|
|
OSID: 1,
|
|
CVE: "CVE-2020-1234",
|
|
}, fleet.MSRCSource)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected := []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 1},
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// add host 2 with same OS
|
|
host2 := test.NewHost(t, ds, "host2", "192.168.0.2", "2222", "2222", time.Now())
|
|
err = ds.UpdateHostOperatingSystem(context.Background(), host2.ID, windowsOS)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2},
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// add 1 macOS host
|
|
host3 := test.NewHost(t, ds, "host3", "192.168.0.3", "3333", "3333", time.Now())
|
|
err = ds.UpdateHostOperatingSystem(context.Background(), host3.ID, macOS)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// assert no new vulns
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// add macos vulnerability
|
|
_, err = ds.InsertOSVulnerability(context.Background(), fleet.OSVulnerability{
|
|
OSID: 2,
|
|
CVE: "CVE-2020-1235",
|
|
}, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2}, // windows vuln
|
|
{CVE: "CVE-2020-1235", HostCount: 1}, // macos vuln
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// add software vuln to host 1
|
|
_, err = ds.UpdateHostSoftware(context.Background(), host1.ID, []fleet.Software{
|
|
{
|
|
Name: "Chrome",
|
|
Version: "1.0.0",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = ds.InsertSoftwareVulnerability(context.Background(), fleet.SoftwareVulnerability{
|
|
SoftwareID: 1,
|
|
CVE: "CVE-2020-1236",
|
|
}, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2}, // windows vuln
|
|
{CVE: "CVE-2020-1235", HostCount: 1}, // macos vuln
|
|
{CVE: "CVE-2020-1236", HostCount: 1}, // software vuln
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// move host 1 to team 1
|
|
team1, err := ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"})
|
|
require.NoError(t, err)
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team1.ID, []uint{host1.ID}))
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// global counts should not change
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// assert team 1 counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team1.ID)})
|
|
require.NoError(t, err)
|
|
team1expected := []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 1}, // windows vuln
|
|
{CVE: "CVE-2020-1236", HostCount: 1}, // software vuln
|
|
}
|
|
assertHostCounts(t, team1expected, list)
|
|
|
|
// assert no team (team 0) counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(0)})
|
|
require.NoError(t, err)
|
|
team0expected := []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 1}, // windows vuln
|
|
{CVE: "CVE-2020-1235", HostCount: 1}, // macos vuln
|
|
}
|
|
assertHostCounts(t, team0expected, list)
|
|
|
|
// add 5 macos hosts (4-9) to team2
|
|
team2, err := ds.NewTeam(context.Background(), &fleet.Team{Name: "team2"})
|
|
require.NoError(t, err)
|
|
for i := 4; i < 9; i++ {
|
|
host := test.NewHost(t, ds, fmt.Sprintf("host%d", i+4), fmt.Sprintf("192.168.0.%d", i+4), fmt.Sprintf("%d", i+4444), fmt.Sprintf("%d", i+4444), time.Now())
|
|
err = ds.UpdateHostOperatingSystem(context.Background(), host.ID, macOS)
|
|
require.NoError(t, err)
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team2.ID, []uint{host.ID}))
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// global counts should not change
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2},
|
|
{CVE: "CVE-2020-1235", HostCount: 6}, // + 5 macos hosts
|
|
{CVE: "CVE-2020-1236", HostCount: 1},
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// team1 counts should not change
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team1.ID)})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, team1expected, list)
|
|
|
|
// team0 counts should not change
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(0)})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, team0expected, list)
|
|
|
|
// team2 counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team2.ID)})
|
|
require.NoError(t, err)
|
|
team2expected := []hostCount{
|
|
{CVE: "CVE-2020-1235", HostCount: 5}, // macos vuln
|
|
}
|
|
assertHostCounts(t, team2expected, list)
|
|
|
|
// patch team2 hosts
|
|
macOSPatched := fleet.OperatingSystem{
|
|
Name: "macOS",
|
|
Version: "14.2",
|
|
Arch: "arm64",
|
|
Platform: "darwin",
|
|
}
|
|
for i := 4; i < 9; i++ {
|
|
err = ds.UpdateHostOperatingSystem(context.Background(), uint(i), macOSPatched) //nolint:gosec // dismiss G115
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// no change to team1 counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team1.ID)})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, team1expected, list)
|
|
|
|
// no change to team0 counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(0)})
|
|
require.NoError(t, err)
|
|
assertHostCounts(t, team0expected, list)
|
|
|
|
// no vulns in team2
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team2.ID)})
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 0)
|
|
|
|
// global counts reduced
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2},
|
|
{CVE: "CVE-2020-1235", HostCount: 1}, // -5 macos hosts
|
|
{CVE: "CVE-2020-1236", HostCount: 1},
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
|
|
// patch software vuln
|
|
_, err = ds.UpdateHostSoftware(context.Background(), host1.ID, []fleet.Software{})
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// global counts reduced
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
globalExpected = []hostCount{
|
|
{CVE: "CVE-2020-1234", HostCount: 2},
|
|
{CVE: "CVE-2020-1235", HostCount: 1},
|
|
// CVE-2020-1236 removed
|
|
}
|
|
assertHostCounts(t, globalExpected, list)
|
|
}
|
|
|
|
// testVulnerabilityHostCountBatchInserts tests the ability to insert a large
|
|
// number of vulnerabilities in a single batch insert
|
|
// to keep this test fast, we only insert 5 hosts
|
|
func testVulnerabilityHostCountBatchInserts(t *testing.T, ds *Datastore) {
|
|
// create 5 hosts
|
|
hosts := make([]*fleet.Host, 5)
|
|
for i := 0; i < 5; i++ {
|
|
hosts[i] = test.NewHost(t, ds, fmt.Sprintf("host%d", i), fmt.Sprintf("192.168.0.%d", i), fmt.Sprintf("%d", i+1000), fmt.Sprintf("%d", i+1000), time.Now())
|
|
}
|
|
|
|
// add 2 hosts to team 1
|
|
team1, err := ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"})
|
|
require.NoError(t, err)
|
|
|
|
for i := 0; i < 2; i++ {
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team1.ID, []uint{hosts[i].ID}))
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// create 200 OS vulns
|
|
osVulns := make([]fleet.OSVulnerability, 200)
|
|
for i := 0; i < 200; i++ {
|
|
osVulns[i] = fleet.OSVulnerability{
|
|
OSID: 1,
|
|
CVE: fmt.Sprintf("CVE-2020-%d", i),
|
|
}
|
|
}
|
|
|
|
// create 200 software vulns
|
|
softwareVulns := make([]fleet.SoftwareVulnerability, 200)
|
|
for i := 0; i < 200; i++ {
|
|
softwareVulns[i] = fleet.SoftwareVulnerability{
|
|
SoftwareID: 1,
|
|
CVE: fmt.Sprintf("CVE-2021-%d", i),
|
|
}
|
|
}
|
|
|
|
// insert OS vulns
|
|
_, err = ds.InsertOSVulnerabilities(context.Background(), osVulns, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
// insert software vulns
|
|
for _, vuln := range softwareVulns {
|
|
_, err = ds.InsertSoftwareVulnerability(context.Background(), vuln, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// update host OS
|
|
for i := 0; i < 5; i++ {
|
|
err = ds.UpdateHostOperatingSystem(context.Background(), hosts[i].ID, fleet.OperatingSystem{
|
|
Name: "Windows 11 Pro",
|
|
Version: "10.0.22000.3007",
|
|
Arch: "x86_64",
|
|
Platform: "windows",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// update host software
|
|
for i := 0; i < 5; i++ {
|
|
_, err = ds.UpdateHostSoftware(context.Background(), hosts[i].ID, []fleet.Software{
|
|
{
|
|
Name: "Chrome",
|
|
Version: "1.0.0",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// update host counts
|
|
err = ds.UpdateVulnerabilityHostCounts(context.Background(), 5)
|
|
require.NoError(t, err)
|
|
|
|
// assert host counts
|
|
list, _, err := ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{})
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 400)
|
|
for _, vuln := range list {
|
|
require.Equal(t, uint(5), vuln.HostsCount)
|
|
}
|
|
|
|
// assert team counts
|
|
list, _, err = ds.ListVulnerabilities(context.Background(), fleet.VulnListOptions{TeamID: ptr.Uint(team1.ID)})
|
|
require.NoError(t, err)
|
|
require.Len(t, list, 400)
|
|
for _, vuln := range list {
|
|
require.Equal(t, uint(2), vuln.HostsCount)
|
|
}
|
|
}
|
|
|
|
func testOSVersionsByCVEFailsGracefullyWithNoOSVersionRows(t *testing.T, ds *Datastore) {
|
|
osv, _, err := ds.OSVersionsByCVE(context.Background(), "CVE-2020-1238", nil)
|
|
require.NoError(t, err)
|
|
require.Nil(t, osv)
|
|
}
|
|
|
|
func testOSVersionsByCVE(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
// global
|
|
osv, _, err := ds.OSVersionsByCVE(context.Background(), "CVE-2020-1238", nil)
|
|
require.NoError(t, err)
|
|
|
|
expected := []fleet.VulnerableOS{
|
|
{
|
|
OSVersion: fleet.OSVersion{
|
|
Name: "Microsoft Windows 11 Enterprise 22H2 10.0.22621.2715",
|
|
NameOnly: "Microsoft Windows 11 Enterprise 22H2",
|
|
OSVersionID: 1,
|
|
Version: "10.0.22621.2715",
|
|
Platform: "windows",
|
|
HostsCount: 10,
|
|
},
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
}
|
|
|
|
require.Len(t, osv, 1)
|
|
require.Equal(t, osv[0].OSVersion, expected[0].OSVersion)
|
|
|
|
// team 1
|
|
expected[0].OSVersion.HostsCount = 4
|
|
osv, _, err = ds.OSVersionsByCVE(context.Background(), "CVE-2020-1238", ptr.Uint(1))
|
|
require.NoError(t, err)
|
|
require.Len(t, osv, 1)
|
|
require.Equal(t, osv[0].OSVersion, expected[0].OSVersion)
|
|
|
|
// team 2
|
|
expected[0].OSVersion.HostsCount = 3
|
|
osv, _, err = ds.OSVersionsByCVE(context.Background(), "CVE-2020-1238", ptr.Uint(2))
|
|
require.NoError(t, err)
|
|
require.Len(t, osv, 1)
|
|
require.Equal(t, osv[0].OSVersion, expected[0].OSVersion)
|
|
}
|
|
|
|
func testSoftwareByCVE(t *testing.T, ds *Datastore) {
|
|
seedVulnerabilities(t, ds)
|
|
|
|
// global
|
|
software, _, err := ds.SoftwareByCVE(context.Background(), "CVE-2020-1234", nil)
|
|
require.NoError(t, err)
|
|
|
|
expected := &fleet.VulnerableSoftware{
|
|
ID: 1,
|
|
Name: "Chrome",
|
|
Version: "1.0.0",
|
|
Source: "programs",
|
|
HostsCount: 6,
|
|
GenerateCPE: "cpe:2.3:a:google:chrome:1.0.0:*:*:*:*:*:*:*:*",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
}
|
|
|
|
require.Len(t, software, 1)
|
|
require.Equal(t, expected, software[0])
|
|
|
|
// team 1
|
|
expected.HostsCount = 4
|
|
software, _, err = ds.SoftwareByCVE(context.Background(), "CVE-2020-1234", ptr.Uint(1))
|
|
require.NoError(t, err)
|
|
require.Len(t, software, 1)
|
|
require.Equal(t, expected, software[0])
|
|
|
|
// team 2
|
|
expected.HostsCount = 1
|
|
software, _, err = ds.SoftwareByCVE(context.Background(), "CVE-2020-1234", ptr.Uint(2))
|
|
require.NoError(t, err)
|
|
require.Len(t, software, 1)
|
|
require.Equal(t, expected, software[0])
|
|
|
|
// team 0
|
|
expected.HostsCount = 1
|
|
software, _, err = ds.SoftwareByCVE(context.Background(), "CVE-2020-1234", ptr.Uint(0))
|
|
require.NoError(t, err)
|
|
require.Len(t, software, 1)
|
|
require.Equal(t, expected, software[0])
|
|
}
|
|
|
|
func assertHostCounts(t *testing.T, expected []hostCount, actual []fleet.VulnerabilityWithMetadata) {
|
|
t.Helper()
|
|
require.Len(t, actual, len(expected))
|
|
for _, vuln := range actual {
|
|
require.Contains(t, expected, hostCount{
|
|
CVE: vuln.CVE.CVE,
|
|
HostCount: vuln.HostsCount,
|
|
})
|
|
}
|
|
}
|
|
|
|
func seedVulnerabilities(t *testing.T, ds *Datastore) {
|
|
// insert 20 hosts
|
|
var hostids []uint
|
|
for i := 0; i < 20; i++ {
|
|
host := test.NewHost(t, ds, fmt.Sprintf("host%d", i), fmt.Sprintf("192.168.0.%d", i), fmt.Sprintf("%d", i+1000), fmt.Sprintf("%d", i+1000), time.Now())
|
|
hostids = append(hostids, host.ID)
|
|
}
|
|
|
|
// update 10 hosts to windows
|
|
for i := 0; i < 10; i++ {
|
|
arch := "arm64"
|
|
if i%2 == 0 {
|
|
arch = "x86_64"
|
|
}
|
|
err := ds.UpdateHostOperatingSystem(context.Background(), hostids[i], fleet.OperatingSystem{
|
|
Name: "Microsoft Windows 11 Enterprise 22H2",
|
|
Version: "10.0.22621.2715",
|
|
Arch: arch,
|
|
Platform: "windows",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// update 5 hosts to macOS
|
|
for i := 10; i < 15; i++ {
|
|
arch := "arm64"
|
|
if i%2 == 0 {
|
|
arch = "x86_64"
|
|
}
|
|
err := ds.UpdateHostOperatingSystem(context.Background(), hostids[i], fleet.OperatingSystem{
|
|
Name: "macOS",
|
|
Version: "14.1.2",
|
|
Arch: arch,
|
|
Platform: "darwin",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// move 4 windows hosts to team 1
|
|
team1, err := ds.NewTeam(context.Background(), &fleet.Team{Name: "team1"})
|
|
require.NoError(t, err)
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team1.ID, hostids[:4]))
|
|
require.NoError(t, err)
|
|
|
|
// move 3 windows hosts to team 2
|
|
team2, err := ds.NewTeam(context.Background(), &fleet.Team{Name: "team2"})
|
|
require.NoError(t, err)
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team2.ID, hostids[4:7]))
|
|
require.NoError(t, err)
|
|
|
|
// move 1 macOS host to team 2
|
|
err = ds.AddHostsToTeam(context.Background(), fleet.NewAddHostsToTeamParams(&team2.ID, []uint{hostids[10]}))
|
|
require.NoError(t, err)
|
|
|
|
err = ds.UpdateOSVersions(context.Background())
|
|
require.NoError(t, err)
|
|
|
|
// State:
|
|
// 10 global windows hosts
|
|
// 5 global macOS hosts
|
|
// 3 windows hosts in no team
|
|
// 4 windows hosts in team 1
|
|
// 3 windows hosts in team 2
|
|
// 4 macOS hosts in no team
|
|
// 1 macOS host in team 2
|
|
|
|
// add software to 6 windows hosts
|
|
// affects:
|
|
// 5 global windows hosts
|
|
// 4 windows hosts in team 1
|
|
// 1 windows host in team 2
|
|
// 1 host in no team
|
|
|
|
for i := 0; i < 5; i++ {
|
|
_, err = ds.UpdateHostSoftware(context.Background(), hostids[i], []fleet.Software{
|
|
{
|
|
Name: "Chrome",
|
|
Version: "1.0.0",
|
|
Source: "programs",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// add software to 1 windows host in no team
|
|
_, err = ds.UpdateHostSoftware(context.Background(), hostids[7], []fleet.Software{
|
|
{
|
|
Name: "Chrome",
|
|
Version: "1.0.0",
|
|
Source: "programs",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ds.UpsertSoftwareCPEs(context.Background(), []fleet.SoftwareCPE{
|
|
{
|
|
SoftwareID: 1,
|
|
CPE: "cpe:2.3:a:google:chrome:1.0.0:*:*:*:*:*:*:*:*",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = ds.SyncHostsSoftware(context.Background(), time.Now())
|
|
require.NoError(t, err)
|
|
|
|
softwareVulns := []fleet.SoftwareVulnerability{
|
|
{
|
|
SoftwareID: 1,
|
|
CVE: "CVE-2020-1234",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
{
|
|
SoftwareID: 1,
|
|
CVE: "CVE-2020-1235",
|
|
ResolvedInVersion: ptr.String("1.0.1"),
|
|
},
|
|
{
|
|
SoftwareID: 2,
|
|
CVE: "CVE-2020-1235", // overlaps software ID 1
|
|
},
|
|
{
|
|
SoftwareID: 2,
|
|
CVE: "CVE-2020-1236",
|
|
},
|
|
{
|
|
SoftwareID: 2,
|
|
CVE: "CVE-2020-1237",
|
|
},
|
|
{
|
|
SoftwareID: 2,
|
|
CVE: "CVE-2020-1238", // overlaps between software and OS
|
|
},
|
|
}
|
|
|
|
osVulns := []fleet.OSVulnerability{
|
|
{
|
|
OSID: 1, // windows x86_64
|
|
CVE: "CVE-2020-1238",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
{
|
|
OSID: 1,
|
|
CVE: "CVE-2020-1239",
|
|
ResolvedInVersion: ptr.String("1.0.1"),
|
|
},
|
|
{
|
|
OSID: 2, // windows arm64
|
|
CVE: "CVE-2020-1238",
|
|
ResolvedInVersion: ptr.String("1.0.0"),
|
|
},
|
|
{
|
|
OSID: 2,
|
|
CVE: "CVE-2020-1239",
|
|
ResolvedInVersion: ptr.String("1.0.1"),
|
|
},
|
|
{
|
|
OSID: 2, // macos x86_64
|
|
CVE: "CVE-2020-1240",
|
|
},
|
|
{
|
|
OSID: 2,
|
|
CVE: "CVE-2020-1241",
|
|
},
|
|
{
|
|
OSID: 3, // macos arm64
|
|
CVE: "CVE-2020-1240",
|
|
},
|
|
{
|
|
OSID: 3,
|
|
CVE: "CVE-2020-1241",
|
|
},
|
|
}
|
|
|
|
mockTime := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
cveMeta := []fleet.CVEMeta{
|
|
{
|
|
CVE: "CVE-2020-1234",
|
|
CVSSScore: ptr.Float64(7.5),
|
|
EPSSProbability: ptr.Float64(0.5),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime),
|
|
Description: "Test CVE 2020-1234",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1235",
|
|
CVSSScore: ptr.Float64(7.6),
|
|
EPSSProbability: ptr.Float64(0.51),
|
|
CISAKnownExploit: ptr.Bool(false),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 2)),
|
|
Description: "Test CVE 2020-1235",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1236",
|
|
CVSSScore: ptr.Float64(7.7),
|
|
EPSSProbability: ptr.Float64(0.52),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 1)),
|
|
Description: "Test CVE 2020-1236",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1237",
|
|
CVSSScore: ptr.Float64(7.8),
|
|
EPSSProbability: ptr.Float64(0.53),
|
|
CISAKnownExploit: ptr.Bool(false),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 3)),
|
|
Description: "Test CVE 2020-1237",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1238",
|
|
CVSSScore: ptr.Float64(7.9),
|
|
EPSSProbability: ptr.Float64(0.54),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 4)),
|
|
Description: "Test CVE 2020-1238",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1239",
|
|
CVSSScore: ptr.Float64(8.0),
|
|
EPSSProbability: ptr.Float64(0.55),
|
|
CISAKnownExploit: ptr.Bool(false),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 5)),
|
|
Description: "Test CVE 2020-1239",
|
|
},
|
|
{
|
|
CVE: "CVE-2020-1240",
|
|
CVSSScore: ptr.Float64(8.1),
|
|
EPSSProbability: ptr.Float64(0.56),
|
|
CISAKnownExploit: ptr.Bool(true),
|
|
Published: ptr.Time(mockTime.Add(time.Hour * 6)),
|
|
Description: "Test CVE 2020-1240",
|
|
},
|
|
// CVE-2020-1241 ommited to test null values
|
|
}
|
|
|
|
vulnHostCount := []struct {
|
|
cve string
|
|
teamID uint
|
|
globalstats bool
|
|
hostCount int
|
|
}{
|
|
{
|
|
cve: "CVE-2020-1234",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 100,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1234",
|
|
teamID: 1,
|
|
hostCount: 20,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1234",
|
|
teamID: 0,
|
|
hostCount: 80,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1235",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 90,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1235",
|
|
teamID: 1,
|
|
hostCount: 19,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1235",
|
|
teamID: 0,
|
|
hostCount: 71,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1236",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 80,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1236",
|
|
teamID: 1,
|
|
hostCount: 18,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1236",
|
|
teamID: 0,
|
|
hostCount: 62,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1237",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 70,
|
|
},
|
|
// no team 1 host count for CVE-2020-1237
|
|
{
|
|
cve: "CVE-2020-1238",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 60,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1238",
|
|
teamID: 1,
|
|
hostCount: 16,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1238",
|
|
teamID: 0,
|
|
hostCount: 44,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1239",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 50,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1239",
|
|
teamID: 1,
|
|
hostCount: 15,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1239",
|
|
teamID: 0,
|
|
hostCount: 35,
|
|
},
|
|
// no host counts for CVE-2020-1240
|
|
{
|
|
cve: "CVE-2020-1241",
|
|
teamID: 0,
|
|
globalstats: true,
|
|
hostCount: 40,
|
|
},
|
|
{
|
|
cve: "CVE-2020-1241",
|
|
teamID: 1,
|
|
hostCount: 14,
|
|
},
|
|
{
|
|
// no team
|
|
cve: "CVE-2020-1241",
|
|
teamID: 0,
|
|
hostCount: 26,
|
|
},
|
|
}
|
|
|
|
// Insert OS Vuln
|
|
_, err = ds.InsertOSVulnerabilities(context.Background(), osVulns, fleet.NVDSource)
|
|
require.NoError(t, err)
|
|
|
|
// Insert Software Vuln
|
|
for _, vuln := range softwareVulns {
|
|
_, err = ds.InsertSoftwareVulnerability(context.Background(), vuln, fleet.CustomSource)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Insert CVEMeta
|
|
err = ds.InsertCVEMeta(context.Background(), cveMeta)
|
|
require.NoError(t, err)
|
|
|
|
// Insert Host Count
|
|
insertStmt := `
|
|
INSERT INTO vulnerability_host_counts (cve, team_id, host_count, global_stats)
|
|
VALUES (?, ?, ?, ?)
|
|
`
|
|
for _, vuln := range vulnHostCount {
|
|
_, err = ds.writer(context.Background()).Exec(insertStmt, vuln.cve, vuln.teamID, vuln.hostCount, vuln.globalstats)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|