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, )