Commit Graph
2576 Commits
Author SHA1 Message Date
Scott GressandCopilot Autofix powered by AI 4daed869ad Add API param linter (#44045)
Adds a linter to ensure we don't add new instances of `team` or `query`
in API params. This will be used incrementally, but this PR also adds
`nolint` directives to places that still have these terms, both to avoid
false-positives later and to help with full migration away from these
terms in in Fleet 5.

Example:

```
server/fleet/campaigns.go:51:16: json tag "team_id": uses deprecated "team"/"teams" — use "fleet"/"fleets" instead (apiparamcheck)
        Team  *uint   `json:"team_id,omitempty"`
```

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

* **New Features**
* Added a new static analyzer (apiparamcheck) to flag deprecated API
parameter names ("team/teams") and improper usages of "query/queries".

* **Chores**
  * Integrated the new check into CI tooling and configuration.
  * Added analyzer tests and plugin registration.
* Applied targeted lint-suppression annotations across code and tests
where legacy parameter names must remain.
<!-- 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-05-11 08:46:02 -05:00
Jonathan Katz d439cb1690 Fix missing deleted_policy activity for auto-cleaned patch policies (#45045)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44286

Unset `patch_software_title_id` rather than deleting the policy in
`BatchSetSoftwareInstallers`, so the orphaned policy gets picked up by
the `policiesToDelete` loop in `server/service/client.go:3121`. As a
result, the `deleted_policy` activity is now created properly, and
gitops dry/real runs also report the deletion:

```
dry run:
[-] would've deleted policy macOS - 010 Editor up to date
[-] would've deleted 1 policy

real run:
[-] deleting policy macOS - 010 Editor up to date
[-] deleting 1 policy
[-] deleted 1 policy
```

# 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**
* Fixed missing deletion activity logs when patch policies are removed
via GitOps so policy deletion events are now recorded.

* **Behavior Changes**
* Batch-updating installers now retains obsolete patch policies but
clears their patch installer reference instead of deleting the policy.

* **Tests**
* Added integration coverage to verify deletion activities are emitted
and installer batch behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-09 00:33:00 -04:00
Magnus Jensen ea3513a1e7 always assign profile to missing devices due to replica lag (#45008)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44980

# 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
- [ ] QA'd all new/changed functionality manually (Not, outside of tests
due to exercising replica lag is difficult)

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

## Summary by CodeRabbit

## Release Notes

* **Bug Fixes**
* Improved reliability of device profile assignment by ensuring all
devices receive profiles consistently, even when replica lag affects
device synchronization from Device Enrollment Program services.

* **Tests**
* Added test coverage validating device profile assignment behavior
under replica lag scenarios.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-08 15:33:36 +02:00
Magnus Jensen 034691966f check push cert staleness after 5 minutes of in-memory cache time (#44919)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44376 

I opted for an in-memory cache here, as it's not a critical cache piece,
we are fine with the cache being different times on different containers
(just means some might rotate to the correct cert faster than 5
minutes).
It's also a small piece of work, rather than pulling in redis etc.

Verified that it now logs, if the cert is stale after a 5 minute
in-memory cache.
```
ts=2026-05-07T11:30:08Z level=info msg="push certificate is stale after re-checking" topic=com.apple.mgmt.External.34c4a9b0-6501-4ce6-afc6-32eac6420ee7 staleToken="\x90C\xe4K\xc6a\x97\xb5?\x1b\x9a\x04'\xe7b\x8d" newHash=".fP\xc7O7\xab\xab\x9d\x92\xd5#\xe4u\xe0\xf6"
ts=2026-05-07T11:30:08Z level=info component=apple-mdm-push msg="retrieved push cert" topic=com.apple.mgmt.External.34c4a9b0-6501-4ce6-afc6-32eac6420ee7
```

# 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**
* APNs push certificates now refresh in-memory when rotated; staleness
is detected using certificate checksums with a short grace window.

* **Tests**
* Added tests for certificate retrieval, staleness detection/refresh
behavior, and push-cert storage error handling.

* **Documentation**
* Updated docs to describe the APNs push-certificate refresh and
staleness behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-08 11:02:22 +02:00
Sharon Katz f3f830bd9d Fix GitOps failure when moving labels from global to fleet scope (#44983)
Closes #44950

## Local reproduction

Reproduced locally using a MySQL integration test against the local test
database. The test simulates the exact GitOps scenario from the issue:

1. Create a label and associate it with an MDM profile
2. Delete the label (FK `ON DELETE SET NULL` sets `label_id = NULL`)
3. Create a new label with the **same name** (simulates moving from
global to fleet scope)
4. Call `batchSetProfileLabelAssociationsDB` with the profile
referencing the new label

**Before fix** (code from `main`, unfixed):
```
$ MYSQL_TEST=1 go test -run "TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated" -v -count=1 ./server/datastore/mysql/...

=== RUN   TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_darwin
    Error: selecting existing profile labels: sql: Scan error on column index 1, name "label_id": converting NULL to uint is unsupported
=== RUN   TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_windows
    Error: selecting existing profile labels: sql: Scan error on column index 1, name "label_id": converting NULL to uint is unsupported
--- FAIL: TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_darwin (0.02s)
--- FAIL: TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_windows (0.02s)
FAIL
```

**After fix:**
```
$ MYSQL_TEST=1 go test -run "TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated" -v -count=1 ./server/datastore/mysql/...

=== RUN   TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_windows
=== RUN   TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_darwin
--- PASS: TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_windows (0.03s)
--- PASS: TestMDMShared/TestBatchSetProfileLabelAssociations/same_label_name_recreated_after_deletion_darwin (0.03s)
PASS
ok  github.com/fleetdm/fleet/v4/server/datastore/mysql  2.761s
```

## Code changes

When a label is deleted, MySQL's `ON DELETE SET NULL` foreign key
constraint automatically sets `label_id = NULL` in the profile-label
association row. The Go code then crashes trying to scan that NULL into
a `uint` field.

- **`server/datastore/mysql/mdm.go`** — Added `COALESCE(label_id, 0)` to
the SELECT in `batchSetProfileLabelAssociationsDB`, so that NULL
`label_id` values are returned as 0 instead of causing a scan error when
Go tries to read NULL into a `uint`.
- **`server/datastore/mysql/apple_mdm.go`** — Same `COALESCE(label_id,
0)` fix in `batchSetDeclarationLabelAssociationsDB`. Also added `OR
label_id IS NULL` to the DELETE statement to clean up broken rows,
matching the profile labels behavior from #42637.

Other queries in the same codebase (e.g.,
`listProfileLabelsForProfiles`) already use `COALESCE(label_id, 0)` —
these two were missed.

## Testing

- `same_label_name_recreated_after_deletion_{darwin,windows}` —
reproduces the exact bug: associates a profile with a label, deletes the
label (NULL label_id), creates a new label with the same name, and
verifies `batchSetProfileLabelAssociationsDB` succeeds, the broken row
is cleaned up, and the correct label association exists
- Full MDM test suite passes: `MYSQL_TEST=1 go test -run "TestMDM"
./server/datastore/mysql/...` (76s)
- `make lint-go-incremental` passes
2026-05-08 07:49:23 +02:00
Konstantin SykulevandCopilot Autofix powered by AI e15f37d4e0 Optimize OSV vulnerability scanning (#44684)
**Related issue:** Resolves #44391

# 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


I tested this locally 
number of ubuntu hosts: 6,252
average software per host: 2,302
distinct software items: 61,213
host_software rows: 14.4M
generates software_cve rows 305,826
OS sub-versions: 25

The time before my optimization **10m53s** down to **4m26s** the
optimization.

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

* **Chores**
* Optimized OSV vulnerability scanning to aggregate work by OS version
and batch lookups, reducing redundant queries for faster scans.

* **Refactor**
* Restructured scanning flow to process OS versions in batched chunks
with clearer logging and early exits when no work is required.

* **Tests**
* Added tests for querying, batching, source filtering, deduplication,
and empty-input behaviors.
<!-- 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-05-07 22:21:22 -06:00
Tim Lee dc0c7bd72f Recover stuck SCEP managed-cert state via matcher extension (#44691) 2026-05-07 11:52:25 -06:00
Nico d8a1ffae81 Clear stale broken label rows on profile batch upsert (#44847)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #42637

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

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

### Reproduction steps:

- Created Label X and Label Y as manual labels in the UI.
- Applied gitops referencing the labels. The specified profile
referenced Label X:

```yaml
macos_settings:
    custom_settings:
      - path: ../repro-42637-profile.mobileconfig
        labels_exclude_any:
          - "Repro Label X 42637"
```

- Manually ran a SQL query to update `label_id` to NULL.

<img width="712" height="46" alt="Screenshot 2026-05-06 at 6 19 51 PM"
src="https://github.com/user-attachments/assets/32f386c7-adf3-48e8-adee-03102831e556"
/>


- Re-ran gitops referencing Label Y in the profile config.

```yaml
macos_settings:
    custom_settings:
      - path: ../repro-42637-profile.mobileconfig
        labels_include_any:
          - "Repro Label Y 42637"
```

- Old row was preserved AND a new one was created (association to Label
Y):

<img width="709" height="68" alt="Screenshot 2026-05-06 at 6 22 07 PM"
src="https://github.com/user-attachments/assets/fe2c4644-eb95-45a0-a582-994ad88e45be"
/>

### Testing steps

- Re-built fleetctl with the fix applied and re-ran gitops, still
referencing Label Y for the profile.
- Confirmed the orphan row was deleted.

<img width="740" height="212" alt="Screenshot 2026-05-06 at 6 24 43 PM"
src="https://github.com/user-attachments/assets/da9e9461-c352-4266-80b8-625a98e055ec"
/>


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

## Summary by CodeRabbit

## Bug Fixes
* Fixed an issue where MDM configuration profiles would remain enforced
on hosts after their associated labels were deleted during fleetctl
gitops apply operations. Label associations are now properly cleared
when profiles are reapplied with updated targeting.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-07 16:10:15 +02:00
Scott Gress 684becade8 Allow disabling chart datasets: backend (#44769)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** For #44077 

# Details

This PR implements enforcement of the "disable dataset" feature.  

When a dataset is disabled globally, we:

* Stop collecting all data for that dataset (the `Collect` method for
that dataset is not called in the cron job)
* Remove all previously-collected data for the dataset via an
asynchronous job

When a dataset is disabled for one or more fleets, we:

* Provide the list of disabled fleets as an argument to each dataset's
`Collect` method. Each dataset is responsible for filtering out hosts in
the most efficient way possible
* Scrub the data for the relevant datasets using a bitmask, so that all
hosts from the disabled fleets are removed from the data. This is done
via an asynchronous job.

# 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.
n/a, unreleased

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

  ### Prerequisites / Test Setup

- [ ] Fleet running with at least 3 teams (call them T1, T2, T3) and ≥3
hosts in each, plus ≥2 hosts with no team
- [ ] At least one host on each team has reported recent uptime (within
the bucket window)
- [ ] At least one host in each team is affected by a tracked CVE (so
`host_scd_data` for `dataset='cve'` will have non-empty bitmaps)
- [ ] AppConfig: both `features.historical_data.uptime` and
`features.historical_data.vulnerabilities` start as `true`; same for
every team
- [ ] Let the collection cron run at least one full tick to populate
baseline rows in `host_scd_data` for both `uptime` and `cve`
- [ ] Note the current row count per dataset: `SELECT dataset, COUNT(*)
FROM host_scd_data GROUP BY dataset;`

  ---

  ### 1. Cron Skips Globally-Disabled Datasets

  #### 1.1 Global disable of `uptime`

- [x] Disable globally: `PATCH /api/v1/fleet/config` with
`features.historical_data.uptime = false`
- [x] Verify activity feed shows `disabled_historical_dataset` for
`uptime` (existing behavior)
- [x] Wait for next collection tick (or trigger it via fleetctl debug if
available)
  - [x] Confirm **no new rows** appear for `dataset='uptime'`:
`SELECT MAX(valid_from) FROM host_scd_data WHERE dataset='uptime';`
        should not advance after the disable
- [x] Confirm cron still writes `cve` rows on the same tick (per-dataset
isolation)
  - [x] Re-enable: PATCH `historical_data.uptime = true`
  - [x] Verify next tick resumes writing `uptime` rows

  #### 1.2 Global disable of `vulnerabilities`

  - [x] Repeat 1.1 with `features.historical_data.vulnerabilities`
  - [x] Confirm `cve` writes stop, `uptime` continues

  #### 1.3 Both disabled globally

  - [x] Disable both globally
  - [x] Confirm cron tick produces zero new rows for either dataset
  - [x] Confirm cron does not error or get stuck
  - [x] Re-enable both

  ---

  ### 2. Per-Fleet Disable — Cron Filters at SQL

  #### 2.1 Single team disabled for one dataset

  - [x] Disable uptime for T1 only: PATCH team T1 with
        `features.historical_data.uptime = false`
- [x] Verify scoped `disabled_historical_dataset` activity emitted for
T1
  - [x] Wait for next cron tick / trigger cron
- [x] Pick a host known to be in T1 (call it `H_T1`); confirm its bit is
NOT set in any `uptime` row written *after* the disable by filtering the
chart to that host
- [x] Pick a host in T2 (`H_T2`); confirm its bit IS still set in the
same rows (T2 is not disabled)
- [x] Pick a no-team host (`H_none`); confirm its bit IS still set
(no-team hosts follow the global value)

  #### 2.2 Same fleet, different dataset

- [x] With T1's uptime disabled, confirm T1's hosts ARE still written
into `cve` rows on subsequent ticks (per-dataset isolation)

  #### 2.3 All teams disabled, global on, no-team hosts

  - [x] Disable uptime on every team (T1, T2, T3)
- [x] Confirm next tick still writes a row containing only no-team
hosts' bits (global is on, no-team hosts always count)
  - [x] Re-enable uptime on all teams

  ---

  ### 3. Global Scrub — DELETE

  #### 3.1 Successful global scrub

  - [x] Note baseline:
        `SELECT COUNT(*) FROM host_scd_data WHERE dataset='uptime';`
        (should be > 5000 to exercise the loop; if not, manually
        insert filler rows or run multiple cron ticks)
  - [x] Disable uptime globally via the API
  - [x] Wait for the worker to pick up the scrub / trigger the job
  - [x] Confirm the count drops to 0:
        `SELECT COUNT(*) FROM host_scd_data WHERE dataset='uptime';`
  - [x] Confirm rows for **other datasets** are untouched
  - [ ] Test again but disable via GitOps

  ---

  ### 4. Per-Fleet Scrub — ANDNOT

  #### 4.1 Single-fleet scrub clears bits

  - [x] Identify hosts in T1 and record their IDs (call this set `S`)
- [x] Pre-disable, confirm at least one `host_scd_data` row for
`dataset='uptime'` has bits set at positions in `S` by filtering the
chart to those hosts
  - [x] Disable uptime on T1 only, via the API
  - [x] Wait for the scrub to run / trigger it
- [x] Confirm: every existing row for `dataset='uptime'` now has NO bits
set at any position in `S`. Spot-check by filtering the chart to those
hosts
- [x] Confirm rows for `dataset='cve'` (different dataset) are untouched
  - [x] Confirm bits for hosts in T2/T3 (not disabled) are still set
  - [x] Run test again but disable via GitOps

  #### 4.2 Multi-fleet scrub via GitOps batch

- [x] Apply a GitOps spec that flips cve to false on T1 and T3 in a
single apply
  - [x] Wait for scrub(s) to complete
- [x] Confirm bits for the union of T1∪T2 hosts are cleared from every
row of `dataset='cve'`
  - [x] Confirm T2 hosts' bits remain set

  ---

  ### 5. Activity Feed Cross-Check

  - [x] Each global flip emits exactly one `disabled_historical_dataset`
        activity (existing behavior, unchanged)
  - [x] Each per-team flip emits one scoped activity with the team's
        ID and name
  - [x] PATCH submitting unchanged values emits **no** activity and
        causes **no** scrub (no `host_scd_data` data change observed
        after the cron tick)
  - [x] No new "scrub completed" or "scrub started" activity is
        emitted (out of scope for v1)
  - [x] Re-enable flips emit `enabled_historical_dataset` activities
        and do NOT emit any scrub-related activity

  ---

  ### 6. Regression Spot Checks

  - [x] With everything enabled (default), the chart UI renders the
        same data as before this change (no behavior change in the
        "all on" case)
  - [x] AppConfig YAML round-trip (`fleetctl apply`) is benign:
        applying the unchanged config produces no scrub jobs and no
        activities
  - [x] GitOps apply with `historical_data` omitted from team specs
        defaults to `true` (per the gitops-api change) and does not
        trigger spurious scrubs
  - [x] After a full disable+scrub of cve, the `host_scd_data` table
        has no `dataset='cve'` rows; the chart UI for "vulnerable
        hosts over time" shows an empty/zero state without errors

  ---


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

* **New Features**
* Chart collection now supports per-dataset scoping and honors
team-level disables; new scrub jobs are registered and worker handlers
added.
* New dataset scrub operations: global and fleet-scoped scrubs; scrubs
can be enqueued and are deduplicated to avoid duplicate pending jobs.
Historical-data changes enqueue scrubs after save (errors logged,
non-blocking).
* **Tests**
* Added unit tests for scope resolution, scrub enqueue/dedup behavior,
scrub workers, scrub application, and low-level blob scrub logic.
* **Documentation**
  * Added OpenSpec metadata for the chart scrub change.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-07 08:52:35 -05:00
Jordan Montgomery 4910c450a4 43887 MLAPR backend (#44726)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43887

Adds the password rotation state machine for macOS local admin accounts.
Changes file covered in prior PR

# 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

* **New Features**
* Automatic macOS managed-local-account password rotation (5‑minute
scheduler) with queued SetAutoAdminPassword device commands
* Manual rotation API: POST /hosts/{id}/managed_local_account/rotate
(returns 204)
* API now reports auto-rotation timing and pending-rotation state
(auto_rotate_at, pending_rotation)
  * Activity records for successful and failed rotations

* **Behavior Changes**
* Password availability is based on stored encrypted password (broader
than before)
  * Rotate-while-in-flight is rejected to prevent duplicate rotations

* **Tests**
* New unit and integration tests for rotation flows, cron behavior, and
failure paths
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-07 06:36:30 -04:00
Magnus JensenandLucas Manuel Rodriguez d3775bda86 Check device auth token individual before querying host tables on auth (#44817)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
Resolves #44816.

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

## Summary by CodeRabbit

* **Performance**
* Improved device authentication efficiency by optimizing token
resolution, reducing database load for both valid and invalid token
scenarios in Fleet Desktop.

* **Tests**
* Added comprehensive test coverage for device authentication fast-fail
scenarios, including handling of non-existent tokens, expired tokens,
and edge cases.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
2026-05-06 16:55:35 -03:00
Victor Lyuboslavsky c79d33a3a6 Add support for SAN in Android certificate templates. (#44690)
2/3rds of this PR is OpenSpec and tests. Use OpenSpec files as a
reference (if needed). They're there to help the review, and not to be a
review surface themselves.

- Backend implementation for `subject_alternative_name` in certificate
templates.
- Includes schema migration, variable expansion, GitOps support.
- Limits SAN types to `DNS`, `EMAIL`, `UPN`, `IP`, and `URI`.

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

# 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

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.

## New Fleet configuration settings

- [x] Verified that the setting is exported via `fleetctl
generate-gitops`
- [x] 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)
- [ ] 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**
* Android certificate templates support Subject Alternative Name (SAN)
with validation (DNS, EMAIL, UPN, IP, URI), Fleet-variable substitution,
runtime expansion, and delivery; SAN use is gated by Premium license
  * GitOps now validates and includes SAN in Android certificate flows

* **Chores**
  * Database schema updated to store SAN on certificate templates
  * Changelog entry added

* **Tests**
* Added unit and end-to-end tests covering SAN validation, variable
expansion, and GitOps behavior
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 14:43:09 -05:00
d38163db94 Setup experience for Windows. (#44306)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43859 

This PR brings the Windows Autopilot setup experience to parity with
macOS DEP. Windows hosts that enroll
through Autopilot now coordinate with Fleet during the OOBE Enrollment
Status Page (ESP), so admin-defined
software installs run while the device is still waiting at the ESP
screen, before the user can sign in. Fleet
holds the device on the ESP until profiles and setup-experience software
all reach a terminal state, then
either releases the device to login or blocks it on a Reset PC failure
screen. A new team-level setting
controls the policy: when enabled, any critical software install failure
during ESP blocks the device with a
software-specific error message; when disabled, the device releases
regardless of install outcomes (best
effort). A pure 3-hour timeout also forces a finalize, with a
timeout-specific error message on the block
screen. The setting is premium-only and rejected when Windows MDM is not
configured.

Beyond the gating itself, the PR adds the supporting machinery:
orbit-driven setup-experience initialization
on Windows so installs are enqueued at the right moment,
defense-in-depth cancellation of pending software
installs (both queue rows and status rows) whenever the device is going
to block or time out, idempotent
re-enrollment cleanup so a device that resets and re-enrolls during ESP
starts from a clean state. Internally, finalize is structured so a
transient failure at any step (cancel, persist, or the state-machine
transition) leaves the device retriable on the next management session
rather than permanently stuck on "Working on it...". The behavior is
exercised by example-based tests, a property-based test that randomly
samples the wait/block/release decision matrix, and manual VM testing
across Autopilot edge cases.

<img width="1184" height="776" alt="image"
src="https://github.com/user-attachments/assets/5e48660d-235d-40bd-80b6-f8591c579279"
/>

# Checklist for submitter

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

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

* **Bug Fixes**
* Re-enrollment now clears stale setup-experience results and pending
activities so devices aren’t blocked by old work.
* Insert operations tolerate missing enrollments and return clear
not-found behavior.

* **New Features**
* ESP finalization waits for software installation results and can block
or release based on configurable “require all” behavior; blocking
cancels pending steps and shows prioritized error text.
  * Finalization persists batched final commands for consistent retries.
* Orbit config exposes setup-experience notification for pending/active
Windows hosts.

* **Tests**
* Expanded coverage for ESP flows, datastore awaiting-configuration,
Orbit config, and re-enrollment cascades.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Sykulev <konst@sykulev.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-06 14:26:58 -05:00
Marko LisicaandCarlo e988dd4756 Support VPP apps from non-US App Store regions (#44368)
**Related issue:** Resolves #43846

---------

Co-authored-by: Carlo <1778532+cdcme@users.noreply.github.com>
2026-05-06 12:01:07 -04:00
Magnus Jensen 7e8994d6db CSAH: clear state on ABM re-enrollment (#44722)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43945 

# 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

* **New Features**
* Configurable option to preserve past host activity history during
Apple Business Manager re-enrollment.
* Re-enrollment can perform a targeted reset of host MDM state and
upcoming activities when preservation is disabled.
* Token-update flow now conditionally triggers the reenrollment reset
based on device/migration state and the preserve flag.

* **Bug Fixes**
* Host vitals and host-scoped data are cleared on ABM re-enrollment when
preservation is disabled.
* Reset is skipped during specific migration scenarios to avoid
disruption.

* **Tests**
* Added tests and mocks validating reset behavior, the preservation
flag, and migration-based skip logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-06 09:57:58 +02:00
Sharon Katz 1c522097d0 Fix missing GitOps label validation for invalid field combinations (#44410)
**Related issue:** Closes #34229

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

---

`fleetctl gitops` silently accepted labels with invalid parameter
combinations (e.g. manual labels with query/criteria/platform). Added
per-type field validation in a centralized
`fleet.ValidateLabelMembershipFields` function, called from the GitOps
parser, `ApplyLabelSpecs`, and `NewLabel`.

| Type | Allowed | Now rejects |
|------|---------|-------------|
| `manual` | `name`, `description`, `hosts` | `query`, `criteria`,
`platform` |
| `dynamic` | `name`, `description`, `query`, `platform` | `criteria`,
`hosts`; validates platform value |
| `host_vitals` | `name`, `description`, `criteria` | `query`,
`platform`, `hosts` |

### Automated tests

- `TestLabelInvalidFieldCombinations` in `pkg/spec/gitops_test.go` — 17
sub-tests covering every invalid combination per label type, plus 3
valid happy-path cases.
- `TestNewLabelFieldValidation` in `server/service/labels_test.go` — 4
cases for NewLabel validation.
- `TestApplyLabelSpecsManualLabelNilHosts` — 10 sub-cases for
ApplyLabelSpecs field validation.
- `TestWhenCreatingNewLabelsPlatformIsValidated` — platform validation
across NewLabel and ApplyLabelSpecs.

All existing `pkg/spec` and `server/service` label tests pass.

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

* **Bug Fixes**
* Labels now reject invalid field combinations for manual, dynamic, and
host_vitals types with clear error responses instead of failing
silently.
* **Tests**
* Added comprehensive tests covering valid and invalid label
configurations across membership types.
* **Documentation**
  * Changelog entry describing the behavioral fix.  
* **Chores**
* Removed an unnecessary platform constraint from a label configuration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---

### Manual test results

Ran against a local Fleet server with the built binary.

**API - NewLabel (POST /api/latest/fleet/labels)**

| Test | Input | Expected | Result |
|------|-------|----------|--------|
| 1 | manual + platform=darwin | 422, field=`platform` | PASS |
| 2 | dynamic + platform=invalidplatform | 422, field=`platform` | PASS
|
| 3 | dynamic + platform=darwin + query | 200 | PASS |
| 4 | manual (no platform) | 200 | PASS |
| 5 | host_vitals + platform=darwin | 422, field=`platform` | PASS |
| 6 | dynamic + whitespace-only query | 422, field=`query` | PASS |

**API - ApplyLabelSpecs (POST /api/latest/fleet/spec/labels)**

| Test | Input | Expected | Result |
|------|-------|----------|--------|
| 7 | manual + query | 422, field=`query` | PASS |
| 8 | dynamic + hosts | 422, field=`hosts` | PASS |
| 9 | valid dynamic | 200 | PASS |

**Round-trip: get labels --yaml then apply**

| Test | Scenario | Result |
|------|----------|--------|
| 10 | Legacy manual label with platform=darwin in DB | Platform
stripped from YAML, re-apply succeeds — PASS |
| 11 | Dynamic label with platform=darwin | Platform preserved in YAML,
re-apply succeeds — PASS |

**GitOps parser (fleetctl gitops --dry-run)**

| Test | Input | Result |
|------|-------|--------|
| 12 | manual + query + platform + criteria | All 3 errors surfaced at
once — PASS |
| 13 | valid manual label | No validation errors — PASS |
| 14 | dynamic + invalid platform | Error surfaced — PASS |

---

### Code walkthrough

**`server/fleet/labels.go`** — Added
`ValidateLabelMembershipFields(*LabelSpec) *InvalidArgumentError`. This
is the single source of truth for label field validation, returning
field-specific errors (`platform`, `query`, `criteria`, `hosts`). Lives
here because this package defines the label types both callers import.
Also uses `strings.TrimSpace` to reject whitespace-only queries.

**`server/service/labels.go`** — Three changes: (1) Removed the early
blanket platform check from `NewLabel` that ran before the membership
type was known. (2) Added `ValidateLabelMembershipFields` call in
`NewLabel` after type inference, so the API rejects invalid combos at
creation time. (3) Replaced three incomplete inline checks in
`ApplyLabelSpecs` with a single call to the centralized function, using
`err.WithStatus(422)` to preserve field-specific error shape in the API
response.

**`pkg/spec/gitops.go`** — Replaced the inline validation switch and a
standalone `ValidLabelPlatformVariants` check with a call to
`ValidateLabelMembershipFields`. Unwraps the returned errors
individually into `multiError` so all validation problems are reported
to the user at once.

**`cmd/fleetctl/fleetctl/generate_gitops.go`** — Gated platform emission
on `LabelMembershipTypeDynamic` so legacy manual/host_vitals labels with
a stored platform don't produce YAML that fails re-import.

**`cmd/fleetctl/fleetctl/get.go`** — Added `stripMismatchedLabelFields`
which clears type-inappropriate fields (query, platform, criteria,
hosts) per membership type before YAML output. Called in both code
paths: listing all labels and fetching a single label by name. Ensures
the `get labels --yaml` → `apply` round-trip works for legacy data.

**`server/datastore/mysql/labels.go`** — Added missing `l.criteria`
column to `GetLabelSpec` SELECT, matching `GetLabelSpecs`. Without it,
host_vitals labels fetched by name lost their criteria in the YAML
output, causing re-import to fail with the new validation.
2026-05-05 16:19:41 -04:00
Nico e1029042e5 Add regression test for renaming a patch policy via GitOps (#43687) (#44759)
**Related issue:** Resolves #43687

## Summary
Added a regression test for the issue above. Seems to have been fixed in
#43420 as part of 4.83.1.

# Checklist for submitter

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

## Testing

- Created a patch policy for Adobe Acrobat Reader for the Workstations
fleet.
- Ran generate-gitops.
- Renamed the entry for this patch policy to have the "EDITED -" prefix.
- Ran gitops.
- Ran succeeded and I see the policy's name was updated on the UI.

<img width="761" height="411" alt="Screenshot 2026-05-05 at 8 14 45 PM"
src="https://github.com/user-attachments/assets/4091e4db-d935-40ef-8d85-4ae9094af24e"
/>

<img width="1242" height="357" alt="Screenshot 2026-05-05 at 8 21 05 PM"
src="https://github.com/user-attachments/assets/1f13d5bc-3293-403f-9353-fa8f9a93d39d"
/>
2026-05-05 21:38:18 +02:00
Juan Fernandez 7088dfa32c Add include_all label scope to GitOps and fleetctl (#41566)
Resolves #41566 

Wires labels_include_all to GitOps and fleetctl for policies and reports.
2026-05-05 15:13:22 -04:00
Scott Gress c3b82539a5 Allow disabling historical data collection (GitOps / API) (#44488)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** For #44077

# Details

* Adds `historical_data` key to app and team config (and gitops) with
`uptime` and `vulnerabilities` subkeys. Keys default to `true`, meaning
"collect this data"
* Adds `enabled_historical_dataset` and `disabled_historical_dataset`
activities when these values are flipped via GitOps or the config APIs

The majority of the file changes in here are GitOps test files that need
to be updated to have the new config in them.

**This PR does _not_ implement using these configs to actually disable
data collection or purge data; that will come in a follow-up PR (as well
as the front-end)**

# 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.
n/a, unreleased

## Testing

- [X] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually
  #### Defaults
- [X] Fresh install: `GET /api/v1/fleet/config` returns
`features.historical_data.uptime: true` and
`features.historical_data.vulnerabilities: true`
- [X] Created a new fleet via `POST /api/v1/fleet/teams`, then `GET
/api/v1/fleet/fleets/{id}` returns `features.historical_data.uptime:
true` and `features.historical_data.vulnerabilities: true`

  #### Global PATCH (`POST /api/v1/fleet/config`)
- [X] PATCHed `{"features": {"historical_data": {"vulnerabilities":
false}}}` — `vulnerabilities` flipped to `false`, `uptime` unchanged at
`true`
- [X] PATCHed `{"features": {"historical_data": {"uptime": false,
"vulnerabilities": true}}}` — both values applied as sent
- [X] PATCHed `{"features": {"historical_data": {"vulnerabilites":
false}}}` (typo in sub-key) — request rejected with 4xx, stored config
unchanged

  #### Fleet PATCH (`PATCH /api/v1/fleet/fleets/{id}`)
- [X] PATCHed a fleet with `{"features": {"historical_data": {"uptime":
false}}}` — fleet's `uptime` flipped to `false`, `vulnerabilities`
unchanged
- [X] Subsequent `GET /api/v1/fleet/fleets/{id}` returns the toggled
values under `features.historical_data` (storage shape is symmetric with
global)
- [X] PATCHed a fleet with `{"features": {"enable_host_users": false}}`
(a non-`historical_data` features sub-field) — request returned 200 but
the fleet's `enable_host_users` is unchanged (silently ignored, per
existing endpoint convention)

  #### GitOps — global (`fleetctl gitops -f global.yml`)
- [X] Applied a YAML with `features.historical_data: {uptime: true,
vulnerabilities: false}` — `vulnerabilities` is `false` after apply,
`uptime` is `true`
- [X] Applied a YAML whose `org_settings` omits `features` entirely —
both sub-keys are `true` after apply (defaults injected even if
previously disabled)
- [X] Applied a YAML where `historical_data` only contains `uptime:
false` — `uptime: false` is honored, `vulnerabilities` defaults to
`true`
- [X] Disabled `vulnerabilities` via the API, then ran `fleetctl gitops`
with a YAML that doesn't pin it — `vulnerabilities` flips back to `true`
(this is intentional; gitops is the source of truth)

  #### GitOps — fleet
- [X] Applied a fleet YAML with `features.historical_data: {uptime:
false}` — that fleet has `uptime: false`, `vulnerabilities: true` after
apply
- [X] Applied a fleet YAML whose `team_settings.features` omits
`historical_data` — both sub-keys are `true` after apply
- [X] Applied a fleet YAML that omits `features` entirely — both
sub-keys are `true` after apply

  #### `fleetctl apply` (legacy, partial-merge)
- [ ] Disabled `vulnerabilities` via the API, then ran `fleetctl apply`
with a YAML that doesn't mention `historical_data` — `vulnerabilities`
is still `false` (apply leaves omitted fields alone)

  #### Activities — global
- [X] After PATCHing global to disable `vulnerabilities`, the latest
activity is `disabled_historical_dataset` with payload `{"dataset":
"vulnerabilities", "fleet_id": null, "fleet_name": null}`
- [X] After PATCHing global with both sub-keys flipping in one request,
two activities are emitted (one per sub-key)
- [X] After PATCHing global with the same values that are already
stored, zero new activities are emitted
- [X] After re-enabling a previously disabled dataset, the activity type
is `enabled_historical_dataset`

  #### Activities — per fleet
- [X] After PATCHing fleet `workstations` to disable `uptime`, the
activity is `disabled_historical_dataset` with payload `{"dataset":
"uptime", "fleet_id": <workstations id>, "fleet_name": "workstations"}`
- [X] Toggling the same dataset on two different fleets produces two
distinct activities, one per fleet
- [X] After a fleet PATCH with the same values already stored, zero new
activities are emitted

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

## 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`
- [X] 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)
  - https://github.com/fleetdm/fleet/pull/44703
- [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)
- [X] 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**
* Historical-data controls: per-org and per-team toggles for uptime and
vulnerability time‑series, with defaults applied when keys are omitted
and enable/disable activities emitted on changes.

* **Bug Fixes**
* Partial updates and PATCH/GitOps flows preserve unspecified
historical-data sub-keys instead of clearing them.

* **Tests**
* Expanded unit and integration tests covering defaults, partial
PATCH/GitOps behavior, idempotency, and activity emission.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-05 11:12:23 -05:00
227e94de5b 🤖 Chore: remove deprecated appendListOptionsWithCursorToSQL (#44385)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44723

# 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


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

* **Bug Fixes**
* Strengthened validation of sorting/order parameters across many list
and cursor-based endpoints — unsupported sort keys now return explicit
errors and prevent unsafe queries.
* Labels listing: label-list pagination query name changed; ordering by
host_count is rejected when host counts are disabled (validated at
request parsing).

* **Tests**
* Added/expanded tests covering allowed order keys, rejection of unknown
keys, and pagination behavior for multiple listing APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
2026-05-05 10:26:47 -04:00
Scott Gress 5e7f5a7584 Optimize data collection: add index and batch deletes (#44692)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44609

# Details

This PR optimizes the historical data collection system in two ways:

1. Adds an additional index on the `host_scd_data` table allowing more
efficient lookups of rows by their `valid_to`, to optimize both closing
out open rows and deleting old rows
2. Implements batching in the job that deletes old rows, so that it no
longer blocks writes if the collection job happens to happen at the same
time as the cleanup job

# 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.
n/a, unreleased
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.
- [ ] Timeouts are implemented and retries are limited to avoid infinite
loops

## Testing

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

SQL explains -- before:

```
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table         | partitions | type | possible_keys | key  | key_len | ref  | rows   | filtered | Extra       |
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+
|  1 | DELETE      | host_scd_data | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 144320 |   100.00 | Using where |
+----+-------------+---------------+------------+------+---------------+------+---------+------+--------+----------+-------------+

+----+-------------+---------------+------------+-------+--------------------------------------+--------------------+---------+-------------+------+----------+-------------+
| id | select_type | table         | partitions | type  | possible_keys                        | key                | key_len | ref         | rows | filtered | Extra       |
+----+-------------+---------------+------------+-------+--------------------------------------+--------------------+---------+-------------+------+----------+-------------+
|  1 | UPDATE      | host_scd_data | NULL       | range | uniq_entity_bucket,idx_dataset_range | uniq_entity_bucket | 604     | const,const | 3030 |   100.00 | Using where |
+----+-------------+---------------+------------+-------+--------------------------------------+--------------------+---------+-------------+------+----------+-------------+
```

Using a test set of data (~144k "open" rows), UPDATES happened at 9 ops
per second.

after:

```
+----+-------------+---------------+------------+-------+----------------------+----------------------+---------+-------+-------+----------+-------------+
| id | select_type | table         | partitions | type  | possible_keys        | key                  | key_len | ref   | rows  | filtered | Extra       |
+----+-------------+---------------+------------+-------+----------------------+----------------------+---------+-------+-------+----------+-------------+
|  1 | DELETE      | host_scd_data | NULL       | range | idx_valid_to_dataset | idx_valid_to_dataset | 5       | const | 55749 |   100.00 | Using where |
+----+-------------+---------------+------------+-------+----------------------+----------------------+---------+-------+-------+----------+-------------+

+----+-------------+---------------+------------+-------+-----------------------------------------------------------+----------------------+---------+-------------------+------+----------+------------------------------+
| id | select_type | table         | partitions | type  | possible_keys                                             | key                  | key_len | ref               | rows | filtered | Extra                        |
+----+-------------+---------------+------------+-------+-----------------------------------------------------------+----------------------+---------+-------------------+------+----------+------------------------------+
|  1 | UPDATE      | host_scd_data | NULL       | range | uniq_entity_bucket,idx_dataset_range,idx_valid_to_dataset | idx_valid_to_dataset | 609     | const,const,const |    4 |   100.00 | Using where; Using temporary |
+----+-------------+---------------+------------+-------+-----------------------------------------------------------+----------------------+---------+-------------------+------+----------+------------------------------+
```

Using the same test set of data, UPDATES happened at 4,910 ops per
second.

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 should significantly improve results!
- [ ] Alerted the release DRI if additional load testing is needed

## Database migrations

- [X] 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`).

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

* **Chores**
* Cleanup now runs in controlled, ordered batches, removing only
closed/historical records while respecting cancellation; error reporting
for cleanup was strengthened.
* Added a new composite index on historical data to improve cleanup and
query performance.
* **Tests**
* Added tests and test helpers validating batched cleanup behavior,
preservation of open records, multi-batch operation, and cancellation
handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-05 08:29:47 -05:00
Nico b4a207fb5a Add ability to upload custom org logos (#44390)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44330, Resolves #44331

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

## Testing

- [x] Added/updated automated tests. (I'd defer integration tests to a
separate PR since this one is pretty large already.)

- [x] QA'd all new/changed functionality manually. I've tested this on
both the setup flow and the organization settings page. I haven't had
the time to test this on other places where we render the logo (macOS
setup experience / MDM migration dialog).


https://github.com/user-attachments/assets/95d4eae5-3da6-40f4-98a1-8575b97d96b3

## New Fleet configuration settings

- [x] Setting(s) is/are explicitly excluded from GitOps.

Will handle GitOps in a separate PR.

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

* **New Features**
  * Organizations can upload custom logos for light and dark modes.
* Registration and Org Settings support logo file upload, preview,
per-mode replace/delete, and validation (size & image formats).
* Activity feed records logo changes/deletions; site nav displays
uploaded logos per theme.
* File uploader/preview adds a Fleet logo graphic option and improved
logo validation.
  * Config/GitOps outputs now include separate dark/light logo fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-05 14:42:52 +02:00
Tim Lee 8d37ec690c Revert "Fix SCEP autorenew failing for offline hosts (#44250)" (#44535) 2026-05-04 13:33:42 -06:00
Konstantin SykulevandCopilot 779cdd663b Periodic background job to cleanup Windows MDM command queue (#44458)
**Related issue:** Resolves #44190

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

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

* **New Features**
* Added a periodic cleanup job that removes aged, acknowledged Windows
MDM command-queue entries to reduce write pressure during ACK
processing.

* **Bug Fixes**
* Pending-command detection now excludes already-ACKed commands from
dispatch; queue rows are retained after ACK and cleaned later.

* **Tests**
* Added and updated tests to validate cleanup behavior and revised
ACK/queue semantics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-04 11:32:45 -05:00
Martin AngersandMagnus Jensen c2dda6a16c Wipe host cancels all upcoming activities (#44323)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40459 

# 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

Recording:
https://drive.google.com/file/d/1_XqLyy-oY-WnIa97R4t9HihiBq3Fui6n/view?usp=drive_link

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

* **New Features**
* Wiping a host now cancels all upcoming and queued activities for that
host in a single, atomic operation to avoid intermediate activations.

* **Bug Fixes**
* Wipe response handling now distinguishes success vs failure and
reliably cancels queued activities; datastore errors during host lookup
or cancellation are surfaced.
* Device lock/erase flows consistently update and propagate datastore
errors.

* **Tests**
* Added integration and datastore tests validating wipe clears upcoming
activities across macOS, Windows, Linux, and mixed-host scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Magnus Jensen <magnus@fleetdm.com>
2026-05-01 14:01:46 -06:00
Carlo ce5640c99e Prevent silent corruption of software title icons (#44540) 2026-05-01 14:19:48 -04:00
Magnus Jensen 275b266ca1 produce failed enrollment renewal activity (#44511)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41418 

# 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

To manually QA, I put an early return with `msg.Fail` in the
`mdm_scep.go` file under PKIOperation method, and then triggered a SCEP
renewal.

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

* **New Features**
* Activity logging for Apple MDM enrollment profile renewal failures to
improve auditing and diagnostics.
* Host display enhancements: include computer name and hardware model to
improve host identification in activities and UI.

* **Tests**
* Integration tests verifying enrollment renewal failure activity
creation, association to the correct host, and activity payload
contents.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 11:38:08 -06:00
Victor Lyuboslavsky de86536f42 Redis-backed cache for host-by-key lookups (#43936)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43928 

This PR adds a Redis-backed cache in front of the two host-by-key
lookups on the agent auth paths.

Docs: https://github.com/fleetdm/fleet/pull/44504

## What changes

**Read path (osquery/orbit auth):**

- `LoadHostByNodeKey` and `LoadHostByOrbitNodeKey` now check Redis
before falling through to MySQL.
- Successful lookups are cached for 60s ± 10% jitter (configurable via
`FLEET_REDIS_HOST_CACHE_TTL`).
- `NotFound` results are cached for 5s as a negative entry, dampening
repeated probes for keys that
do not exist (deleted hosts whose agents are still polling, attacker
scans, retry storms).
- Concurrent lookups for the same key collapse into one DB query via
`singleflight`. The shared
query runs under a context detached from any one caller's deadline so
the leader giving up does
not abort the work for joiners. The shared query is itself bounded by a
30s timeout so a wedged
  DB call cannot pin the singleflight slot indefinitely.

**Write path (invalidations):**

- These methods now invalidate the cache after a successful inner call:
`UpdateHost`, `SerialUpdateHost`, `UpdateHostOsqueryIntervals`,
`UpdateHostRefetchRequested`,
`UpdateHostRefetchCriticalQueriesUntil`,
`UpdateHostIdentityCertHostIDBySerial`, `EnrollOsquery`,
`EnrollOrbit`, `NewHost`, `DeleteHost`, `DeleteHosts`,
`CleanupExpiredHosts`,
  `CleanupIncomingHosts`, `AddHostsToTeam`.
- `AddHostsToTeam`, `DeleteHosts`, `CleanupExpiredHosts`, and
`CleanupIncomingHosts` use a pipelined
batch invalidator so 10k-host operations stay in the millisecond range
instead of taking minutes
  of sequential round-trips.
- Inner-call errors are not invalidations: a failing write leaves cached
state intact.

**Configuration:**

- New flags `FLEET_REDIS_HOST_CACHE_ENABLED` (default `true`) and
`FLEET_REDIS_HOST_CACHE_TTL`
  (default `60s`).
- Server refuses to start if the cache is enabled with `TTL <= 0`.

**Observability:**

- Three new OTEL counters under the `fleet` meter:
  - `fleet.host_cache.lookups{result=hit|negative_hit|miss}`
  - `fleet.host_cache.errors{op=get|set|del}`
-
`fleet.host_cache.invalidations{reason=update|enroll|team|delete|cert}`
- A pre-built SigNoz dashboard ships in
`tools/signoz/host_cache_dashboard.json`.

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

## 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**
* Optional Redis-backed host lookup cache for osquery and orbit auth,
with automatic invalidation and metrics/monitoring dashboard.

* **Bug Fixes**
* Fixed host-removal batching so cache-related removals use correct
chunks.

* **Tests**
* Added comprehensive host-cache unit tests covering hits, negative
cache, invalidation, concurrency, and JSON round-trips.

* **Chores**
* New config flags to enable the cache and set TTL (default 60s ±10%
jitter).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 12:06:16 -05:00
Lucas Manuel Rodriguez 1e4a9f292f Add activities for user actions on labels (#44522)
Resolves #36976

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

* **New Features**
* Label operations (create, edit, delete) now generate activities shown
in the activity feed with label and optional fleet context.
* Host label add/remove operations emit corresponding label edited
activities; duplicate label names are deduplicated.
* Label activity types are selectable/filterable in the activity
dashboard.

* **Tests**
* Added unit, integration, and UI tests covering label activity
emission, rendering, filtering, and GitOps label lifecycle scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 10:19:45 -03:00
Victor Lyuboslavsky 2723c132c2 Fixed GET /api/v1/fleet/commands timeout in large Fleet deployments (#44297)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44170 and Resolves #44422

Pagination is now pushed into each branch of the merged query, so
per-tick work scales with page size instead of total commands. The
Windows side was rewritten to avoid a disjunctive join that forced a
nested-loop plan. `per_page` is capped (default 10), `page` is capped,
and `order_key` is enforced against a closed allowlist on both code
paths. Cursor pagination is fixed and is the recommended way to traverse
beyond the page cap.

This PR improves but does not fix the use case of fetching commands from
all hosts. Deprecate usage without host_identifier:
https://github.com/fleetdm/fleet/pull/44392/changes

API doc updates: https://github.com/fleetdm/fleet/pull/44292

# 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] 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**
* Enforced pagination on MDM commands list: per_page defaults to 10 (max
1,000) and page is capped at 100; traversal beyond page 100 requires
cursor pagination via after.

* **Bug Fixes / Performance**
* Improved MDM command listing performance and de-duplication for large
queries; fixed SQL error when combining host identifier with cursor
pagination.

* **Validation**
* Requests exceeding pagination caps return 400; invalid sort keys
return 422.

* **Tests**
* Added tests for pagination boundaries, cursor behavior, sort-key
validation, and error responses.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 15:44:19 -05:00
Juan Fernandez 38a6129d0a Add include_all label scope to policies and reports (#44305)
**Related issue:** Resolves #41564 

- Added include_all label scope to policies.
- Added include_all and include_any scope to reports.
2026-04-30 11:28:30 -04:00
W0lfbaneandCopilot ad7ea0aa7f fix(android): remove tautological NCR filter in hostVPPInstalls (#42873)
## Problem

The `hostVPPInstalls` function in `server/datastore/mysql/software.go`
contains a SQL condition:

```sql
(hvsi.platform != 'android' OR ncr.id IS NULL) AND
```

This is a logical tautology — it **never filters any rows**:

- **Android rows**: `ncr.id` is always `NULL` because Android installs
use Google's Android Management API, not nanoMDM. The condition
evaluates to `(FALSE OR TRUE) = TRUE`.
- **Apple rows**: The first operand `hvsi.platform != 'android'` is
`TRUE`, so the whole expression is `TRUE` regardless of `ncr.id`.

The condition was likely added during early Android VPP support to guard
against unexpected NCR joins for Android. However, since
`nano_command_results` is only written by the nanoMDM Apple MDM storage
layer (`server/mdm/nanomdm/storage/mysql/queue.go:168`), the guard can
never trigger.

Elsewhere in the codebase, the canonical pattern for NCR filtering is:

```sql
-- vpp.go:248, software_installers.go:1812
(ncr.id IS NOT NULL OR (:platform = 'android' AND ncr.id IS NULL))
```

This pattern has *different semantics* — it filters per-app aggregate
status counts to only include confirmed installs. The `hostVPPInstalls`
function serves the host software list where showing all statuses
(including pending) is intentional, so no NCR filter is needed.

## Changes

- Removed the dead condition from the `last_vpp_install` UNION branch
- Added a clarifying comment explaining why no NCR filter is applied and
how this differs from other query sites
- Added changelog entry

## Testing

- No behavior change — the removed condition was always TRUE
- Existing tests pass without modification
- `go build ./server/datastore/mysql/...` compiles clean

#android #sql #cleanup

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-30 15:30:19 +01:00
Konstantin Sykulev 5aac997101 Exclude orphaned windows profiles (#44423)
**Related issue:** Resolves #44369


- [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**
* Prevented operations on Windows MDM profiles for hosts without active
enrollments.
* Batch processing now skips hosts lacking current enrollments so only
enrolled hosts receive queued commands.
* Strengthened profile-removal checks to avoid acting on orphaned
profile rows.

* **Tests**
* Added regression tests covering orphaned enrollment/profile scenarios
and mixed-host batch processing.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 17:12:27 -05:00
Jonathan Katz c158f912c6 43962 vpp managed config migration (#44435)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43962
Adds two tables: `vpp_app_configurations` and
`in_house_app_configurations`
`vpp_app_configurations` has `team_id` unsigned **not nullable**, rather
than `team_id` nullable + `global_or_team_id`. This is following the
pattern in `software_title_display_names` and `software_title_icons`,
since software installers are team only and cannot be global.
`android_app_configurations` uses team_id + global_or_team_id but that
seems to be unnecessary.
`in_house_app_configurations` keys on `in_house_app_id` only — the
parent `in_house_apps` row already pins the team and platform.
Both use MEDIUMTEXT to store the XML configuration.

# Checklist for submitter

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


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

* **Chores**
* Added database tables for storing VPP and in-house app configurations,
organized by team/platform with automatic cleanup when parent apps are
deleted.
* **Tests**
* Added migration tests to validate config storage fidelity, uniqueness
and platform-specific constraints, foreign-key enforcement, and
cascade-delete behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 17:47:08 -04:00
Dante Catalfamo 96569a9c1c Fix SCEP autorenew failing for offline hosts (#44250)
**Related issue:** Resolves #44111

Customers reported certificates deployed via custom SCEP proxy were
silently failing to auto-renew, leaving devices with expired certs. Five
compounding bugs were causing this:

### 1. Cert metadata was wiped on every reconcile re-render

`BulkUpsertMDMManagedCertificates` unconditionally overwrote
`not_valid_before`, `not_valid_after`, and `serial` in `ON DUPLICATE KEY
UPDATE`. Since the SCEP-proxy render-time payload has those fields nil
(cert details aren't known until the device completes the handshake and
osquery reports), every renewal trigger wiped them. Once NULL, the
renewal cron's `HAVING validity_period IS NOT NULL` clause excluded the
row — silently disabling future renewal attempts.

Fixed by switching those columns to `COALESCE(VALUES(col), col)` so a
nil incoming value preserves the existing value. DigiCert's flow (which
does set the fields) and osquery's separate UPDATE in
`updateHostMDMManagedCertDetailsDB` are unaffected.

### 2. 1-hour challenge TTL was too short for offline devices

The challenge is generated at profile-render time but consumed when the
device makes its SCEP request — which can be hours or days later (laptop
asleep, on a plane, etc.). Devices that didn't pick up the
InstallProfile push within the hour hit `challenge not found: sql: no
rows in result set` and the renewal failed.

Bumped `OneTimeChallengeTTL` from 1 hour to 7 days. Once consumed, the
challenge is deleted immediately regardless of TTL.

### 3. Renewal cron re-fired on in-flight deliveries

`WHERE hp.status IS NOT NULL` matched `'pending'` and `'verifying'` too,
so a host whose delivery was still in flight (e.g., offline laptop)
would have its profile re-rendered with a fresh challenge every cron
tick — generating orphan nano commands and challenge rows hourly.
Pre-fix this was masked by bug 1; once the COALESCE preserves cert
metadata, the loop becomes visible.

Tightened the filter to `WHERE hp.status IN ('verified', 'failed')` —
settled states only.

### 4. iOS/iPadOS managed-cert profiles short-circuited to verified
before cert metadata synced

iOS/iPadOS profiles short-circuit `pending` → `verified` directly on MDM
ack (no `verifying` step), since osquery isn't available to drive the
standard verification cycle. That's correct for non-cert profiles, but
for managed-cert profiles it created a window where the renewal cron saw
`status='verified'` paired with stale cert metadata still in the renewal
window — and the new `IN ('verified', 'failed')` filter from bug 3 kept
matching, re-firing renewal each tick until `CertificateList` ingestion
eventually caught up.

Fixed by parking iOS/iPadOS managed-cert profiles at `'verifying'` on
MDM ack and flipping them to `'verified'` from
`updateHostMDMManagedCertDetailsDB` once fresh cert metadata arrives —
i.e., reusing the existing state machine instead of inventing a parallel
"renewal in flight" tracking column. The `EXISTS(SELECT 1 FROM
host_mdm_managed_certificates ...)` check is folded into the existing
platform-detection query, so no extra round-trip. macOS is unaffected:
the new flip is redundant with `VerifyHostMDMProfiles` but idempotent.

**Trade-off worth flagging:** if `CertificateList` ingestion never runs
for an iOS managed-cert profile (broken cron, device offline
indefinitely), the profile sits at `'verifying'` and the renewal cron's
filter excludes it. In practice both run on the same Apple MDM cron loop
— if one is broken, much else is too — but it's a sharper failure mode
than letting renewals re-fire wastefully.

### 5. Permanent-failure profiles loop hourly through the renewal cron

Once `'failed'` was added to the cron's status filter (bug 3), there was
no longer any circuit breaker for profiles that fail at render time for
non-transient reasons — CA deleted from app config, IDP variables
missing from host, premium license downgraded. Each cron tick (1h
interval) the cron flips `'failed'` → NULL, reconcile re-renders and
immediately re-fails via `fleet.MarkProfilesFailed`, status returns to
`'failed'`, repeat. Pre-fix this was masked by bug 1 (metadata wipe
acted as accidental circuit breaker); once metadata is preserved (bug 1
fix), the loop becomes real and produces a profile-render attempt + nano
command per failed cert per hour.

Added a `renewalFailedRetryBackoff` constant (24h) and gated the
`'failed'` branch on `hp.updated_at < DATE_SUB(NOW(), INTERVAL
renewalFailedRetryBackoff SECOND)`. Transient SCEP-server outages still
recover (within at most 24h, well under any cert validity window).
Permanent failures still get retried daily (so a customer fixing the
underlying issue eventually auto-recovers), but they don't churn nano
commands hourly. `'verified'` rows in the renewal window are unaffected
— they bypass the gate.

## Tests

- `testMDMManagedSCEPCertificates`: three new sub-tests covering (a)
cert-metadata preservation across reconcile re-renders, (b)
in-flight-status skip behavior, (c) the permanent-failure backoff.
Exercised against both NDES and Custom SCEP via the existing
table-driven harness.
- New `testIOSManagedCertProfileStaysVerifying`: verifies that on iOS, a
managed-cert profile stays at `'verifying'` after MDM ack and only flips
to `'verified'` once `UpdateHostCertificates` ingests fresh cert
metadata.
- New `challenges_test.go` covering `NewChallenge`/`ConsumeChallenge`
lifecycle and TTL boundaries.
- `TestCustomSCEPIntegration`: updated the hardcoded 2-hour challenge
backdate to use `fleet.OneTimeChallengeTTL` so it stays correct as the
constant evolves.
- New `TestCustomSCEPRenewalPreservesCertMetadata` end-to-end test:
drives the full reconcile path (rather than calling the bare datastore
method) so a future change to the render-time payload structure can't
silently regress the COALESCE preservation.
2026-04-29 17:14:26 -04:00
Jordan MontgomeryandCopilot Autofix powered by AI 62b60fef24 Improve filtering on commands endpoints (#44426)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
Provides better errors on invalid/unexpected sort keys passed to
`/api/v1/fleet/commands`, `/api/v1/fleet/mdm/commands` and
`/api/v1/fleet/mdm/apple/commands` endpoints

# 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 validation for invalid `order_key` values on MDM command
endpoints (`/api/v1/fleet/commands`, `/api/v1/fleet/mdm/commands`, and
`/api/v1/fleet/mdm/apple/commands`), ensuring only approved sorting
parameters are accepted.

<!-- 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-04-29 16:08:49 -04:00
Jordan MontgomeryandCopilot 78c0b0c651 43885: MLAPR migration + UUID capture (#44244)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43885

Adds a migration and code to capture the value of the fleet managed
admin account if one exists. Changes file added for entire feature

# 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

## 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.
- [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**
* Automatic password rotation for managed local admin accounts on macOS,
triggered after viewing activity.
* Provisioning now captures and persists the managed admin account
identifier (UUID) to support rotation and prevents that account from
being stored as a regular user.
* Hosts will request a best-effort recheck when the managed admin
identifier is not yet available.

* **Chores**
* Database schema updated to store rotation scheduling and pending
credential state.

* **Tests**
* Added tests covering UUID capture, conditional updates, migration, and
ingest behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 11:14:50 -04:00
Magnus Jensen 98cad56716 redirect to correct URL, and allow both URLs for MDM SSO SAML validation if set (#44156)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41592 

# 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 SSO failures when a custom Apple MDM URL is configured: callback
requests are now redirected to the configured MDM URL when needed, and
SAML validation correctly considers the configured MDM/server URLs so
authentication succeeds for custom MDM setups.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 08:43:58 -06:00
Lucas Manuel Rodriguez 52caba768c Fix filtering in /api/v1/fleet/labels/:id/hosts endpoint (#44293)
- [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

* **Bug Fixes**
* Fixed filtering in the /api/v1/fleet/labels/:id/hosts endpoint and
tightened validation to reject invalid sort/order keys with HTTP 422
responses.
* Enforced ordering restrictions tied to feature flags (issues and
device-mapping), rejecting unsupported order_key values.

* **Tests**
* Added extensive integration tests for order_key validation,
deterministic sorting across allowed keys, and cursor pagination.

* **Documentation**
  * Added a changelog entry noting the hosts-in-label filtering fix.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 10:43:39 -03:00
Allen Houchins ff60104c9a Prevent patch title ID collisions (#44179)
Fixes #44183
2026-04-28 18:22:11 -04:00
c0ecbfc1d8 Return Windows Enrollment Status Page (ESP) (#43454)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #42843

This change shows Windows Enrollment Status Page (ESP) during OOBE
enrollment. It does not track/update the status of that page, so the end
user does not actually see any progress on it. Its purpose is to block
the user from proceeding to desktop until all the profiles have been
sent to the device. Software apps are not being tracked/blocked in this
PR.

This is what the final ESP screen looks for this PR before it takes the
user to set up Windows Hello:
<img width="646" height="549" alt="image"
src="https://github.com/user-attachments/assets/748a2710-9388-4d04-93d1-8f2a518965a1"
/>


# Checklist for submitter

## 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**
* Enrollment Status Page (ESP) support for Windows Autopilot: sends
hold/release commands and advances enrollment states during setup.
* Scoped profile installation checks per host and a default ESP timeout
(3 hours).

* **Bug Fixes**
* Clears prior profile delivery state during reenrollment cleanup to
avoid stale delivery state.
* Safer state transitions for "awaiting configuration" with guarded
compare-and-swap updates.

* **Tests**
* New unit and integration tests validating ESP flows and
awaiting-configuration transitions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Sykulev <konst@sykulev.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-28 15:39:03 -05:00
Victor Lyuboslavsky 9628f49cb8 Improved the performance of Windows MDM profile reconciliation (#44075)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44052 

Improve performance by reducing the time for the synchronous API call to
update profiles or switch teams. And spreading out the application of
profiles by processing 2000 hosts every 30 seconds.

1. **Windows profile reconciliation is no longer synchronous to
bulk-set.**
Apple, Android, and Apple-declaration paths still write their pending
state inside the bulk-set transaction. The Windows path commits the
transactional inputs and lets the existing `mdm_windows_profile_manager`
cron pick the work up on its next tick. The visible effect is that
`host_mdm_windows_profiles` is no longer guaranteed to be populated by
the time bulk-set returns; it converges within one cron interval.

2. **The Windows reconciler now processes hosts in bounded batches, with
a persisted cursor.**
Previous behavior was "scan the universe of pending Windows hosts on
every tick." New behavior is a host-window query bounded by batch size
and a `host_uuid` cursor, advanced after the batch commits successfully
and persisted across ticks. A failed tick leaves the cursor untouched so
the same window is retried.

3. **Two replication races are now explicitly handled.**
- Admin-delete vs reconcile: the existence check the reconciler uses to
avoid touching a just-deleted profile reads from the primary, not a
replica.
- Insert lag in the reconciler's own listings: hosts that appear in the
cursor query but are not yet visible in the scoped listings advance the
cursor instead of jamming the loop.

4. **`updates.WindowsConfigProfile` from `BulkSetPendingMDMHostProfiles`
is now always false in production.**
The only consumer ORs it with the transactional signal from
`BatchSetMDMProfiles`, which is the accurate source. The bulk-set call
no longer attempts to compute or return that activity signal itself.

5. **Tests opt in to the old synchronous behavior via a named hook.**
Default test behavior matches production (deferred). Legacy tests whose
assertions require Windows rows immediately after bulk-set call an
explicit enable-hook and rely on `t.Cleanup` to restore.

# 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

* **New Features**
* Windows MDM profile reconciliation batching improvements enable large
team transfers and bulk profile change operations to complete faster,
with profile updates rolling out in the background without blocking host
check-ins or other MDM activity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-28 15:37:43 -05:00
Jonathan Katz 51dca83dec Fix script-only packages not setting install script file (#44299)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43659

# 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**
* Preserves install scripts for script-only software installers when
using hash-based references in GitOps, preventing self-service installs
from silently no‑opping.
* **Tests**
* Added an integration regression test to verify batch installer
resolution by hash preserves uploaded install script contents.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-28 13:37:03 -04:00
Carlo 5ac50a2dc9 Bound orbit retries on missing installer details to 5 mins (#44284)
Fixes #44084
2026-04-28 12:58:10 -04:00
Martin Angers 2c609ae78e CSAH: appconfig/gitops/DB migration to add preserve_host_activities_on_reenrollment field (#44212)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43943 

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

See
https://github.com/fleetdm/fleet/issues/43943#issuecomment-4329658412

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.

## New Fleet configuration settings

- [x] Verified that the setting is exported via `fleetctl
generate-gitops`
- [x] 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)
(see https://github.com/fleetdm/fleet/pull/43877/changes)
- [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)
- [ ] Verified that any relevant UI is disabled when GitOps mode is
enabled (should be done by
https://github.com/fleetdm/fleet/issues/43947)



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

* **New Features**
* Added a configuration option to preserve host activities during host
re-enrollment, letting admins choose whether activity history is
retained when hosts re-enroll.

* **Chores**
* Updated defaults and database migration state so the new setting is
present in stored and generated configs and in GitOps outputs.

* **Tests**
* Added unit, integration, migration, and GitOps fixtures to validate
behavior, serialization, and upgrade semantics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-28 08:47:38 -04:00
Konstantin Sykulev 9ec20e60b7 Windows MDM improved host profile status performance (#44225)
**Related issue:** Resolves #44189

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

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

* **Performance**
* Optimized Windows MDM profile removal to skip redundant database
writes for terminal removals.

* **Bug Fixes**
* Ensure terminal remove responses (both verified and failed) delete the
corresponding profile records without affecting concurrent installs.

* **Tests**
* Added coverage for mixed install/remove responses and re-install after
a verified removal.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 20:09:27 -05:00
Jonathan Katz 899dc5aa57 Check for duplicate linux software installers (#44234)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #43959 #44038
Refactored `checkSoftwareConflictsByIdentifier` to a switch statement
with different logic per platform


# 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 duplicate software installer entries on Linux.
- Improved conflict detection for software installers across iOS, macOS,
Windows, and Linux platforms to prevent incompatible uploads.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 17:14:47 -04:00
Lucas Manuel Rodriguez bd18bac797 Adding gitOpsModeEnabled and gitOpsModeExceptions to anonymous statistics payload (#44161)
**Related issue:** Resolves #42240.

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

* **New Features**
* Statistics now include GitOps mode: whether it’s enabled and the
ordered list of configured exception categories (serializes as an empty
list when none).

* **Tests**
* Added tests for GitOps-related statistics transitions and made
statistics-timing tests deterministic for reliable behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-27 08:28:49 -03:00
Victor LyuboslavskyandCopilot 65fcc132ae Fixed a race where a host could silently revert to its previous team (#44074)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #44071 

Verified fix in loadtest.

# 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

* **Bug Fixes**
* Fixed a race condition that could cause hosts to silently revert to a
previous team after an admin team transfer.
* Improved reliability of team-transfer handling to prevent unexpected
reversion during certificate/template transfers and device/profile
operations.

* **Tests**
* Added regression tests to ensure team assignments persist correctly
across host refreshes and related workflows.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-24 14:34:37 -05:00
Lucas Manuel Rodriguez c22954edf2 Remove unused windows_updates MySQL table and ingestion (#44128)
**Related issue:** Resolves #44127

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

## Testing

- [x] QA'd all new/changed functionality manually

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

* **Chores**
* Removed the unused Windows Updates feature: ingestion, parsing,
persistence APIs, and detail query; added a migration to drop the
related database table.
* **Tests**
* Removed unit and integration tests for Windows update parsing,
ingestion, persistence, and query inclusion.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-24 15:21:34 -03:00