Commit Graph
2745 Commits
Author SHA1 Message Date
Carlo b3c2a6368d Count FMAs by slug (#48783)
**Related issue:** Resolves #48528

This keys the count, pagination, and the frontend row-combining on the
app's slug token (the prefix before `/`, shared across an app's platform
entries but distinct across apps). The count now equals the rows shown
in every view (macOS, Windows, All), and name-colliding apps stay as
separate rows.

  # Checklist for submitter

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

  ## Testing

  - [x] Added/updated automated tests
  - [x] QA'd all new/changed functionality manually

  For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Software listings now group platform-specific installers into a single
app row based on the app identifier, improving how macOS and Windows
entries appear together.

* **Bug Fixes**
* Apps with the same display name but different identifiers now stay
separate instead of being merged incorrectly.
* List counts and pagination now match the combined app view more
accurately across the software pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 17:05:59 -04:00
Victor Lyuboslavsky bf94df6e6f Show certificates on host details page for Windows (#31294) (#48469)
Surface the existing "Certificates" card on the host details page for
Windows hosts, with parity to macOS. Requires osquery 5.23.1 or higher.

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

Demo video: https://www.youtube.com/watch?v=kGRp-YtnnJc
Docs: https://github.com/fleetdm/fleet/pull/48493/changes

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

## Testing

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

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **New Features**
* Windows host certificates now display on the host details page (gated
by minimum agent/osquery version), including scope (**System** vs
**User**) and improved scope-aware certificates list details.

* **Bug Fixes**
* Certificate table labeling and help text are now platform-appropriate
(with “Keychain” renamed to “Scope”).
* Windows certificate reconciliation is more resilient, preserving
certificates for scopes not observed during a collection run and
preventing row collapsing when ids repeat across scopes.

* **Tests**
* Expanded coverage for Windows/malformed DN parsing and scope-aware
reconciliation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 20:28:21 +01:00
Victor Lyuboslavsky 51f1e85c05 Improved the performance of Windows MDM profile installation (#48733)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45650 

# 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] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

* **Bug Fixes**
* Improved MySQL migration handling for MDM command results by safely
removing an outdated foreign key when present, preventing issues during
upgrade and re-run scenarios.
* Updated the database schema definition to keep related response
foreign key behavior consistent.
* **Chores**
* Added the latest migration version to the migration status seed data
to ensure version tracking stays in sync.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 18:34:10 +01:00
fletcher-rudra dfc8c272d3 Add Zorin OS as a recognized Linux platform (#45712)
**Related issue:** Resolves #45710

# Checklist for submitter

- [x] Changes file added (`changes/45710-zorin-os-support`).
- [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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops.
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes.

## Testing

- [x] Added/updated automated tests —
`server/vulnerabilities/oval/oval_platform_test.go` extended with Zorin
→ Ubuntu LTS mapping cases (16/17/18) plus an unknown-version case
(`Zorin OS 99` → `zorin_99`, which `IsSupported()` rejects).
- [x] QA'd all new/changed functionality manually — Zorin OS 17.0 and
18.1 hosts enrolled against a patched Fleet server, host details show
`platform=zorin`, software inventory populates, and OVAL CVE matching
produces results against the corresponding `ubuntu_2204` / `ubuntu_2404`
feeds.

## Database migrations

- N/A. No schema changes.

## New Fleet configuration settings

- N/A. No new settings.

## fleetd/orbit/Fleet Desktop

- N/A. Server + frontend only; no fleetd/orbit changes.

---

## Summary

Fleet previously logged `unrecognized platform` for Zorin OS hosts
(osquery reports `platform=zorin` from `/etc/os-release` `ID=zorin`).
The common workaround was running osquery with
`--force_platform=ubuntu`, which masquerades the host. This change adds
`zorin` as a first-class Linux platform alongside Ubuntu:

- **`server/fleet/hosts.go`** — register `zorin` in `HostLinuxOSs` and
`HostDebPackageOSs`
- **`server/datastore/mysql/linux_mdm.go`** — include Zorin in the Linux
disk-encryption summary query
- **`server/vulnerabilities/oval/oval_platform.go`** — map Zorin major
version to the underlying Ubuntu LTS OVAL feed (16 → 20.04, 17 → 22.04,
18 → 24.04). Unknown future versions fall through to an unsupported
`zorin_<major>` identifier so vulnerability scanning is skipped rather
than served stale data from an aging LTS feed.
- **frontend** — add `zorin` to `HOST_LINUX_PLATFORMS`, the
disk-encryption support list and type guard, the label platform
dropdown, and the icon mapping (Ubuntu icon, since no Zorin-specific
asset exists in the repo).

No new dependency, schema migration, or config setting. Reuses existing
Ubuntu OVAL feeds and the existing Ubuntu icon.

Diff is ~30 lines net across 9 files (8 patched + 1 `changes/` file).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added Zorin OS as a supported Linux platform.
* Zorin hosts included in Linux disk-encryption summaries and treated as
disk-encryption capable.
* Zorin OS available as a selectable/filterable platform label and
considered DEB-install compatible.
* Vulnerability scanning enabled for Zorin 16→Ubuntu 20.04, 17→22.04,
18→24.04; unknown/future Zorin versions are marked unsupported and
skipped for CVE matching.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/45712?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-06 13:00:10 -03:00
Sharon KatzandClaude Opus 4.6 6ba04b0d20 Optimize query aggregated stats cron to skip queries without execution data (#48698)
**Related issue:** Resolves #48697

## Summary

The hourly `UpdateQueryAggregatedStats` cron job currently walks **every
query ID** in the `queries` table and runs 5 expensive
percentile-calculation queries per query against
`scheduled_query_stats`, plus 1 INSERT/UPDATE to store results. Most
queries have no execution data at all (they are saved queries,
live-only, or de-scheduled), so this work is pure waste.

This PR changes the cron to only process queries that actually have
execution data, by querying `scheduled_query_stats` directly instead of
the `queries` table. The now-unused `walkIdsInTable` helper function is
also removed.

### How the calculations work

`CalculateAggregatedPerfStatsPercentiles` computes performance
statistics for each query that has been scheduled and executed by hosts.
For each qualifying query ID, it runs these operations against the read
replica:

1. **P50 user_time** -- Calculates the median (50th percentile) of
per-host average user-mode CPU time. The query groups
`scheduled_query_stats` rows by `host_id`, computes `SUM(user_time) /
SUM(executions)` per host, sorts them, then picks the row at position
`FLOOR(total_rows * 0.5) + 1` using a `@rownum` session variable.

2. **P95 user_time** -- Same calculation but picks the 95th percentile
row (`FLOOR(total_rows * 0.95) + 1`).

3. **P50 system_time** -- Same percentile calculation for kernel/system
CPU time.

4. **P95 system_time** -- 95th percentile of system CPU time.

5. **Total executions** -- `SELECT COALESCE(SUM(executions), 0) FROM
scheduled_query_stats WHERE scheduled_query_id = ?`

6. **INSERT/UPDATE** -- Writes the JSON result (`user_time_p50`,
`user_time_p95`, `system_time_p50`, `system_time_p95`,
`total_executions`) into the `aggregated_stats` table via `INSERT ... ON
DUPLICATE KEY UPDATE`.

### What changed

**Before:** `SELECT id FROM queries` -- walks every query (200-400+ in a
typical deployment).

**After:** `SELECT DISTINCT scheduled_query_id FROM
scheduled_query_stats WHERE executions > 0` -- walks only queries that
have actual execution data (typically 10-20).

### Benchmark results (MySQL 8.0, 300 queries seeded, only 15 with
stats)

| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| Avg time per cron run | 3.36s | 0.28s | **12.2x faster** |
| DB operations per run | 1,800 | 90 | **95% fewer** |
| DB operations per day | 43,200 | 2,160 | **41,040 eliminated** |
| `aggregated_stats` rows written | 300 (285 empty) | 15 (all
meaningful) | Less table bloat |
| Correctness | baseline | byte-identical JSON | **Zero regression** |

At 500+ queries the current approach **drops MySQL connections**
(`unexpected EOF` / `invalid connection`) because the cursor is held
open across thousands of heavy serial queries. The optimized version
handles any scale trivially.

### Impact analysis

Verified safe across all consumers: all query endpoints use `LEFT JOIN
aggregated_stats` (NULL-safe for missing rows), the frontend explicitly
handles null stats as "Undetermined", live query stats
(`service_campaigns.go`) call `CalculateAggregatedPerfStatsPercentiles`
directly and are unaffected, and query deletion already cleans up both
`scheduled_query_stats` and `aggregated_stats` rows.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] Added/updated automated tests
- [x] Confirmed that the fix is not expected to adversely impact load
test results

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-07-03 13:29:49 -04:00
Lucas Manuel Rodriguez 1ceca6ad8e Cleanup policy_membership stale entries in distributed/write (#48674)
Resolves #47241.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

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

## Testing

- [X] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Better host policy results by automatically cleaning up out-of-scope
`policy_membership` records.
* Refreshes host failing-policy counts after cleanup, including when
distributed writes report “no policies in scope.”
* Preserves existing safeguards by skipping this cleanup during
setup/initial configuration to prevent premature updates.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-03 14:12:08 -03:00
Lucas Manuel Rodriguez 171504dc18 Fix nondeterministic device_mapping order in ListHosts (#48696)
Fixes the `device_mapping` ordering flake in
`TestIntegrations/TestListHostsByLabel` and
`TestIntegrations/TestHostsReportDownload`, seen across integration-core
jobs on `main` since July 1 (e.g. [this
run](https://github.com/fleetdm/fleet/actions/runs/28567132474)).

#48488 replaced the derived-table `GROUP_CONCAT` join in `ListHosts`
with a correlated subquery, but the `GROUP_CONCAT` has no `ORDER BY`, so
MySQL returns `device_mapping` entries in arbitrary order. The old plan
happened to read `idx_host_emails_host_id_email` in index order, which
masked this; the new access path doesn't, so the order now varies
between endpoints and runs — the tests compare `GET /hosts` output
against `GET /labels/{id}/hosts` (and CSV report) output for the same
host and intermittently see `[b@b.c, a@b.c]` vs `[a@b.c, b@b.c]`.

This adds `ORDER BY he.email, he.source` inside the `GROUP_CONCAT`,
matching the ordering of the single-host `listHostDeviceMappingDB`
query. The sort applies only within each host's few email rows, so it
doesn't affect the perf improvement from #48488.

No changes file: #48488 is unreleased (not in any RC branch), so this is
a fix to an unreleased change.

# Checklist for submitter

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

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

Covered by existing tests: `TestIntegrations/TestListHostsByLabel` and
`TestIntegrations/TestHostsReportDownload` assert the (now
deterministic) ordering. Ran both 4× locally with `MYSQL_TEST=1
REDIS_TEST=1`, plus the `TestHosts` device-mapping/ListHosts datastore
tests — all green.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved consistency of host device mapping results by making the
ordering deterministic.
* Fixed an issue where device mapping entries could appear in different
orders between requests.
* **Tests**
* Added coverage to verify the device mapping order remains stable and
predictable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-03 14:09:07 -03:00
Jonathan Katz 8b1e806754 Fix GitOps creating duplicate software titles (#48664)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48054 
Changes:
- Changes batch add installer path to reuse
`getOrGenerateSoftwareInstallerTitleID`
- Adds migration to retroactively fix duplicate titles created by this
bug

# 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

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- The tables will actually be updated, so it makes sense for that to
change if it happens

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Resolved a case where GitOps uploads of Windows software could create
duplicate software titles when a host had already reported the same
program.
* Improved deduplication and reassociation so related records
(installers and icons) are merged into the retained title, preserving
the correct upgrade code.
* **Tests**
* Added regression coverage for the duplicate-title scenario to prevent
future repeats.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-02 19:16:28 -04:00
George Karr dea65b824c Bump migration timestamps after 4.88 cherry-pick (#48617)
**Related issue:** Resolves NA (release hygiene — migration ordering)

## What & why

The `4.88.0` patch cherry-picked two migrations,
`20260624210253_AddHostMDMAppleEnrollmentPermissions` and
`20260624210311_RenamePersonalEnrollmentStatus`. Eight migrations on
`main` were **not** cherry-picked into 4.88 but had **earlier**
timestamps than those two:

| Old timestamp | Migration |
|---|---|
| 20260611202649 | AddWindowsMDMConfigProfilesPendingDelete |
| 20260615135619 | AddSetupExperienceSoftwareInstallers |
| 20260617172853 | CreateSoftwareTitleTeamPins |
| 20260617194413 | AddAndroidProfileVariableTracking |
| 20260622124714 | AddPolicyGateToSetupExperienceResults |
| 20260622124734 | AddBYODFleetAndADUEEnrollment |
| 20260623140135 | AddSupportSoftwareCategory |
| 20260624152755 | AddCertAndAndroidAppVariableTracking |

This violates the rule in
`docs/Contributing/workflows/releasing-fleet.md`:

> Any migrations that are not cherry-picked in a patch must have a
_later_ timestamp than migrations that were cherry-picked.

Left as-is, a customer on `4.88.0` (who applied migrations through
`20260624210311`) upgrading to `4.89.0` would hit these 8 as
out-of-order/missing migrations older than their highest-applied
version.

## Fix

Bumped the 8 non-cherry-picked migrations to new timestamps
(`20260702013055`–`20260702013102`) using `tools/bump-migration`,
**preserving their relative order**, so they now sort after the
cherry-picked migrations and
`20260626120000_CompressWindowsMDMResponsesColumn`. Regenerated
`schema.sql`.

Verified `20260626120000_CompressWindowsMDMResponsesColumn` (the only
other non-cherry-picked migration, already correctly ordered) touches
only `windows_mdm_responses` — none of the 8 moved migrations touch that
table, so no dependency inversion is introduced by the reorder. None of
these 10 migrations shipped in `4.87.1`, so no released database is
affected.

`rc-patch-fleet-v4.88.0` needs no change. This lands on `main` and
should be reflected on `rc-minor-fleet-v4.89.0`.

# Checklist for submitter

## Database migrations

- [x] Migration files renamed via `tools/bump-migration`; function names
updated to match new timestamps.
- [x] Regenerated `schema.sql` via `make dump-test-schema`; migrations
apply cleanly in the new order.
- [x] No schema/content changes to the migrations themselves — timestamp
renumber only.

## Testing

- [x] `go build ./server/datastore/mysql/migrations/...` and `go vet`
pass; schema regeneration ran all migrations successfully in order.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added support for additional device/profile tracking across Android,
Apple, Windows, certificates, and apps.
* Added new setup and software management records, including support
software categories and team pins.
  * Added a policy-gating flag for setup experience results.

* **Bug Fixes**
* Improved database consistency with stronger uniqueness and
cascade-delete behavior.
* Updated schema tracking so migrations apply cleanly with the latest
database state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-02 12:00:56 -05:00
Juan Fernandez 4afd59833d Fix installed_software status in policy automation activities
Relates to #38670

The policies/:id/automation_activities endpoint derived the top-level
status for installed_software activities from the live
host_software_installs.status generated column. That column becomes NULL
when the install row is marked removed=1 (e.g. after the installer
package is edited/updated or the software is re-installed), so a
historically-successful install was miscategorized as "error".

Derive the outcome from the activity's recorded details.status instead,
which reflects the install result at the time the activity was created.
The install output still comes from host_software_installs. This applies
to both the displayed status and the ?status=error|success filter.

Also fixed alignment with the info icon on the policy automations table.
2026-07-02 09:59:59 -04:00
George Karr 80b883a2e7 Adding in check to disable recovery lock on personal macos since it doesn't have the required permissions (#48598)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48594

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

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

For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results
- [x] Alerted the release DRI if additional load testing is needed

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Recovery-lock password checks now skip personally owned (BYOD) Apple
devices, avoiding failures on eligible hosts.
* Recovery-lock clear actions are no longer applied to personally owned
enrollments.

* **Tests**
* Added coverage to verify BYOD devices are excluded from both
recovery-lock enforcement and clear workflows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-01 16:20:50 -05:00
Juan Fernandez e8f26ec4ef Fix S3 file carve cleanup hang and rework reconciliation
Relates to #48549

The S3 carve cleanup (server/datastore/s3, run by the
cleanups_then_aggregation cron) advanced ListObjectsV2 pagination using
the response's ContinuationToken — an echo of the request token —
instead of NextContinuationToken. On any bucket with more than one page
of objects this looped forever, hanging the entire serial cleanup cron
and stalling every cleanup/aggregation job ordered after it.

Replace the bucket-listing reconciliation with a direct HeadObject probe
per carve, which is exact and independent of listing order or object
counts:

- Only carves older than 24h with a completed upload are reconciled
(mirrors the MySQL carve store's floor; skips in-flight multipart
uploads). A carve is expired only on a definitive not-found; transient
or other probe errors leave it for a future run, so a carve whose object
still exists is never expired.
- Probes run with bounded concurrency; expirations are written in one
batched, retryable UPDATE (new ExpireCarves datastore method) rather
than one per carve.
- The number of carves reconciled per run is capped so a large backlog
drains across runs without any single run making unbounded S3 requests.

Add S3-carve-store-only server settings (the MySQL carve store is
unaffected):
- s3.carves_cleanup_disabled       — skip reconciliation entirely
- s3.carves_cleanup_max_per_run    — per-run cap (default 1000)
- s3.carves_cleanup_concurrency    — concurrent probes (default 32)

Also log the expired count per run and fix the test bucket cleanup
helper to paginate. Adds unit tests (transient-error safety, partial
failure, concurrency) and a MySQL integration test for ExpireCarves.
2026-07-01 14:00:59 -04:00
Jordan Montgomery 6223af892e Fix manual-personal enrollment for iOS/iPadOS (#48534)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

Unreleased bug, no changes file

- [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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] 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:

- [x] Confirmed that the fix is not expected to adversely impact load
test results
- [ ] Alerted the release DRI if additional load testing is needed


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Personal enrollment status is now preserved and updated correctly when
MDM device records change.
* macOS MDM ingestion now keeps the BYOD/personal enrollment flag for
Fleet devices instead of defaulting it away.
* Incoming server URLs continue to have query parameters removed while
still retaining the enrollment status used for processing.

* **Tests**
* Added coverage for personal enrollment updates and macOS ingestion
scenarios, including BYOD and non-BYOD cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-01 09:59:55 -05:00
Lucas Manuel RodriguezandCopilot Autofix powered by AI 2d70a7b500 Associate all matching hosts with a SCIM/IdP user (not just the first) (#48351)
Resolves https://github.com/fleetdm/fleet/issues/48378 (issue found
while working on the Google Workspace IdP integration).

## Summary

Fixes a bug where an IdP user associated with **multiple hosts** only
had IdP host vitals populated on **one** of them.

`maybeAssociateScimUserWithHostMDMIdP` (called when a SCIM/IdP user is
created) matched all hosts whose MDM IdP account corresponds to the
user, but then deliberately linked only `hostIDs[0]` (with a `// TODO:
confirm desired behavior` / "just use the first one"). So when a user is
created *after* the hosts already enrolled — e.g. a directory sync
creating users for people who each have a laptop and a desktop — only
the first host got a `host_scim_user` row, and therefore only that host
received the user's IdP host vitals and profile-variable resends.

The fix links **every** matching host. `associateHostWithScimUser` is
keyed on `host_id` (`INSERT … ON DUPLICATE KEY UPDATE`) and triggers its
own per-host profile resend, so calling it once per host is safe and
idempotent.

This is shared SCIM linking code, so the fix benefits all IdP sources
(Okta/Entra SCIM as well as the Google Workspace directory sync that
surfaced it). Deletes and updates already handled multiple hosts
correctly; only the initial reverse-link was capped.

## Testing

Added `testScimUserCreateAssociatesAllMatchingHosts`
(`server/datastore/mysql/scim_test.go`): two hosts share one MDM IdP
account, then a SCIM user is created — both hosts must resolve to it via
`ScimUserByHostID`. Fails before the fix (host #2 unlinked), passes
after.

**Related issue:** Resolves #48378

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`.
- [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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* SCIM/IdP user provisioning now associates a new SCIM user with **all**
matching hosts, not just the first match.
* Host end-user details (including IdP username/full name) are now
populated consistently on every associated host.
* **Tests**
* Added SCIM integration and datastore regression coverage to ensure
multiple hosts linked to the same IdP account are all associated during
user creation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-01 10:40:31 -03:00
Lucas Manuel Rodriguez bec3b0dc2a Reduce MySQL reader load on GET /hosts with device_mapping + search query (#47722) (#48488)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47722

The issue was from a customer running `GET
/api/v1/fleet/hosts?device_mapping=true&page=1&per_page=100&query=<ADDRESS>%40example.com`
on a script in a for loop. This change reduces the impact of the API on
such workflows.

Results from my local load test:

EXPLAIN ANALYZE:
```
┌───────────────────────────────────┬────────────────┬─────────────┬─────────────────────────────────────────────┐
│                                   │ optimizer cost │ actual time │         device_mapping aggregation          │
├───────────────────────────────────┼────────────────┼─────────────┼─────────────────────────────────────────────┤
│ Old (derived-table GROUP BY join) │ ~23,179        │ ~73 ms      │ materialized dm derived table, cost ~7,125  │
├───────────────────────────────────┼────────────────┼─────────────┼─────────────────────────────────────────────┤
│ New (correlated subquery)         │ ~1,260         │ ~25 ms      │ Aggregate … loops=1 (only the returned row) │
└───────────────────────────────────┴────────────────┴─────────────┴─────────────────────────────────────────────┘
```
Tests with 10k hosts:
```
┌───────────────────────────────────┬────────────┬───────────────┬───────┐
│              dataset              │ OLD (main) │ NEW (this PR) │ ratio │
├───────────────────────────────────┼────────────┼───────────────┼───────┤
│ 10k hosts × 3 emails (30k rows)   │ 4.6s       │ 1.1s          │ ~4×   │
├───────────────────────────────────┼────────────┼───────────────┼───────┤
│ 10k hosts × 30 emails (300k rows) │ 35.9s      │ 1.2s          │ ~30×  │
└───────────────────────────────────┴────────────┴───────────────┴───────┘
```

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

## Testing

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

## What & why

`GET
/api/v1/fleet/hosts?device_mapping=true&page=1&per_page=100&query=<email>`
caused high MySQL **reader** load on instances with ~10k hosts. Each
page load ran an expensive aggregation over the entire `host_emails`
table even though only ~100 rows are returned.

**Root cause:** with `device_mapping=true`, `applyHostFilters` added a
`LEFT JOIN` on a derived table with `GROUP BY host_id` over
`host_emails`. Because of the `GROUP BY`, MySQL must fully materialize
that derived table (aggregating every row for all hosts) before the
outer `WHERE`/`LIMIT 100` can be applied, so the full cost is paid on
every page request regardless of result size. `CountHosts` reused the
same options, materializing the aggregation a **second** time per page
load.

**Fixes (both in `server/datastore/mysql/hosts.go`):**

1. Replaced the derived-table join with a correlated subquery in the
`SELECT` list (only when `opt.DeviceMapping`), so it is evaluated only
for the rows actually returned, each as an indexed lookup on
`idx_host_emails_host_id_email`. This matches the existing
`host_additional` pattern in the same query.
2. Set `opt.DeviceMapping = false` in `CountHosts` — the column is never
selected for counting — mirroring the existing `opt.DisableIssues`
handling.

## Notes

- The composite index `idx_host_emails_host_id_email (host_id, email)`
already exists, so the correlated subquery resolves via an indexed
lookup per returned row.
- `TestHosts` (full suite) passes, including `HostDeviceMapping`,
`CustomHostDeviceMapping`, and `IDPHostDeviceMapping` (the last two
verify the `custom_*` → `custom` and `idp` → `mdm_idp_accounts` source
translation still works through the new subquery).
- Recommend validating with `EXPLAIN ANALYZE` on a ~10k-host dataset
before/after, per the issue. I did not have access to such a dataset.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Performance**
* Improved host list responsiveness when using search filters alongside
device mapping.
* Reduced database load during host listing by retrieving device mapping
more efficiently per host.
* Improved host counting speed by avoiding device-mapping evaluation for
count queries.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-01 10:28:30 -03:00
Steven PalmesanoandLucas Manuel Rodriguez fe46e41a52 Add public IP address to host search (#46809)
**Related issue:** Resolves #4842

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.


## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* IP-based host searches now match both private and public IP addresses.
* Updated the host search box placeholder and tooltip to refer to “IP
address” (instead of “private IP”).
* **Tests**
* Expanded backend coverage to verify matching (and non-matching)
results for both private and public IPs when listing and searching
hosts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
2026-06-30 16:20:37 -03:00
Victor Lyuboslavsky af2d4dbbbd Optimize IsHostConnectedToFleetMDM on the orbit check-in hot path (#44629) (#48375)
**Related issue:** Resolves #44629

This folds the connected-to-Fleet check into `GetHostMDM` via a
`connected_to_fleet` column that mirrors the existing
`IsHostConnectedToFleetMDM` and `hostMDMSelect` conditions, and derives
the value in `GetOrbitConfig` from the `host_mdm` data it already
fetches. Result: **2 queries → 1** on the orbit check-in hot path, with
no semantic change.

# Checklist for submitter

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

## Testing

- [x] Added/updated automated tests
- [x] 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 is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Performance Improvements**
* Orbit check-ins now determine MDM connection status from existing host
MDM data, reducing database work and improving response time.
* **Bug Fixes**
* Added platform-aware connection detection so Windows, Apple, and
Android devices report MDM connectivity more accurately.
* Updated related checks and tests to keep connection status consistent
across enrollment and unenrollment changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-29 17:11:27 +01:00
Lucas Manuel Rodriguez 70b41063a4 Google Workspace IdP [3/6]: usage statistics (#48166)
### 🥞 Stack (review/merge bottom-up)

1. #48164 — Activity types (FE+BE)
2. #48165 — Backend (cron + directory sync)
3. **#48166 — Usage statistics ⬅ this PR**
4. #48167 — fleetctl generate-gitops
5. #48168 — Settings UI

📄 Documentation is tracked separately in #48169 (targets
`docs-v4.89.0`).

---

## Summary

**PR 3 of 6.** **Usage statistics**: report whether a Google Workspace
IdP integration is configured via the new `googleWorkspaceConfigured`
field (`server/fleet/statistics.go`,
`server/datastore/mysql/statistics.go`).

> 🥞 **Stacked PR.** Base: `42915-gw-idp-vitals-2-backend` (PR 2).

**Related issue:** Resolves #42915

# Checklist for submitter

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements).
- [ ] Timeouts are implemented and retries are limited to avoid infinite
loops.

## Testing

- [ ] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Usage statistics now include whether Google Workspace is configured,
improving reporting accuracy.

* **Bug Fixes**
* Fixed statistics submissions so the Google Workspace configuration
status is included consistently in outgoing requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-29 12:34:06 -03:00
Victor Lyuboslavsky a019cfb8f4 Compress windows_mdm_responses envelopes on the Windows MDM hot path (#48320)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44188 

# 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

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Windows MDM check-in response payloads are now stored gzip-compressed
in the database to reduce write pressure for large SyncML data.
* When fetching results, responses are automatically decompressed so the
original content is returned to clients.
* Empty payloads are preserved, and stored data is validated to ensure
only valid gzip content is accepted.
* **Database / Migration**
* Added a migration and backfill to move existing records from
uncompressed storage to the new compressed column format.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-26 22:03:01 +01:00
Jordan Montgomery b438893bc2 Do not block further wipe commands on inactive existing entry (#48358)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #45931

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] 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 is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Improved device lock handling so only active pending lock commands are
treated as valid.
* Fixed stale lock state cases where an old lock reference no longer
blocks a new lock request.
* When a prior lock command is no longer deliverable, a new lock command
is now issued and tracked correctly.
* Updated coverage to verify lock status transitions and replacement
behavior in these edge cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-26 15:58:42 -04:00
George Karr 0f439f9593 Auto-update, pin, and rollback Fleet-maintained apps via UI and GitOps (#48293)
**Related issue:** Resolves #38504

  **Constituent PRs (merged into this feature branch):**

- #47682 — Fleet UI: APRF Software title details page Library/Inventory
layout
- #47808 — Extend update software installer API to support FMA version
pinning
  - #47944 — Fleet UI: APRF library item accordion component
  - #48081 — Versions modal, multi-row Library, pinned state
  - #48098 — Add `pinned_version` to `edited_software` activity
  - #48123 — Auto-update FMA cron
  - #48144 — Download a newly-published FMA version when pinned to it

  # Checklist for submitter

- [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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

  ## Testing

  - [x] Added/updated automated tests
- [x] 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 is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Fleet-maintained app version pinning (Latest, exact, and major)
via a new Versions modal.
* Introduced premium auto-updates for maintained apps with pin-aware
promotion and rollback-safe caching.
  * Added expandable library version rows and a Policies modal.
* **Bug Fixes**
* Improved pin handling, cache/manifest hydration, and safer update
behavior on per-app failures and deduplication.
* **UI/UX**
* Refreshed the Software title details experience with new
accordion/list patterns, redesigned details widget/tooltips, and updated
installer presentation.
* **Documentation**
* Expanded Storybook component/page coverage and adjusted Storybook
canvas padding.
* **Tests**
* Added/updated unit and integration tests for pinning, auto-update
flows, and new modal/UI behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-26 14:42:23 -05:00
Carlo d5afa45efd Pin-cleanup on FMA delete (#48333)
**Related issue:** Resolves #48309

Deleting a Fleet-maintained app from a team now also deletes its
`software_title_team_pins` row. Previously the pin survived the delete
(the FK cascades only on title deletion, and the title row outlives the
installer rows), so re-adding the app resurfaced a stale pin pointing at
a version no longer cached.

  # Checklist for submitter

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

  ## Testing

  - [x] Added/updated automated tests
  - [x] QA'd all new/changed functionality manually
2026-06-26 13:18:50 -04:00
Carlo 0b6f8066db Return per-version filename in fleet_maintained_versions (#48335)
**Related issue:** Resolves #48334

The software title response now returns a per-version `filename` in
`fleet_maintained_versions`, and the Library version rows render each
version's own filename instead of the active installer's. Previously,
every cached-version row showed the active installer's filename because
the array didn't include one.

  # Checklist for submitter

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

  ## Testing

  - [x] Added/updated automated tests
  - [x] QA'd all new/changed functionality manually
2026-06-26 12:44:55 -04:00
George Karr a2af2d97a0 Adding BYOD backend changes (#47716)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** #23242

Backend changes for Apple BYOD (personal) MDM enrollment.

- Adds a `byod` enrollment path that distinguishes personal devices from
organization-owned devices.
- Persists per-host Apple MDM enrollment access rights in a new
`host_mdm_apple_enrollment_permissions` table so SCEP/ACME renewal
honours Apple's monotonic-narrowing invariant (permissions can never be
widened on profile replacement).
- Surfaces wipe/lock/clear-passcode allowed flags on host details for
manually-enrolled Apple hosts.
- Renames the personal enrollment status label to `On (manual -
personal)`.

# 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.
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

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

### Test plan

- Manual (profile) enrollment, company-owned: device receives full
access rights; wipe/lock/clear-passcode allowed.
- Manual (profile) enrollment, personal (BYOD via `byod=1`): device
receives narrowed access rights (no device lock/erase); host details
show wipe/lock/clear-passcode disabled.
- SCEP/ACME renewal for each of the above: renewed profile preserves the
original ServerURL (incl. `byod=1`) and the stored (narrowed) access
rights; Apple does not reject the replacement.
- Renewal batching: multiple company-owned hosts collapse into a single
InstallProfile command; a BYOD host gets its own command.
- Account-Driven User Enrollment (ADUE): enroll a personal device via
ADUE and confirm it is inherently restricted (Apple `UserEnrollment`
mode — no device lock/erase regardless of AccessRights), and that its
SCEP renewal succeeds and preserves the account-driven enrollment
profile.
- Deleted-then-returned device: delete a still-enrolled BYOD host in
Fleet, let it check back in, and confirm a subsequent SCEP renewal still
uses the narrowed permissions.

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added personal (BYOD) Apple MDM enrollment support across manual
profiles, OTA enrollments, and SCEP/ACME certificate renewals, with
access rights generated appropriately.
* Apple host details now surface per-device permission flags for wipe,
lock, and clear passcode when available.
* Enrollment status text now shows personal manual enrollments as “On
(manual - personal)”.
* **Bug Fixes**
* Enforced remote wipe/lock (and clear passcode) permissions correctly
for personal devices, including persistence across renewals.
* Host deletion cleanup now removes newly tracked enrollment permission
data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 18:21:26 -05:00
Konstantin Sykulev c226a3feab On var change resend Android certificate templates and managed app configs (#48278)
**Related issue:** Resolves #36681, #48042

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

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Certificate templates and managed Android app configurations now keep
track of referenced variables.
* Variable changes can now trigger automatic re-sending of affected
profiles and app availability updates.
* **Bug Fixes**
* Resend behavior now refreshes certificate templates when related
variable values change.
* Android managed app configurations are re-queued when their variables
are updated.
* **Database**
* Added support for variable tracking on certificate templates and
Android app configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-25 16:27:30 -05:00
Carlo DiCelico 2f9147e685 merge main 2026-06-25 13:57:22 -04:00
Carlo 6630aec5fb Auto-update FMA cron (#48123)
**Related issue:** Resolves #47681

Adds an hourly, Premium-only cron (maintained_apps_auto_update) that
keeps Fleet-maintained apps current. For each FMA-backed active
installer (per team), it fetches the latest manifest, downloads and
caches a newly-published version when the pin allows, and advances the
team's active installer based on the pin state:

- Unpinned (Latest): download/cache the newest published version and
advance the active installer to it.
- Caret pin (^N): advance to the newest version within major N
(downloading it if newly published). Never cross into another major.
  - Literal pin: never advance and never download.

New versions are cached as additional software_installers rows (no
schema change; reuses the existing (global_or_team_id, title_id,
version) index), capped at the two most recent per team for rollback.

  # Checklist for submitter

- [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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops

  ## Testing

  - [x] Added/updated automated tests
  - [x] QA'd all new/changed functionality manually
2026-06-25 13:02:01 -04:00
Jordan MontgomeryandCopilot Autofix powered by AI 07ebe1d836 Check if host is still on script's team before executing batch (#48244)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] 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 is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Scheduled batch script execution now re-checks host team membership at
execution time, skipping any hosts moved to a different team before the
batch runs.
* Added a clear “team mismatch” incompatibility outcome and ensured
incompatible hosts are not queued for execution.
* **Tests**
* Expanded script scheduling tests to cover host-to-team transfers
between scheduling and execution, including updated incompatibility
counts and per-host expectations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-25 12:04:34 -04:00
faff41e41d Fix My device page software sorting by display name (#45836)
**Related issue:** Closes #43673 (remaining issue reported by @getvictor
after PR #44873)

## Changes

The "My device" page / host details software tab sorts software by
`software_titles.name` (often an installer filename) instead of the
custom display name. PR #44873 fixed this for the global
`/software/titles` endpoint but missed the host-specific
`ListHostSoftware` query path.

**Fix:** Add a `LEFT JOIN software_title_display_names` to the outer
query wrapper in `ListHostSoftware`, and update
`hostSoftwareAllowedOrderKeys` to use
`COALESCE(NULLIF(stdn.display_name, ''), name)` so display names are
used for sorting when set.

**1 file changed:** `server/datastore/mysql/software.go`

# Checklist for submitter

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

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Dante Catalfamo <43040593+dantecatalfamo@users.noreply.github.com>
2026-06-24 14:16:57 -04:00
Allen Houchins d66242856f Disambiguate FMAs sharing macOS bundle IDs (#47951)
Fix handling of Fleet-maintained apps that share a macOS bundle
identifier (e.g. Firefox and Firefox ESR). Removed the blind rename from
UpsertMaintainedApp and added ReconcileMaintainedAppSoftwareNames: a
two-pass, idempotent reconciliation that (1) renames titles tied to a
single FMA via installer links and (2) heuristically renames by bundle
identifier only when the identifier maps to exactly one FMA name.
Updated team join logic to prefer matching by installer link and fall
back to bundle identifier, changed GetFMANamesByIdentifier to omit
ambiguous identifiers, added a call to reconcile during the
maintained-apps sync, and extended the datastore interface and mock
accordingly. Added tests and a manifest check for known shared
identifiers, plus a changelog entry.

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

# 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

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))


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Bug Fixes

* Fixed an issue where macOS apps sharing a bundle identifier (e.g.,
Firefox and Firefox ESR) would incorrectly report each other as already
installed and could have their software titles unexpectedly changed.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 13:05:53 -05:00
Scott Gress 20af889c56 Add regression test for #46604 (#46613) 2026-06-24 07:52:25 -07:00
Jonathan Katz b784de80b0 Cancel software install records instead of deleting when an installer is deleted (#48127)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47348
Does two things:
- Removes the software_installers and software_titles joins. These could
be null, but we would still want to create software install records for
these installs even if the installer or title were deleted.
- Changes every case where a host_software_installs is deleted into
setting the canceled flag to 1 on that row.

It also updates some comments. `deletePendingSoftwareInstallsForPolicy`
had a comment that said it should be called _after_ deleting a policy,
but that seems wrong and was not actually reflected in the code even
when it was originally added. It should be called _before_ deleting the
policy so that the siua.policy_id column is still available before it
gets set to null by the FK constraint. Same for
`deletePendingHostScriptExecutionsForPolicy`.

Also removes the `NOTE(mna): ...` comment, because it seems like the
code works as intended and only the comments were wrong.

# 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 is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Prevented a 500 error when late software installation results are
reported after the related installer has been deleted.
* Pending software install entries are now preserved as **canceled**
(instead of being deleted) during installer, policy, and batch update
flows, keeping results consistent.
* Improved correctness of intermediate failure recording and
setup-experience deletion behavior, including distinguishing
**canceled** vs **removed** installs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-24 10:00:27 -04:00
Allen Houchins b55d45806c Align software category name comparison with DB (#47983)
Normalize category name comparisons to match MySQL's utf8mb4_unicode_ci
collation (case-insensitive and ignoring Unicode variation selectors) to
avoid duplicate-entry errors. Add normalizeSoftwareCategoryName and
SoftwareCategoryNamesEqual (server/fleet/software.go) and use them where
categories are deduped (ee/server/service/software_installers.go). Make
batch insert idempotent by using ON DUPLICATE KEY UPDATE in the MySQL
batch insert (server/datastore/mysql/software.go). Add tests for
name-equality behavior and idempotent batch inserts
(server/fleet/software_test.go,
server/datastore/mysql/software_test.go). This prevents collisions
between visually identical emoji forms (e.g. with/without U+FE0F) and
tolerates concurrent/default category inserts.

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

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] 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

- [ ] 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))


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed GitOps runs failing due to software category duplicate-entry
errors when names contain certain Unicode characters (e.g., emoji
variation selectors).

* **Improvements**
* Enhanced software category deduplication to properly handle
Unicode-equivalent names.
* Made batch category insertion operations idempotent to prevent
duplicate-key errors.

* **Tests**
* Added tests for software category idempotency and Unicode character
handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 12:38:58 -05:00
Lucas Manuel Rodriguez 8e94d5e820 Remove unused migration tests (#48074)
This is just removing tests that are never run (always skipped), see:

https://github.com/fleetdm/fleet/blob/7fa7e8f26d108c9421ed7b8e83ffde4468dab6ba/server/datastore/mysql/migrations/tables/migration_test.go#L101-L110

- [X] QA'd all new/changed functionality manually
2026-06-23 13:43:10 -03:00
Jonathan Katz 5c3eacae21 Extend update software installer API to support FMA version pinning (#47808)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #46726 

Updates the `PATCH /api/v1/fleet/software/titles/:id/package` endpoint
to be able to set the pinned version in the `software_title_team_pins`
table and set the active installer if it changed.

`GET /software/titles/:id` returns the pinned version for a title from
that table now.

A few small fixes and updates that are related to this feature but not
strictly in the scope of the subtask are also included (see individual
comments).

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] 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

## New Fleet configuration settings

This setting is already handled by gitops, this PR adds control of it
with the update software installer api

- [ ] 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`
    - TODO in another PR
- [ ] 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


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added version pinning for Fleet-maintained app installers, allowing
teams to lock to specific versions or clear pins to use "Latest"
* Policies automatically repoint to the newly active installer when a
version pin is changed
* Support for semantic version syntax including caret-style version
constraints

* **Tests**
* Added comprehensive integration test for Fleet-maintained app version
pinning workflows and validation rules

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 12:09:23 -04:00
Allen Houchins a972ca21b0 Add "Support" default software category (#47923)
**Related issue:** Resolves #48064

Adds a new default self-service software category, rendered as **🛟
Support**, alongside the existing six defaults (Browsers, Communication,
Developer tools, Productivity, Security, Utilities).

## What changed

**Backend (Go)**
- `server/fleet/software.go` — added `🛟 Support` to
`DefaultSelfServiceCategoryNames` (seeds new fleets) and `"Support": "🛟
Support"` to `LegacySoftwareCategoryNames` (so GitOps/FMA manifests can
reference the non-emoji `Support`).
- New migration `20260619120000_AddSupportSoftwareCategory` — inserts
the global default (`team_id=0`) and backfills every existing fleet.
Timestamps pinned for deterministic schema dumps; `INSERT IGNORE` guards
the `(team_id, name)` unique key.
- `schema.sql` regenerated via `tools/dbutils`.
- `cmd/maintained-apps/main.go` — added `Support` to the FMA validator
allowlist.

**Frontend**
- `frontend/interfaces/software.ts` — added `"Support"` to the
`SoftwareCategory` union.
- `frontend/pages/hosts/details/cards/Software/SelfService/helpers.ts` —
added `{ label: "🛟 Support", value: "Support" }` to the fallback list.

**Docs**
- `docs/Configuration/yaml-files.md` — documented `Support` as a
supported GitOps category.

## Note on sort order
`ListSoftwareCategories` does `ORDER BY name` under
`utf8mb4_unicode_ci`, which sorts by the word after the (ignorable)
emoji. `🛟 Support` is therefore placed between `🔐 Security` and `🛠️
Utilities`.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`.

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

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

Verified against a dockerized MySQL:
- Migration test `TestUp_20260619120000`
- `TestSoftware/SoftwareCategoryCRUD` (order-sensitive assertion)
- `TestSelfServiceCategoriesCRUD` + `TestDeviceSelfServiceCategories`
integration tests
- `cmd/maintained-apps` tests, ee categories test, `go vet`, `make
lint-go-incremental` (0 issues)
- `tools/dbutils` schema regeneration matches

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

## New Fleet configuration settings

- [x] Verified the setting is documented (GitOps `categories` supported
values in `docs/Configuration/yaml-files.md`).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Introduced the "🛟 Support" category as a new self-service software
classification option. Users can now better organize support-related
applications within their software catalog. The category is available
globally across all teams, providing improved organization and discovery
capabilities for support applications alongside utilities and other
existing software categories.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-23 09:41:08 -05:00
Carlo 47db1a63bd Fix host software details dropping pending installs/uninstalls (#47954)
**Related issue:** Related to #47839

Follow-up to #47949 (same root cause, same issue). That PR fixed the
OR-dominance drop out in the software *title summary* queries; this
applies the same fix to the four per-host queries behind a host's
*software details* page.

A host with more than one queued install or uninstall for the same
installer, VPP app, or in-house app could disappear from its software
details page: the old self anti-join's `(priority < OR created_at >)`
predicate let two rows eliminate each other, so neither survived. This
rewrites those four queries (`hostSoftwareInstalls`,
`hostSoftwareUninstalls`, `hostVPPInstalls`, `hostInHouseInstalls`) to
rank with `ROW_NUMBER()` and keep one row per item. No performance
change — these are per-host queries.

  # Checklist for submitter

- [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] `SELECT *` is avoided and SQL injection is prevented (named
placeholders used for all values in the modified statements).

  ## Testing

  - [x] Added/updated automated tests
- [x] 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)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed an issue where installers, VPP apps, and in-house apps could
disappear from a host's software details page when multiple install or
uninstall actions were queued for the same item.

* **Tests**
  * Added regression tests to prevent this issue from recurring.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 10:34:37 -04:00
Jordan Montgomery cf32a02fbc Bump migrations that conflict with v4.87 (#48002)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves migration failure dsicussed here:
https://fleetdm.slack.com/archives/C019WG4GH0A/p1782131309693279

20260610172952_AddHasACMEPayloadToHostMDMAppleProfiles.go was backported
to v4.87 but had a newer timestamp so would have conflicted with
migrations merged in the frist few days of v4.89 development

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

No changes file as this is ultimately an `unreleased-bug`
- [ ] 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] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added support for policy gating in setup workflows.
  * Added support for BYOD fleet and enrollment tracking capabilities.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 09:17:04 -04:00
Carlo 3f5b58888f Fix software title page timeout for large upcoming-activity backlogs (#47949)
**Related issue:** Resolves #47839

The software title details page (`GET
/api/v1/fleet/software/titles/{id}`) could take minutes to load and
return a 500 or 502 when an installer, VPP app, or in-house app was
scoped to many hosts.

The cause was the status summary query. To find each host's most recent
pending activity, it joined `upcoming_activities` to itself, and an `OR`
in the join condition stopped MySQL from using an index. The query got
much slower as the pending backlog grew.

This rewrites the query in all three summary functions
(`GetSummaryHostSoftwareInstalls`, `GetSummaryHostVPPAppInstalls`,
`GetSummaryHostInHouseAppInstalls`) to use a `ROW_NUMBER()` window
function. It filters to the installer or app first, then picks each
host's most recent activity, which removes the self-join. It also fixes
a related bug where the old `OR` condition could drop a host from the
counts.

Verified live against a 6,000-host backlog. The page went from ~12.7s to
~1.1s with identical status counts.

  # Checklist for submitter

- [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-chan
  ges.md#changes-files) for more information.
- [x] `SELECT *` is avoided and SQL injection is prevented (named
placeholders used for all values in the modified statements).

  ## Testing

  - [x] Added/updated automated tests
- [x] 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
  
  ## Before (reproduction):
  

[before.webm](https://github.com/user-attachments/assets/f03154d5-6062-42e3-81d3-ce33b0809145)

  ## After (fix):


[after.webm](https://github.com/user-attachments/assets/b6d1ce53-7778-4023-84b7-56c49d846649)



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Fixed software title details pages timing out for installers, VPP
apps, and in-house apps when hosts have large backlogs of pending
activities.
* Improved pending software install status selection to prevent hosts
from being dropped or counted inconsistently when multiple upcoming
activities exist.

* **Tests**
* Added regression coverage for upcoming-per-host counting without
dropouts when multiple queued activity entries share the same host and
app context.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 17:01:05 -04:00
Scott Gress fcd99a1842 Fix GenerateHostStatusStatistics query so that hosts enrolled chart is accurate (#47791)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47605

# Details

The queries in GenerateHostStatusStatistics (which feeds the
host_summary API) were incorrectly filtering out hosts that had been
removed from ABM from the denominator (i.e. "total hosts") count, while
keeping them in the per-platform counts. This PR fixes the query so that
the sum of the platforms matches the total.

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

## Testing

- [X] Added/updated automated tests
- [X] QA'd all new/changed functionality manually
**Before**
<img width="499" height="420" alt="image"
src="https://github.com/user-attachments/assets/9b8d22bc-1dac-49e6-9d2f-8ce9ffda5f17"
/>

**After**
<img width="501" height="420" alt="image"
src="https://github.com/user-attachments/assets/fe3705b7-9a90-4d9c-a9dc-b04b2905f3fb"
/>




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Fixed an issue causing incorrect platform percentage breakdowns in the
"Hosts enrolled" dashboard chart.
* Corrected host status calculations to ensure accurate total and
per-platform host counts by properly handling device enrollment
assignment records.

* **Tests**
* Added regression tests to validate the correct handling of device
enrollment records in host status calculations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 11:14:24 -05:00
Juan Fernandez 5368b99636 Policy status page: automation activity history, reset endpoint, and details UI
Resolves #38670 

Adds the backend and frontend for the Policy status page — a historical, per-host view of policy automation outcomes — plus a way to reset a policy's results.
2026-06-19 12:13:36 -04:00
Jonathan Katz ad52492c78 Undo rename from utilities to support (#47881)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #

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

- [x] QA'd all new/changed functionality manually
Tested with fleet maintained apps and VPP in UI and gitops 

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
N/A since this is just editing the existing migration
- [ ] 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:

- [x] 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)
- [x] 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)
- newly added custom category, or default category, was cleared if was
not in the yaml file
- [ ] Verified that any relevant UI is disabled when GitOps mode is
enabled


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Corrected the “Productivity” category emoji/name and the “Utilities”
category emoji/name across the system for consistent display and
behavior.
* **Tests**
* Updated unit, integration, and handler tests to expect the corrected
category strings and delete-button labels.
* **Chores**
* Refreshed database seed data and migration/test expectations to align
default and per-team category names, preserving IDs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-19 10:12:10 -04:00
Konstantin Sykulev b32ceb72e1 Added variables in Android configuration profiles (#47750)
**Related issue:** Resolves
https://github.com/fleetdm/fleet/issues/41968

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

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added support for using Fleet variables (`$FLEET_VAR_HOST_*`) in
Android configuration profiles, enabling per-host dynamic value
substitution during deployment.

* **Improvements**
* Strengthened Android profile validation to reject unsupported Fleet
variables and prevent invalid placements (for example, using variables
in JSON object keys or non-string fields).
* Enhanced deployment behavior when variables can’t be resolved for a
host, marking affected profiles as delivery failed and avoiding partial
policy application.
* Improved Android per-host rollout by applying installs in staggered
batches for smoother throughput.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-18 13:53:28 -05:00
Magnus Jensen 8d26d298e2 fixed update teams not updating appconfig, and team delete not cleaning up appconfig (#47826)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Unreleased bugs while going through test plan

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Apple Business Manager (ABM) token team assignments now stay
synchronized when team defaults change across BYOD, macOS, iOS, and
iPadOS.
* “No team” selections are now saved as cleared (empty) assignments for
cleaner configuration output.
* Improved ABM token cleanup during team deletion to remove references
tied to the deleted team.
* **Tests**
* Added/extended coverage for ABM token team update behavior (including
invalid team handling and nil inputs) and deletion cleanup.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-18 18:23:02 +02:00
Victor Lyuboslavsky a2757a1d7c Don't require end user auth on orbit re-enrollment (#46300) (#47740)
Windows and Linux hosts that had already orbit-enrolled were prompted
for end user authentication (an SSO browser tab) when fleetd re-enrolled
after a service restart, node key file loss, or osquery DB rebuild.
Hosts enrolled before EUA was enabled have no host_mdm_idp_accounts row,
so the service-layer EUA gate treated every re-enroll like a brand-new
device.

Before returning END_USER_AUTH_REQUIRED, EnrollOrbit now checks whether
a host matching the enrollment identifiers already exists and previously
held an orbit node key (HostPreviouslyOrbitEnrolled, reusing
matchHostDuringEnrollment's semantics). If so, the re-enroll proceeds
without prompting. Genuinely new devices, and hosts moved to a different
Fleet server, are still gated.

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

# 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 is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

**Bug Fixes**
* Fixed unnecessary end-user authentication prompts for Windows and
Linux hosts during fleetd re-enrollment after a service restart.
Previously enrolled devices can now re-enroll without being prompted for
SSO authentication, while new devices still require the appropriate
authentication.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-18 16:22:49 +01:00
Jonathan Katz 75a0a03732 Pin Fleet-maintained apps - migration (#47778)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47678 

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [ ] 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

- [ ] 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 is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Teams can now pin software titles to designate preferred versions for
tracking and management.

* **Chores**
  * Database schema updated to support software title pinning.
  * Updated team deletion procedures to handle associated pins.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-17 15:29:02 -04:00
RachelElysia f677e904ab Self-service categories: bug fixes on empty state, fleet-free layout, dropped fleet_id (#47779) 2026-06-17 15:04:39 -04:00
Juan Fernandez 042b0b0498 Prevent in-flight carves from being wrongly expired by S3 cleanup
Relates to #43045

Skip carves whose upload has not completed (BlocksComplete) so cleanup
only reconciles carves that should already have a listable object.
2026-06-17 12:33:30 -04:00
Konstantin Sykulev ead9d40293 Added FLEET_VAR_HOST to android configs (#47642)
**Related issue:** Resolves #45353

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

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added support for `$FLEET_VAR_HOST_*` variables in Android managed app
configurations, including host UUID, hardware serial, platform, and
end-user IdP details.

* **Improvements**
* Android app configurations are now validated to reject unsupported
Fleet variables.
* Fleet variables are substituted with real per-host values during
Android app configuration deployment, including batch/GitOps and
host-specific workflows.

* **Tests**
* Added unit and integration coverage for supported/unsupported
variables, substitution behavior, and JSON escaping.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-17 11:10:03 -05:00
Magnus Jensen fe4d74edc2 Join MDM for missing status for non osquery devices (#47672)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #46243 

# 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.
- [x] Timeouts are implemented and retries are limited to avoid infinite
loops
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Fixed incorrect reporting of iOS, iPadOS, and Android hosts as missing
(including MIA and missing-over-30-days counts).
* Host status filtering and dashboard cards now correctly fall back to
Apple MDM activity when OSQuery “last seen” data is unavailable,
preventing recently active devices from being flagged.
* **Tests**
* Added coverage to verify iOS/iPadOS hosts aren’t treated as missing
when Apple MDM “last seen” is recent, and are flagged once it becomes
stale.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-17 16:20:54 +02:00