diff --git a/changes/15855-vm-software b/changes/15855-vm-software new file mode 100644 index 0000000000..7cb935cfe8 --- /dev/null +++ b/changes/15855-vm-software @@ -0,0 +1,2 @@ +- Fixes issue where software from a Parallels VM on a MacOS host would show up in Fleet as if it + were the host's software. \ No newline at end of file diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go index c63a1d5528..98550f36a0 100644 --- a/server/service/osquery_utils/queries.go +++ b/server/service/osquery_utils/queries.go @@ -1267,6 +1267,10 @@ func directIngestSoftware(ctx context.Context, logger log.Logger, host *fleet.Ho sanitizeSoftware(host, s, logger) + if shouldRemoveSoftware(host, s) { + continue + } + software = append(software, *s) installedPath := strings.TrimSpace(row["installed_path"]) @@ -1394,6 +1398,16 @@ func sanitizeSoftware(h *fleet.Host, s *fleet.Software, logger log.Logger) { } } +// shouldRemoveSoftware returns whether or not we should remove the given Software item from this +// host's software list. +func shouldRemoveSoftware(h *fleet.Host, s *fleet.Software) bool { + // Parallels is a common VM software for MacOS. Parallels makes the VM's applications + // visible in the host as MacOS applications, which leads to confusing output (e.g. a MacOS + // host reporting that it has Notepad installed when this is just an app from the Windows VM + // under Parallels). We want to filter out those "applications" to avoid confusion. + return h.Platform == "darwin" && strings.HasPrefix(s.BundleIdentifier, "com.parallels.winapp") +} + func directIngestUsers(ctx context.Context, logger log.Logger, host *fleet.Host, ds fleet.Datastore, rows []map[string]string) error { var users []fleet.HostUser for _, row := range rows { diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go index ae400834ee..7acf1d3e4d 100644 --- a/server/service/osquery_utils/queries_test.go +++ b/server/service/osquery_utils/queries_test.go @@ -1747,3 +1747,30 @@ func TestDirectIngestWindowsProfiles(t *testing.T) { } } } + +func TestShouldRemoveSoftware(t *testing.T) { + tests := []struct { + name string + want bool + s *fleet.Software + h *fleet.Host + }{ + { + name: "parallels windows software on MacOS host", + want: true, + h: &fleet.Host{Platform: "darwin"}, + s: &fleet.Software{BundleIdentifier: "com.parallels.winapp.notepad", Name: "Notepad.app"}, + }, + { + name: "regular macos software", + want: false, + h: &fleet.Host{Platform: "darwin"}, + s: &fleet.Software{BundleIdentifier: "com.apple.dock", Name: "Dock.app"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, shouldRemoveSoftware(tt.h, tt.s)) + }) + } +}