Delete unnecessary patch policies in batch set software installers (#43112)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #42991 

# 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.

- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] Timeouts are implemented and retries are limited to avoid infinite
loops
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually
This commit is contained in:
Jonathan Katz
2026-04-07 15:58:29 -04:00
committed by GitHub
parent e8de86be83
commit 856830b7ca
3 changed files with 101 additions and 0 deletions
@@ -28184,3 +28184,75 @@ func (s *integrationEnterpriseTestSuite) TestPinMajorVersion() {
)
})
}
func (s *integrationEnterpriseTestSuite) TestBatchSetSoftwareInstallersDeletesObsoletePatchPolicy() {
t := s.T()
team, err := s.ds.NewTeam(context.Background(), &fleet.Team{Name: "team_" + t.Name()})
require.NoError(t, err)
// Set up two FMAs: zoom/windows and 1password/darwin.
states := make(map[string]*fmaTestState, 2)
states["/zoom/windows.json"] = &fmaTestState{
version: "1.0",
installerBytes: []byte("xyz"),
installerPath: "/zoom.msi",
}
states["/1password/darwin.json"] = &fmaTestState{
version: "1.0",
installerBytes: []byte("abc"),
installerPath: "/1password.pkg",
}
startFMAServers(t, s.ds, states)
// Setup: batch set only zoom/windows.
var resp batchSetSoftwareInstallersResponse
s.DoJSON("POST", "/api/latest/fleet/software/batch",
batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{{Slug: ptr.String("zoom/windows")}}, TeamName: team.Name},
http.StatusAccepted, &resp,
"team_name", team.Name, "team_id", fmt.Sprint(team.ID),
)
waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team.Name, resp.RequestUUID)
// Batch set both zoom/windows and 1password/darwin.
s.DoJSON("POST", "/api/latest/fleet/software/batch",
batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{
{Slug: ptr.String("zoom/windows")},
{Slug: ptr.String("1password/darwin")},
}, TeamName: team.Name},
http.StatusAccepted, &resp,
"team_name", team.Name, "team_id", fmt.Sprint(team.ID),
)
waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team.Name, resp.RequestUUID)
// Create a patch policy for 1password/darwin.
applyResp := fleet.ApplyPolicySpecsResponse{}
s.DoJSON("POST", "/api/latest/fleet/spec/policies",
fleet.ApplyPolicySpecsRequest{Specs: []*fleet.PolicySpec{{
Name: "1password patch policy",
Team: team.Name,
Type: fleet.PolicyTypePatch,
FleetMaintainedAppSlug: "1password/darwin",
}}},
http.StatusOK, &applyResp,
)
// Verify the patch policy exists.
listPolResp := fleet.ListTeamPoliciesResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/fleets/%d/policies", team.ID), fleet.ListTeamPoliciesRequest{}, http.StatusOK, &listPolResp, "page", "0")
require.Len(t, listPolResp.Policies, 1)
require.Equal(t, fleet.PolicyTypePatch, listPolResp.Policies[0].Type)
// Batch set only zoom/windows — should not fail and should delete the obsolete patch policy.
s.DoJSON("POST", "/api/latest/fleet/software/batch",
batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{{Slug: ptr.String("zoom/windows")}}, TeamName: team.Name},
http.StatusAccepted, &resp,
"team_name", team.Name, "team_id", fmt.Sprint(team.ID),
)
waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team.Name, resp.RequestUUID)
// Verify the patch policy was deleted.
listPolResp = fleet.ListTeamPoliciesResponse{}
s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/fleets/%d/policies", team.ID), fleet.ListTeamPoliciesRequest{}, http.StatusOK, &listPolResp, "page", "0")
require.Empty(t, listPolResp.Policies)
}