From 29bef37837be8366acda48c34a52427d5e4b9edf Mon Sep 17 00:00:00 2001 From: Allen Houchins <32207388+allenhouchins@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:08:26 -0500 Subject: [PATCH] Add macOS app filter to My device software (#48637) Expose the existing "Applications" / "Full inventory" software filter on the Fleet Desktop My device Software tab for macOS hosts. The filter now defaults to Applications for macOS, sends `macos_applications` to the device software API, and keeps that query param during pagination. Updated table tests cover rendering and URL behavior on My device and non-macOS hosts. **Related issue:** Resolves #48636 # 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. - [x] 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 - [ ] 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) - [ ] QA'd all new/changed functionality manually For unreleased bug fixes in a release candidate, one of: - [ ] Confirmed that the fix is not expected to adversely impact load test results - [ ] Alerted the release DRI if additional load testing is needed ## Database migrations - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). ## New Fleet configuration settings - [ ] Setting(s) is/are explicitly excluded from GitOps If you didn't check the box above, follow this checklist for GitOps-enabled settings: - [ ] Verified that the setting is exported via `fleetctl generate-gitops` - [ ] Verified the setting is documented in a separate PR to [the GitOps documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485) - [ ] Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional) - [ ] Verified that any relevant UI is disabled when GitOps mode is enabled ## fleetd/orbit/Fleet Desktop - [ ] Verified compatibility with the latest released version of Fleet (see [Must rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md)) - [ ] If the change applies to only one platform, confirmed that `runtime.GOOS` is used as needed to isolate changes - [ ] Verified that fleetd runs on macOS, Linux and Windows - [ ] Verified auto-update works from the released version of component to the new version (see [tools/tuf/test](../tools/tuf/test/README.md)) ## Summary by CodeRabbit * **New Features** * Added the **Applications / Full inventory** software filter to the **My device > Software** tab for macOS devices. * The selected filter is now preserved when navigating through software results. * **Bug Fixes** * Corrected software filtering behavior across device pages and platforms. * Prevented the macOS filter parameter from being added for non-macOS devices. --- changes/my-device-applications-filter | 1 + .../details/cards/Software/HostSoftware.tsx | 18 +++++------ .../HostSoftwareTable.tests.tsx | 32 +++++++++++++++++-- .../HostSoftwareTable/HostSoftwareTable.tsx | 4 +-- 4 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 changes/my-device-applications-filter diff --git a/changes/my-device-applications-filter b/changes/my-device-applications-filter new file mode 100644 index 0000000000..3cc0812368 --- /dev/null +++ b/changes/my-device-applications-filter @@ -0,0 +1 @@ +- Added the "Applications" / "Full inventory" software filter to the Fleet Desktop **My device > Software** tab for macOS hosts, matching the host details page. diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx index 41efc77154..69e1e1ba9a 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftware.tsx @@ -162,16 +162,13 @@ const HostSoftware = ({ ? isPremiumTierProp : isPremiumTierFromContext; - // The /Applications filter only applies to macOS hosts on the host details - // page, and defaults to ON (only top-level applications) when the host is - // macOS and no explicit value is set in the URL. It is left undefined for - // other platforms, and on the My device page (which has no filter dropdown), - // so the param is neither sent to the API nor appended to the URL on - // pagination. - const macosApplicationsFilter = - !isMyDevicePage && isMacOS(platform) - ? queryParams.macos_applications ?? true - : undefined; + // The /Applications filter only applies to macOS hosts, and defaults to ON + // (only top-level applications) when the host is macOS and no explicit value + // is set in the URL. It is left undefined for other platforms, so the param + // is neither sent to the API nor appended to the URL on pagination. + const macosApplicationsFilter = isMacOS(platform) + ? queryParams.macos_applications ?? true + : undefined; const isUnsupported = isIPadOrIPhone(platform) && queryParams.vulnerable; // no Android software and no vulnerable software for iOS @@ -227,6 +224,7 @@ const HostSoftware = ({ id: id as string, softwareUpdatedAt, ...queryParams, + macos_applications: macosApplicationsFilter, }, ], ({ queryKey }) => deviceAPI.getDeviceSoftware(queryKey[0]), diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsx index a359d600f4..5005183ca7 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tests.tsx @@ -155,6 +155,16 @@ describe("HostSoftwareTable", () => { expect(screen.queryByText("Full inventory")).not.toBeInTheDocument(); }); + it("renders the /Applications filter on the My device page for macOS hosts", () => { + renderWithContext({ + platform: "darwin", + macosApplicationsFilter: true, + isMyDevicePage: true, + }); + + expect(screen.getByText("Applications")).toBeInTheDocument(); + }); + it("appends macos_applications to the URL on pagination when the filter is set", () => { const router = createMockRouter(); renderWithContext({ @@ -171,19 +181,35 @@ describe("HostSoftwareTable", () => { ); }); - it("does not append macos_applications to the URL on pagination when the filter is undefined (My device page)", () => { + it("appends macos_applications to the URL on pagination on the My device page", () => { const router = createMockRouter(); renderWithContext({ router, platform: "darwin", - // My device page leaves the filter undefined since it has no dropdown. - macosApplicationsFilter: undefined, + macosApplicationsFilter: true, isMyDevicePage: true, data: fullPageWithNextResults, }); fireEvent.click(screen.getByRole("button", { name: /next/i })); + expect(router.replace).toHaveBeenCalledWith( + expect.stringContaining("macos_applications=true") + ); + }); + + it("does not append macos_applications to the URL on pagination when the filter is undefined (non-macOS host)", () => { + const router = createMockRouter(); + renderWithContext({ + router, + platform: "windows", + // Non-macOS platforms leave the filter undefined since it doesn't apply. + macosApplicationsFilter: undefined, + data: fullPageWithNextResults, + }); + + fireEvent.click(screen.getByRole("button", { name: /next/i })); + expect(router.replace).toHaveBeenCalledTimes(1); expect(router.replace).not.toHaveBeenCalledWith( expect.stringContaining("macos_applications") diff --git a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx index ce41f43762..15be2ce39c 100644 --- a/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx +++ b/frontend/pages/hosts/details/cards/Software/HostSoftwareTable/HostSoftwareTable.tsx @@ -217,9 +217,7 @@ const HostSoftwareTable = ({ // The /Applications filter is only relevant for macOS hosts. const showApplicationsFilter = - !isMyDevicePage && - isMacOS(platform) && - macosApplicationsFilter !== undefined; + isMacOS(platform) && macosApplicationsFilter !== undefined; const applicationsFilterOptions: CustomOptionType[] = [ { label: "Full inventory", value: "false" },