Match Windows Fleet maintained apps by similar name (#40544)

**Related issue:** Resolves #37802 
Attempt to fix Windows FMAs not showing up as available when the
software titles they match to have upgrade codes. Since we don't surface
upgrade codes in the `fleet_maintained_apps` table and matching exactly
by name could miss some cases, this fix uses `team_titles.name LIKE
CONCAT(LEAST(fma.name, fma.unique_identifier), '%')`. Note the LEAST
there is only for the "Box Drive" app which has a longer name than
unique_identifier, and just compares the strings and not their length.

This isn't optimal for performance or correctness, but it only checks
with titles already available to the team as installers so it shouldn't
be terrible. Until upgrade_code is surfaced in the
`fleet_maintained_apps` table this should be sufficient.

# 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] Added/updated automated tests

- [x] QA'd all new/changed functionality manually
This commit is contained in:
Jonathan Katz
2026-02-26 12:47:52 -05:00
committed by GitHub
parent e0169fb82c
commit 3abdf74344
3 changed files with 99 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
- Fixed a bug where Fleet maintained apps for Windows won't show as available in the list when they actually are.
+11 -2
View File
@@ -47,7 +47,7 @@ ON DUPLICATE KEY UPDATE
const teamFMATitlesJoin = `
team_titles.id software_title_id FROM fleet_maintained_apps fma
LEFT JOIN (
SELECT DISTINCT st.id, st.unique_identifier
SELECT DISTINCT st.id, st.unique_identifier, st.name, si.platform
FROM software_titles st
LEFT JOIN
software_installers si
@@ -63,7 +63,16 @@ const teamFMATitlesJoin = `
AND vat.platform = va.platform
AND vat.global_or_team_id = ?
WHERE si.id IS NOT NULL OR vat.id IS NOT NULL
) team_titles ON team_titles.unique_identifier = fma.unique_identifier`
) team_titles
ON team_titles.unique_identifier = fma.unique_identifier
-- pattern match fma name to a similar title name, since upgrade_code is not surfaced in fma table
OR (
team_titles.platform = fma.platform
AND fma.platform = 'windows'
-- Box Drive is the only FMA at the point of writing this where unique_identifier is shorter than name
AND team_titles.name LIKE CONCAT(LEAST(fma.name, fma.unique_identifier), '%')
)
`
func (ds *Datastore) GetMaintainedAppByID(ctx context.Context, appID uint, teamID *uint) (*fleet.MaintainedApp, error) {
stmt := `SELECT fma.id, fma.name, fma.platform, fma.unique_identifier, fma.slug, `
@@ -24,6 +24,7 @@ func TestMaintainedApps(t *testing.T) {
{"ListAndGetAvailableApps", testListAndGetAvailableApps},
{"SyncAndRemoveApps", testSyncAndRemoveApps},
{"GetMaintainedAppBySlug", testGetMaintainedAppBySlug},
{"ListAvailableAppsWindows", testListAvailableAppsWindows},
}
for _, c := range cases {
@@ -542,3 +543,89 @@ func testGetMaintainedAppBySlug(t *testing.T, ds *Datastore) {
TitleID: nil,
}, gotApp)
}
func testListAvailableAppsWindows(t *testing.T, ds *Datastore) {
ctx := context.Background()
team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "Team 1"})
require.NoError(t, err)
user := test.NewUser(t, ds, "Alice", "alice@example.com", true)
maintained1, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
Name: "Maintained1",
Slug: "maintained1",
Platform: "windows",
UniqueIdentifier: "Maintained1 (MSI)",
})
require.NoError(t, err)
maintained2, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{
Name: "Maintained2",
Slug: "maintained2",
Platform: "darwin",
UniqueIdentifier: "com.foo",
})
require.NoError(t, err)
expectedApps := []fleet.MaintainedApp{
{
ID: maintained1.ID,
Name: maintained1.Name,
Platform: maintained1.Platform,
Slug: "maintained1",
},
{
ID: maintained2.ID,
Name: maintained2.Name,
Platform: maintained2.Platform,
Slug: "maintained2",
},
}
apps, _, err := ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
require.NoError(t, err)
require.Len(t, apps, 2)
require.Nil(t, apps[0].TitleID)
require.Nil(t, apps[1].TitleID)
require.Equal(t, expectedApps, apps)
// upload an installer that will create a title with a similar name, but with
// an upgrade code so that unique identifier doesn't match
_, titleID, err := ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
Title: "Maintained1 (MSI)",
UpgradeCode: "{UPGRADE-CODE}",
Source: "programs",
StorageID: "storageid1",
Filename: "maintained1.msi",
Extension: "msi",
Platform: "windows",
Version: "1.0",
UserID: user.ID,
TeamID: &team1.ID,
ValidatedLabels: &fleet.LabelIdentsWithScope{},
FleetMaintainedAppID: ptr.Uint(maintained1.ID),
})
require.NoError(t, err)
// create a pkg installer that should not match by similar name
_, _, err = ds.MatchOrCreateSoftwareInstaller(ctx, &fleet.UploadSoftwareInstallerPayload{
Title: "Maintained2 ",
BundleIdentifier: "Maintained2.ShallNotBeMatched",
Source: "apps",
StorageID: "storageid2",
Filename: "maintained2.pkg",
Extension: "pkg",
Platform: "darwin",
Version: "1.0",
UserID: user.ID,
TeamID: &team1.ID,
ValidatedLabels: &fleet.LabelIdentsWithScope{},
})
require.NoError(t, err)
// the windows app should be found using using name, because the existing software title has an upgrade code
apps, _, err = ds.ListAvailableFleetMaintainedApps(ctx, &team1.ID, fleet.ListOptions{IncludeMetadata: true})
require.NoError(t, err)
require.Len(t, apps, 2)
require.NotNil(t, apps[0].TitleID)
require.Equal(t, titleID, *apps[0].TitleID)
// the darwin app should not be matched by name
require.Nil(t, apps[1].TitleID)
}