From ecb8ee19e2cf1ce4c9ccfa27a644ee8c13e93d77 Mon Sep 17 00:00:00 2001 From: Victor Lyuboslavsky <2685025+getvictor@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:17:23 -0500 Subject: [PATCH] Fixed bad software ingestion debug message and added filter for invalid software with missing names. (#33682) **Related issue:** Resolves #33681 # Checklist for submitter - [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 --- changes/33681-software-ingestion-dbg-msg | 1 + server/datastore/mysql/software.go | 26 +++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 changes/33681-software-ingestion-dbg-msg diff --git a/changes/33681-software-ingestion-dbg-msg b/changes/33681-software-ingestion-dbg-msg new file mode 100644 index 0000000000..3a7f3095e6 --- /dev/null +++ b/changes/33681-software-ingestion-dbg-msg @@ -0,0 +1 @@ +Fixed bad software ingestion debug message and added filter for invalid software with missing names. diff --git a/server/datastore/mysql/software.go b/server/datastore/mysql/software.go index f20dcaeec3..86ed008726 100644 --- a/server/datastore/mysql/software.go +++ b/server/datastore/mysql/software.go @@ -363,6 +363,19 @@ WHERE return softwares, nil } +// filterSoftwareWithEmptyNames removes software entries with empty names in-place. +// This is a well-known Go idiom: https://go.dev/wiki/SliceTricks#filter-in-place +func filterSoftwareWithEmptyNames(software []fleet.Software) []fleet.Software { + n := 0 + for _, sw := range software { + if sw.Name != "" { + software[n] = sw + n++ + } + } + return software[:n] +} + // applyChangesForNewSoftwareDB returns the current host software and the applied mutations: what // was inserted and what was deleted func (ds *Datastore) applyChangesForNewSoftwareDB( @@ -372,6 +385,9 @@ func (ds *Datastore) applyChangesForNewSoftwareDB( ) (*fleet.UpdateHostSoftwareDBResult, error) { r := &fleet.UpdateHostSoftwareDBResult{} + // We want to make sure we have valid data before proceeding. We've seen Windows programs with empty names. + software = filterSoftwareWithEmptyNames(software) + // This code executes once an hour for each host, so we should optimize for MySQL master (writer) DB performance. // We use a slave (reader) DB to avoid accessing the master. If nothing has changed, we avoid all access to the master. // It is possible that the software list is out of sync between the slave and the master. This is unlikely because @@ -710,11 +726,15 @@ func (ds *Datastore) getIncomingSoftwareChecksumsToExistingTitles( existingChecksums := uniqueTitleStrToChecksums[titleStr] if len(existingChecksums) > 0 { // Log when multiple checksums map to the same title. + existingChecksumsHex := make([]string, len(existingChecksums)) + for i, cs := range existingChecksums { + existingChecksumsHex[i] = fmt.Sprintf("%x", cs) + } level.Debug(ds.logger).Log( "msg", "multiple checksums mapping to same title", "title_str", titleStr, - "new_checksum", checksum, - "existing_checksums", fmt.Sprintf("%v", existingChecksums), + "new_checksum", fmt.Sprintf("%x", checksum), + "existing_checksums", fmt.Sprintf("%v", existingChecksumsHex), "software_name", sw.Name, "software_version", sw.Version, ) @@ -1162,7 +1182,7 @@ func (ds *Datastore) linkSoftwareToHost( // Log missing software but continue level.Warn(ds.logger).Log( "msg", "software not found after pre-insertion", - "checksum", checksum, + "checksum", fmt.Sprintf("%x", checksum), "name", sw.Name, "version", sw.Version, )