Remove unused windows_updates MySQL table and ingestion (#44128)
**Related issue:** Resolves #44127 - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. ## Testing - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Removed the unused Windows Updates feature: ingestion, parsing, persistence APIs, and detail query; added a migration to drop the related database table. * **Tests** * Removed unit and integration tests for Windows update parsing, ingestion, persistence, and query inclusion. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1 @@
|
||||
* Removed unused `windows_updates` MySQL table and ingestion code.
|
||||
@@ -1304,20 +1304,6 @@ WITH cached_groups AS (select * from groups)
|
||||
SELECT uid, username, email FROM users
|
||||
```
|
||||
|
||||
## windows_update_history
|
||||
|
||||
- Platforms: windows
|
||||
|
||||
- Discovery query:
|
||||
```sql
|
||||
SELECT 1 FROM osquery_registry WHERE active = true AND registry = 'table' AND name = 'windows_update_history'
|
||||
```
|
||||
|
||||
- Query:
|
||||
```sql
|
||||
SELECT date, title FROM windows_update_history WHERE result_code = 'Succeeded'
|
||||
```
|
||||
|
||||
<br /><br />[^1]: Software override queries write over the default queries. They are used to populate the software inventory.
|
||||
<meta name="navSection" value="Dig deeper">
|
||||
<meta name="pageOrderInSection" value="1600">
|
||||
@@ -583,7 +583,6 @@ var hostRefs = []string{
|
||||
"host_orbit_info",
|
||||
"host_munki_issues",
|
||||
"host_display_names",
|
||||
"windows_updates",
|
||||
"host_disks",
|
||||
"host_updates",
|
||||
"host_disk_encryption_keys",
|
||||
|
||||
@@ -8985,10 +8985,6 @@ func testHostsDeleteHosts(t *testing.T, ds *Datastore) {
|
||||
// Update host_operating_system
|
||||
err = ds.UpdateHostOperatingSystem(context.Background(), host.ID, fleet.OperatingSystem{Name: "foo", Version: "bar"})
|
||||
require.NoError(t, err)
|
||||
// Insert a windows update for the host
|
||||
stmt := `INSERT INTO windows_updates (host_id, date_epoch, kb_id) VALUES (?, ?, ?)`
|
||||
_, err = ds.writer(context.Background()).Exec(stmt, host.ID, 1, 123)
|
||||
require.NoError(t, err)
|
||||
// set host' disk space
|
||||
err = ds.SetOrUpdateHostDisksSpace(context.Background(), host.ID, 12, 25, 40.0, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package tables
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
MigrationClient.AddMigration(Up_20260423161824, Down_20260423161824)
|
||||
}
|
||||
|
||||
func Up_20260423161824(tx *sql.Tx) error {
|
||||
if _, err := tx.Exec(`DROP TABLE IF EXISTS windows_updates`); err != nil {
|
||||
return fmt.Errorf("drop windows_updates table: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Down_20260423161824(tx *sql.Tx) error {
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,74 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
func (ds *Datastore) ListWindowsUpdatesByHostID(
|
||||
ctx context.Context,
|
||||
hostID uint,
|
||||
) ([]fleet.WindowsUpdate, error) {
|
||||
stmt := `
|
||||
SELECT kb_id, date_epoch
|
||||
FROM windows_updates wu
|
||||
WHERE host_id = ?
|
||||
ORDER BY date_epoch
|
||||
`
|
||||
updates := []fleet.WindowsUpdate{}
|
||||
|
||||
if err := sqlx.SelectContext(ctx, ds.reader(ctx), &updates, stmt, hostID); err != nil {
|
||||
return nil, ctxerr.Wrap(ctx, err, "list windows updates")
|
||||
}
|
||||
|
||||
return updates, nil
|
||||
}
|
||||
|
||||
// InsertWindowsUpdates inserts one or more windows updates for the given host.
|
||||
func (ds *Datastore) InsertWindowsUpdates(ctx context.Context, hostID uint, updates []fleet.WindowsUpdate) error {
|
||||
if len(updates) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// The windows_updates_history table in OSQUERY is append only so we only need to figure what
|
||||
// new updates were installed since the last sync.
|
||||
|
||||
var lastUpdateEpoch uint
|
||||
var args []interface{}
|
||||
var placeholders []string
|
||||
|
||||
lastUpdateSmt := `SELECT date_epoch FROM windows_updates WHERE host_id = ? ORDER BY date_epoch DESC LIMIT 1`
|
||||
if err := sqlx.GetContext(ctx, ds.reader(ctx), &lastUpdateEpoch, lastUpdateSmt, hostID); err != nil {
|
||||
if err != sql.ErrNoRows {
|
||||
return ctxerr.Wrap(ctx, err, "inserting windows updates")
|
||||
}
|
||||
lastUpdateEpoch = 0
|
||||
}
|
||||
|
||||
for _, v := range updates {
|
||||
if v.DateEpoch > lastUpdateEpoch {
|
||||
placeholders = append(placeholders, "(?,?,?)")
|
||||
args = append(args, hostID, v.DateEpoch, v.KBID)
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
smt := fmt.Sprintf(
|
||||
`INSERT IGNORE INTO windows_updates (host_id, date_epoch, kb_id) VALUES %s`,
|
||||
strings.Join(placeholders, ","),
|
||||
)
|
||||
|
||||
if _, err := ds.writer(ctx).ExecContext(ctx, smt, args...); err != nil {
|
||||
return ctxerr.Wrap(ctx, err, "inserting windows updates")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/server/fleet"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWindowsUpdates(t *testing.T) {
|
||||
ds := CreateMySQLDS(t)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
fn func(t *testing.T, ds *Datastore)
|
||||
}{
|
||||
{"InsertWindowsUpdates", testInsertWindowsUpdates},
|
||||
{"ListWindowsUpdatesByHostID", testListWindowsUpdatesByHostID},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
defer TruncateTables(t, ds)
|
||||
c.fn(t, ds)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testListWindowsUpdatesByHostID(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
now := uint(time.Now().Unix()) //nolint:gosec // dismiss G115
|
||||
|
||||
t.Run("with no stored updates", func(t *testing.T) {
|
||||
actual, err := ds.ListWindowsUpdatesByHostID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, actual)
|
||||
})
|
||||
|
||||
t.Run("none matching", func(t *testing.T) {
|
||||
updates := []fleet.WindowsUpdate{
|
||||
{KBID: 1, DateEpoch: now},
|
||||
{KBID: 2, DateEpoch: now + 1},
|
||||
}
|
||||
|
||||
err := ds.InsertWindowsUpdates(ctx, 1, updates)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := ds.ListWindowsUpdatesByHostID(ctx, 2)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, actual)
|
||||
})
|
||||
|
||||
t.Run("returns matching", func(t *testing.T) {
|
||||
expected := []fleet.WindowsUpdate{
|
||||
{KBID: 1, DateEpoch: now},
|
||||
{KBID: 2, DateEpoch: now + 1},
|
||||
}
|
||||
|
||||
err := ds.InsertWindowsUpdates(ctx, 1, expected)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := ds.ListWindowsUpdatesByHostID(ctx, 1)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, expected, actual)
|
||||
})
|
||||
}
|
||||
|
||||
func testInsertWindowsUpdates(t *testing.T, ds *Datastore) {
|
||||
ctx := context.Background()
|
||||
now := uint(time.Now().Unix()) //nolint:gosec // dismiss G115
|
||||
smt := `SELECT kb_id, date_epoch FROM windows_updates WHERE host_id = ?`
|
||||
|
||||
t.Run("with no stored updates", func(t *testing.T) {
|
||||
hostID := 1
|
||||
|
||||
updates := []fleet.WindowsUpdate{
|
||||
{KBID: 1, DateEpoch: now},
|
||||
{KBID: 2, DateEpoch: now + 1},
|
||||
}
|
||||
|
||||
err := ds.InsertWindowsUpdates(ctx, 1, updates)
|
||||
require.NoError(t, err)
|
||||
|
||||
var actual []fleet.WindowsUpdate
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &actual, smt, hostID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.ElementsMatch(t, updates, actual)
|
||||
})
|
||||
|
||||
t.Run("with stored updates", func(t *testing.T) {
|
||||
hostID := 1
|
||||
updates := []fleet.WindowsUpdate{
|
||||
{KBID: 1, DateEpoch: now},
|
||||
{KBID: 2, DateEpoch: now + 1},
|
||||
}
|
||||
|
||||
err := ds.InsertWindowsUpdates(ctx, 1, updates)
|
||||
require.NoError(t, err)
|
||||
|
||||
updates = append(updates, fleet.WindowsUpdate{KBID: 3, DateEpoch: now + 2})
|
||||
err = ds.InsertWindowsUpdates(ctx, 1, updates)
|
||||
require.NoError(t, err)
|
||||
|
||||
var actual []fleet.WindowsUpdate
|
||||
err = sqlx.SelectContext(ctx, ds.reader(ctx), &actual, smt, hostID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.ElementsMatch(t, updates, actual)
|
||||
})
|
||||
}
|
||||
@@ -1251,10 +1251,6 @@ type Datastore interface {
|
||||
InnoDBStatus(ctx context.Context) (string, error)
|
||||
ProcessList(ctx context.Context) ([]MySQLProcess, error)
|
||||
|
||||
// WindowsUpdates Store
|
||||
ListWindowsUpdatesByHostID(ctx context.Context, hostID uint) ([]WindowsUpdate, error)
|
||||
InsertWindowsUpdates(ctx context.Context, hostID uint, updates []WindowsUpdate) error
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// OperatingSystemVulnerabilities Store
|
||||
ListOSVulnerabilitiesByOS(ctx context.Context, osID uint) ([]OSVulnerability, error)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type WindowsUpdate struct {
|
||||
KBID uint `db:"kb_id"`
|
||||
DateEpoch uint `db:"date_epoch"`
|
||||
}
|
||||
|
||||
// NewWindowsUpdate returns a new WindowsUpdate from the provided props:
|
||||
// - title: The title of the windows update (see
|
||||
// https://osquery.io/schema/5.4.0/#windows_update_history)
|
||||
// - dateEpoch: The date the update was applied on (see
|
||||
// https://osquery.io/schema/5.4.0/#windows_update_history)
|
||||
func NewWindowsUpdate(title string, dateEpoch string) (WindowsUpdate, error) {
|
||||
kbID, err := parseKBID(title)
|
||||
if err != nil {
|
||||
return WindowsUpdate{}, err
|
||||
}
|
||||
|
||||
dEpoch, err := parseDateEpoch(dateEpoch)
|
||||
if err != nil {
|
||||
return WindowsUpdate{}, err
|
||||
}
|
||||
|
||||
return WindowsUpdate{
|
||||
KBID: kbID,
|
||||
DateEpoch: dEpoch,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (wu WindowsUpdate) MoreRecent(other WindowsUpdate) bool {
|
||||
return wu.DateEpoch > other.DateEpoch
|
||||
}
|
||||
|
||||
func parseDateEpoch(val string) (uint, error) {
|
||||
dEpoch, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if dEpoch < 0 {
|
||||
return 0, fmt.Errorf("invalid epoch value %d", dEpoch)
|
||||
}
|
||||
|
||||
return uint(dEpoch), nil
|
||||
}
|
||||
|
||||
// parseKBID extracts the KB (Knowledge Base) id contained inside a string. KB ids are found based on
|
||||
// the pattern 'KB\d+'. In case of multiple matches, the id
|
||||
// will be based on the last match. Will return an error if:
|
||||
// - No matches are found
|
||||
// - The matched KB contains an 'invalid' id (< 0)
|
||||
func parseKBID(str string) (uint, error) {
|
||||
r := regexp.MustCompile(`\s?\(?KB(?P<Id>\d+)\s?\)?`)
|
||||
m := r.FindAllStringSubmatch(str, -1)
|
||||
idx := r.SubexpIndex("Id")
|
||||
|
||||
if len(m) == 0 || idx <= 0 {
|
||||
return 0, fmt.Errorf("KB id not found in %s", str)
|
||||
}
|
||||
|
||||
last := m[len(m)-1]
|
||||
id, err := strconv.Atoi(last[idx])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if id <= 0 {
|
||||
return 0, fmt.Errorf("Invalid KB id value found in %s", str)
|
||||
}
|
||||
|
||||
return uint(id), nil
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseKBId(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected uint
|
||||
errors bool
|
||||
}{
|
||||
{
|
||||
input: "2022-04 Update for Windows 10 Version 21H2 for x64-based Systems based on (KB2267602) based on KB2267601 (KB5005463)",
|
||||
expected: 5005463,
|
||||
errors: false,
|
||||
},
|
||||
{
|
||||
input: "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.1239.0)",
|
||||
expected: 2267602,
|
||||
errors: false,
|
||||
},
|
||||
{
|
||||
input: "2022-04 Update for Windows 10 Version 21H2 for x64-based Systems (KB5005463)",
|
||||
expected: 5005463,
|
||||
errors: false,
|
||||
},
|
||||
{
|
||||
input: "2022-04 Update for Windows 10 Version 21H2 for x64-based Systems (KB-5005463)",
|
||||
expected: 0,
|
||||
errors: true,
|
||||
},
|
||||
{
|
||||
input: "2022-04 Update for Windows 10 Version 21H2 for x64-based Systems (KB0)",
|
||||
expected: 0,
|
||||
errors: true,
|
||||
},
|
||||
{
|
||||
input: "Some random string",
|
||||
expected: 0,
|
||||
errors: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tCase := range testCases {
|
||||
actual, err := parseKBID(tCase.input)
|
||||
require.Equal(t, tCase.expected, actual)
|
||||
|
||||
if !tCase.errors {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -887,10 +887,6 @@ type InnoDBStatusFunc func(ctx context.Context) (string, error)
|
||||
|
||||
type ProcessListFunc func(ctx context.Context) ([]fleet.MySQLProcess, error)
|
||||
|
||||
type ListWindowsUpdatesByHostIDFunc func(ctx context.Context, hostID uint) ([]fleet.WindowsUpdate, error)
|
||||
|
||||
type InsertWindowsUpdatesFunc func(ctx context.Context, hostID uint, updates []fleet.WindowsUpdate) error
|
||||
|
||||
type ListOSVulnerabilitiesByOSFunc func(ctx context.Context, osID uint) ([]fleet.OSVulnerability, error)
|
||||
|
||||
type ListVulnsByOsNameAndVersionFunc func(ctx context.Context, name string, version string, includeCVSS bool, teamID *uint, maxVulnerabilities *int) (fleet.OSVulnerabilitiesWithCount, error)
|
||||
@@ -3178,12 +3174,6 @@ type DataStore struct {
|
||||
ProcessListFunc ProcessListFunc
|
||||
ProcessListFuncInvoked bool
|
||||
|
||||
ListWindowsUpdatesByHostIDFunc ListWindowsUpdatesByHostIDFunc
|
||||
ListWindowsUpdatesByHostIDFuncInvoked bool
|
||||
|
||||
InsertWindowsUpdatesFunc InsertWindowsUpdatesFunc
|
||||
InsertWindowsUpdatesFuncInvoked bool
|
||||
|
||||
ListOSVulnerabilitiesByOSFunc ListOSVulnerabilitiesByOSFunc
|
||||
ListOSVulnerabilitiesByOSFuncInvoked bool
|
||||
|
||||
@@ -7696,20 +7686,6 @@ func (s *DataStore) ProcessList(ctx context.Context) ([]fleet.MySQLProcess, erro
|
||||
return s.ProcessListFunc(ctx)
|
||||
}
|
||||
|
||||
func (s *DataStore) ListWindowsUpdatesByHostID(ctx context.Context, hostID uint) ([]fleet.WindowsUpdate, error) {
|
||||
s.mu.Lock()
|
||||
s.ListWindowsUpdatesByHostIDFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.ListWindowsUpdatesByHostIDFunc(ctx, hostID)
|
||||
}
|
||||
|
||||
func (s *DataStore) InsertWindowsUpdates(ctx context.Context, hostID uint, updates []fleet.WindowsUpdate) error {
|
||||
s.mu.Lock()
|
||||
s.InsertWindowsUpdatesFuncInvoked = true
|
||||
s.mu.Unlock()
|
||||
return s.InsertWindowsUpdatesFunc(ctx, hostID, updates)
|
||||
}
|
||||
|
||||
func (s *DataStore) ListOSVulnerabilitiesByOS(ctx context.Context, osID uint) ([]fleet.OSVulnerability, error) {
|
||||
s.mu.Lock()
|
||||
s.ListOSVulnerabilitiesByOSFuncInvoked = true
|
||||
|
||||
@@ -1189,7 +1189,6 @@ func verifyDiscovery(t *testing.T, queries, discovery map[string]string) {
|
||||
hostDetailQueryPrefix + "google_chrome_profiles": {},
|
||||
hostDetailQueryPrefix + "mdm": {},
|
||||
hostDetailQueryPrefix + "munki_info": {},
|
||||
hostDetailQueryPrefix + "windows_update_history": {},
|
||||
hostDetailQueryPrefix + "kubequery_info": {},
|
||||
hostDetailQueryPrefix + "orbit_info": {},
|
||||
hostDetailQueryPrefix + "software_vscode_extensions": {},
|
||||
@@ -1715,8 +1714,8 @@ func TestDetailQueriesWithEmptyStrings(t *testing.T) {
|
||||
// queries)
|
||||
queries, discovery, acc, err := svc.GetDistributedQueries(ctx)
|
||||
require.NoError(t, err)
|
||||
// +1 due to 'windows_update_history', +1 due to fleet_no_policies_wildcard query.
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Equal(t, len(expected)+1+1, len(queries)) {
|
||||
// +1 due to fleet_no_policies_wildcard query.
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Len(t, expected, len(queries)-1) {
|
||||
// this is just to print the diff between the expected and actual query
|
||||
// keys when the count assertion fails, to help debugging - they are not
|
||||
// expected to match.
|
||||
@@ -1952,7 +1951,7 @@ func TestDetailQueries(t *testing.T) {
|
||||
queries, discovery, acc, err := svc.GetDistributedQueries(ctx)
|
||||
require.NoError(t, err)
|
||||
// +1 for fleet_no_policies_wildcard
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Equal(t, len(expected)+1, len(queries)) {
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Len(t, expected, len(queries)-1) {
|
||||
// this is just to print the diff between the expected and actual query
|
||||
// keys when the count assertion fails, to help debugging - they are not
|
||||
// expected to match.
|
||||
@@ -2493,8 +2492,8 @@ func TestDistributedQueryResults(t *testing.T) {
|
||||
// Now we should get the active distributed query
|
||||
queries, discovery, acc, err := svc.GetDistributedQueries(hostCtx)
|
||||
require.NoError(t, err)
|
||||
// +1 for the distributed query for campaign ID 42, +1 for windows update history, +1 for the fleet_no_policies_wildcard query.
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Equal(t, len(expected)+3, len(queries)) {
|
||||
// +1 for the distributed query for campaign ID 42, +1 for the fleet_no_policies_wildcard query.
|
||||
if expected := expectedDetailQueriesForPlatform(host.Platform); !assert.Len(t, expected, len(queries)-2) {
|
||||
// this is just to print the diff between the expected and actual query
|
||||
// keys when the count assertion fails, to help debugging - they are not
|
||||
// expected to match.
|
||||
|
||||
@@ -988,13 +988,6 @@ func withCachedUsers(query string) string {
|
||||
return fmt.Sprintf(query, usersQueryStr)
|
||||
}
|
||||
|
||||
var windowsUpdateHistory = DetailQuery{
|
||||
Query: `SELECT date, title FROM windows_update_history WHERE result_code = 'Succeeded'`,
|
||||
Platforms: []string{"windows"},
|
||||
Discovery: discoveryTable("windows_update_history"),
|
||||
DirectIngestFunc: directIngestWindowsUpdateHistory,
|
||||
}
|
||||
|
||||
// macOSEntraIDDetails holds the query and ingestion function for macOS for Microsoft "Conditional access" feature.
|
||||
var macOSEntraIDDetails = DetailQuery{
|
||||
// The query ingests Entra's Device ID and User Principal Name of the account
|
||||
@@ -2079,42 +2072,6 @@ func generateBatteryHealth(ctx context.Context, row map[string]string, logger *s
|
||||
return batteryStatusGood, count, nil
|
||||
}
|
||||
|
||||
func directIngestWindowsUpdateHistory(
|
||||
ctx context.Context,
|
||||
logger *slog.Logger,
|
||||
host *fleet.Host,
|
||||
ds fleet.Datastore,
|
||||
rows []map[string]string,
|
||||
) error {
|
||||
// The windows update history table will also contain entries for the Defender Antivirus. Unfortunately
|
||||
// there's no reliable way to differentiate between those entries and Cumulative OS updates.
|
||||
// Since each antivirus update will have the same KB ID, but different 'dates', to
|
||||
// avoid trying to insert duplicated data, we group by KB ID and then take the most 'out of
|
||||
// date' update in each group.
|
||||
|
||||
uniq := make(map[uint]fleet.WindowsUpdate)
|
||||
for _, row := range rows {
|
||||
u, err := fleet.NewWindowsUpdate(row["title"], row["date"])
|
||||
if err != nil {
|
||||
// If the update failed to parse then we log a debug error and ignore it.
|
||||
// E.g. we've seen KB updates with titles like "Logitech - Image - 1.4.40.0".
|
||||
logger.DebugContext(ctx, "directIngestWindowsUpdateHistory skipped", "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if v, ok := uniq[u.KBID]; !ok || v.MoreRecent(u) {
|
||||
uniq[u.KBID] = u
|
||||
}
|
||||
}
|
||||
|
||||
var updates []fleet.WindowsUpdate
|
||||
for _, v := range uniq {
|
||||
updates = append(updates, v)
|
||||
}
|
||||
|
||||
return ds.InsertWindowsUpdates(ctx, host.ID, updates)
|
||||
}
|
||||
|
||||
func directIngestEntraIDDetails(
|
||||
ctx context.Context,
|
||||
logger *slog.Logger,
|
||||
@@ -3378,10 +3335,6 @@ func GetDetailQueries(
|
||||
generatedMap["users_chrome"] = usersQueryChrome
|
||||
}
|
||||
|
||||
if !fleetConfig.Vulnerabilities.DisableWinOSVulnerabilities {
|
||||
generatedMap["windows_update_history"] = windowsUpdateHistory
|
||||
}
|
||||
|
||||
if fleetConfig.App.EnableScheduledQueryStats {
|
||||
generatedMap["scheduled_query_stats"] = scheduledQueryStats
|
||||
}
|
||||
|
||||
@@ -505,7 +505,6 @@ func TestGetDetailQueries(t *testing.T) {
|
||||
"os_windows",
|
||||
"os_unix_like",
|
||||
"os_chrome",
|
||||
"windows_update_history",
|
||||
"kubequery_info",
|
||||
"orbit_info",
|
||||
"disk_encryption_darwin",
|
||||
@@ -519,9 +518,6 @@ func TestGetDetailQueries(t *testing.T) {
|
||||
require.Len(t, queriesNoConfig, len(baseQueries))
|
||||
sortedKeysCompare(t, queriesNoConfig, baseQueries)
|
||||
|
||||
queriesWithoutWinOSVuln := GetDetailQueries(t.Context(), config.FleetConfig{Vulnerabilities: config.VulnerabilitiesConfig{DisableWinOSVulnerabilities: true}}, nil, nil, Integrations{}, nil)
|
||||
require.Len(t, queriesWithoutWinOSVuln, 29)
|
||||
|
||||
queriesWithUsers := GetDetailQueries(t.Context(), config.FleetConfig{App: config.AppConfig{EnableScheduledQueryStats: true}}, nil, &fleet.Features{EnableHostUsers: true}, Integrations{}, nil)
|
||||
qs := baseQueries
|
||||
qs = append(qs, "users", "users_chrome", "scheduled_query_stats")
|
||||
@@ -1922,45 +1918,6 @@ func TestDirectIngestSoftware(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestDirectIngestWindowsUpdateHistory(t *testing.T) {
|
||||
ds := new(mock.Store)
|
||||
ds.InsertWindowsUpdatesFunc = func(ctx context.Context, hostID uint, updates []fleet.WindowsUpdate) error {
|
||||
require.Len(t, updates, 6)
|
||||
require.ElementsMatch(t, []fleet.WindowsUpdate{
|
||||
{KBID: 2267602, DateEpoch: 1657929207},
|
||||
{KBID: 890830, DateEpoch: 1658226954},
|
||||
{KBID: 5013887, DateEpoch: 1658225364},
|
||||
{KBID: 5005463, DateEpoch: 1658225225},
|
||||
{KBID: 5010472, DateEpoch: 1658224963},
|
||||
{KBID: 4052623, DateEpoch: 1657929544},
|
||||
}, updates)
|
||||
return nil
|
||||
}
|
||||
|
||||
host := fleet.Host{
|
||||
ID: 1,
|
||||
}
|
||||
|
||||
payload := []map[string]string{
|
||||
{"date": "1659392951", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.1239.0)"},
|
||||
{"date": "1658271402", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.442.0)"},
|
||||
{"date": "1658228495", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.415.0)"},
|
||||
{"date": "1658226954", "title": "Windows Malicious Software Removal Tool x64 - v5.103 (KB890830)"},
|
||||
{"date": "1658225364", "title": "2022-06 Cumulative Update for .NET Framework 3.5 and 4.8 for Windows 10 Version 21H2 for x64 (KB5013887)"},
|
||||
{"date": "1658225225", "title": "2022-04 Update for Windows 10 Version 21H2 for x64-based Systems (KB5005463)"},
|
||||
{"date": "1658224963", "title": "2022-02 Cumulative Update Preview for .NET Framework 3.5 and 4.8 for Windows 10 Version 21H2 for x64 (KB5010472)"},
|
||||
{"date": "1658222131", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.400.0)"},
|
||||
{"date": "1658189063", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.376.0)"},
|
||||
{"date": "1658185542", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.386.0)"},
|
||||
{"date": "1657929544", "title": "Update for Microsoft Defender Antivirus antimalware platform - KB4052623 (Version 4.18.2205.7)"},
|
||||
{"date": "1657929207", "title": "Security Intelligence Update for Microsoft Defender Antivirus - KB2267602 (Version 1.371.203.0)"},
|
||||
}
|
||||
|
||||
err := directIngestWindowsUpdateHistory(t.Context(), slog.New(slog.DiscardHandler), &host, ds, payload)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ds.InsertWindowsUpdatesFuncInvoked)
|
||||
}
|
||||
|
||||
func TestIngestKubequeryInfo(t *testing.T) {
|
||||
err := ingestKubequeryInfo(t.Context(), slog.New(slog.DiscardHandler), &fleet.Host{}, nil)
|
||||
require.Error(t, err)
|
||||
|
||||
Reference in New Issue
Block a user