From b96d250eccc3e48d6e4f73aa8ad33ee28527c339 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Mon, 5 Jan 2026 13:44:58 -0600 Subject: [PATCH] Fix missing upgrade codes when Windows FMAs are uploaded via GitOps (#37862) Relates to #37771. No changes file as the #37772 changes file covers this. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) ## Testing - [x] Added/updated automated tests - [x] QA'd all new/changed functionality manually --- ee/server/service/software_installers.go | 4 ++ server/service/integration_enterprise_test.go | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index eb147c6253..8aab6c4f4e 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -2363,6 +2363,10 @@ func (svc *Service) softwareBatchUpload( installer.Platform = p.MaintainedApp.Platform installer.Source = p.MaintainedApp.Source() + if installer.Source == "programs" && p.MaintainedApp.UpgradeCode != "" { + installer.UpgradeCode = p.MaintainedApp.UpgradeCode + } + installer.Extension = extension installer.BundleIdentifier = p.MaintainedApp.BundleIdentifier() installer.StorageID = p.MaintainedApp.SHA256 diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index af6997e7bd..cb8e35a10a 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -18784,6 +18784,43 @@ func (s *integrationEnterpriseTestSuite) TestUpgradeCodesFromMaintainedApps() { require.Equal(t, len(hSWRes.Software), 1) sw0 := hSWRes.Software[0] require.Equal(t, warpUpgradeCode, *sw0.UpgradeCode) + + // nuke the title so we can try this with a batch upload + mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { + _, err := q.ExecContext(ctx, "DELETE FROM software_titles WHERE upgrade_code = ?", warpUpgradeCode) + return err + }) + + // Use the batch endpoint (GitOps) to add the Windows FMA + softwareToInstall := []*fleet.SoftwareInstallerPayload{ + {Slug: ptr.String("cloudflare-warp/windows"), SelfService: true}, + } + var batchResponse batchSetSoftwareInstallersResponse + s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: softwareToInstall}, http.StatusAccepted, &batchResponse) + packages := waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, "", batchResponse.RequestUUID) + require.Len(t, packages, 1) + require.NotNil(t, packages[0].TitleID) + + // Verify the upgrade code was correctly set on the software installer + var installerUpgradeCode *string + mysql.ExecAdhocSQL(t, s.ds, func(q sqlx.ExtContext) error { + return sqlx.GetContext(ctx, q, &installerUpgradeCode, "SELECT upgrade_code FROM software_installers WHERE title_id = ?", packages[0].TitleID) + }) + require.NotNil(t, installerUpgradeCode) + require.Equal(t, warpUpgradeCode, *installerUpgradeCode) + + // Also verify the software title has the upgrade code + var afterBatch listSoftwareTitlesResponse + s.DoJSON( + "GET", "/api/latest/fleet/software/titles", + listSoftwareTitlesRequest{}, + http.StatusOK, &afterBatch, + "per_page", "1", + "available_for_install", "true", + "team_id", "0", + ) + require.Len(t, afterBatch.SoftwareTitles, 1) + require.Equal(t, warpUpgradeCode, *afterBatch.SoftwareTitles[0].UpgradeCode) } func (s *integrationEnterpriseTestSuite) TestWindowsMigrateMDMNotEnabled() {