Commit Graph
5387 Commits
Author SHA1 Message Date
Nico 7f1b330c90 Restrict deleting a fleet to global admins (#50271)
# 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


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

* **Bug Fixes**
* Restricted fleet deletion to users with global write permissions,
including global administrators and GitOps.
* Corrected team deletion authorization to require global write access.
* Prevented global technicians, team technicians, and observer-level
users from deleting teams.
* Updated authorization behavior to consistently enforce the required
access level.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 08:43:51 -03:00
Rajendra Kadam 9d0f510a8d Add DDM custom activations schema (#50133)
**Related issue:** Resolves #49966

Adds the schema for custom DDM activations (parent story #48222).

- **Creates `mdm_apple_ddm_activations`** — stores the activation JSON
as-is (`mediumtext`, so the generated `token` column hashes the exact
stored bytes) with a `declaration_uuid` FK to `mdm_apple_declarations`
that cascades on delete.
- **Extends `mdm_configuration_profile_variables`** with
`apple_ddm_activation_uuid` so activations can carry Fleet variables
(needed by #49970).
- **Adds `activation_updated_at`** to `host_mdm_apple_declarations` so a
changed activation regenerates the declaration's effective token,
mirroring `variables_updated_at` / `assets_updated_at`.
- **Drops `mdm_apple_declaration_activation_references`** — created with
the original DDM tables in `20240327115530_AddDDMTables.go`, never
written to by any code path, so it is empty in every deployment.

### Deviations from the SQL in #49966

The `declaration_uuid` FK is the one addition, [confirmed with
@MagnusHJensen](https://github.com/fleetdm/fleet/issues/49966): it keeps
the 1:1 lifecycle enforced by the database rather than requiring cleanup
in every delete path. `configuration_identifier` is kept alongside it
for validation and DDM serving. Its unique key doubles as the FK's
backing index.

The rest are corrections needed for the specced SQL to work, all
following the precedent in `20260409153715_AddDDMVariablesSupport.go`:

- **`ck_mdm_configuration_profile_variables_exactly_one` is dropped and
re-added** to count the new column. That constraint requires exactly one
owner column to be non-null; adding a seventh without updating it means
any row setting `apple_ddm_activation_uuid` sums to 0, fails the check,
and is rejected.
- **`UNIQUE (apple_ddm_activation_uuid, fleet_variable_id)` added** to
match the six existing owner columns. That table's write path is `INSERT
... ON DUPLICATE KEY UPDATE`, which needs a unique key to collide on.
- **`activation_updated_at` is `DATETIME(6)`, not `TIMESTAMP(6)`** — its
siblings are `datetime(6)` and `EffectiveDDMToken` formats them into the
token string, so `TIMESTAMP`'s session-timezone conversion on read would
change tokens and re-push declarations to every host.
- **`team_id` gets `DEFAULT '0'`** to match `mdm_apple_declarations`,
where 0 is Unassigned.

### Note for #49970

`declaration_uuid` is `NOT NULL`, so the upload path must populate it in
addition to `configuration_identifier`. The declaration UUID prefix has
no separator (`MDMAppleDeclarationUUIDPrefix = "d"`, 1 char + 36-char
UUID = the full `varchar(37)`).

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

No changes file: this sub-task adds schema only and ships no
user-visible behavior.

## Testing

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

`TestUp_20260729115013` covers: the stale table is present before and
gone after; pre-existing `mdm_configuration_profile_variables` rows
survive the check constraint replacement (that `ADD CONSTRAINT`
revalidates every existing row); an activation attaches to a declaration
and gets its generated token; the 1:1 unique key and the FK both reject
bad inserts; a variable row binds to an activation (the case the old
constraint would have rejected); the constraint still rejects two-owner
and zero-owner rows; and deleting the declaration cascades to the
activation and through it to the activation's variable rows.

Also ran the full migrations suite (`MYSQL_TEST=1 go test
./server/datastore/mysql/migrations/...`) to confirm no other migration
is disturbed, and verified the regenerated `schema.sql` diff contains
only changes from this migration.

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

Neither modified table has an `ON UPDATE CURRENT_TIMESTAMP` column, so
no rows have their timestamps touched.


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

## Summary by CodeRabbit

* **New Features**
  * Added support for Apple DDM custom activations.
* Added activation-specific tokens and timestamps to support reliable
declaration updates.
* Enabled configuration variables to be associated with a specific
activation.
* Added validation to prevent duplicate or invalid activation
associations.
* Activations and related settings are now automatically removed when
their declaration is deleted.

* **Tests**
* Added coverage for activation creation, uniqueness, validation,
associations, token generation, and cascading cleanup.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-31 14:36:15 +05:30
Carlo a442d7af3a Python script-only packages: follow-on QA fixes (#50143)
**Related issues:** Resolves #50068, Resolves #50106, Resolves #50107,
Resolves #50108, Resolves #50110, Resolves #50114

Follow-on fixes from QA of #41470 (Python script-only packages):

- Software-installer validation errors are action-neutral, so the Add
and Edit flows each show the correct single verb, and the
unsupported-file error names a content/format mismatch instead of
blaming the extension (#50068, #50107).
- `.py` packages accept `setup_experience_platform` (`darwin`/`linux`),
matching `.sh` (#50106).
- A failed-to-run install script (exit code `-1`) now renders a
diagnostic instead of empty output, and orbit surfaces the underlying
execve error (#50108).
- The install-rejection message for `.sh`/`.py` packages says "macOS and
Linux hosts" instead of "linux" (#50110).
- Orbit writes each script's temp file with an extension matching its
shebang (`.py`/`.sh`/`.ps1`), so tracebacks reference the right file
type (#50114).

# Checklist for submitter

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

## Testing

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

## fleetd/orbit/Fleet Desktop

- [x] If the change applies to only one platform, confirmed that
`runtime.GOOS` is used as needed to isolate changes.
- [x] Verified compatibility with the latest released version of Fleet
(orbit-only change; the server↔agent `SoftwareInstallDetails` contract
is unchanged).


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

* **Bug Fixes**
* Improved installer validation and rejection messaging for
unsupported/invalid package contents (including correcting “add” vs
“edit” wording and avoiding duplicated phrasing).
* Added clearer diagnostics when install scripts fail to start
(including empty output cases).
* Corrected handling of script-only packages so Python scripts use the
proper script type/extension, reducing misleading tracebacks.
* Updated platform availability messaging so `.sh`/`.py` packages
display macOS+Linux support.
* **New Features**
* Python script-only packages can now specify macOS and Linux setup
experience platforms.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-30 14:40:24 -04:00
Carlo 294a172d11 Clarify maintained app download timeout errors (#50104)
**Related issue:** Resolves #48416

When adding a Fleet-maintained app, a large-installer download that's
canceled or times out now returns a clear message pointing at the likely
proxy/load-balancer timeout, instead of a raw `context canceled`.

# 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


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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved error messages when adding Fleet-maintained apps times out or
is canceled during large installer downloads.
* Added clearer guidance for configuring server, proxy, and load
balancer timeouts.
* Properly handles additional timeout and upstream cancellation
responses, including HTTP 408, 499, and 504.


<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-30 14:39:56 -04:00
Juan Fernandez b06cbde1de Exclude non-existent host IDs from host transfer activity
The host transfer endpoint recorded raw requested host IDs in the
transferred_hosts activity verbatim, letting an authorized user inject
fabricated IDs into the audit trail. Derive the activity's host IDs and
names only from hosts that actually exist, and skip the activity when
none exist.
2026-07-30 13:59:43 -04:00
Juan Fernandez e5b0f313f9 Fix password reset accepting case-mutated tokens
Reset tokens are base64url (case-sensitive) but the
password_reset_requests.token column used case-insensitive
utf8mb4_unicode_ci, so a case-mutated token copy still matched. Switch
the column to utf8mb4_bin for byte-exact comparison.
2026-07-30 13:59:24 -04:00
George Karr 8a65ecf20b Bound Android device reconciliation pagination loop (#49615) 2026-07-30 12:08:17 -05:00
Nico dcefd13130 Stop leaking live query campaign existence via the websocket results stream (#50210)
# 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


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

## Summary by CodeRabbit

* **Bug Fixes**
* Standardized websocket error responses when requested campaigns are
unavailable.
* Prevented campaign existence from being inferred through differing
error messages.
* Improved consistency for both nonexistent campaigns and campaigns
inaccessible to the current user.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-30 12:01:39 -03:00
c83ecc2231 Match Windows software with version in name to FMA software title
Resolves #44406

Windows programs report a version in their name (e.g. `Granola
7.373.2`), so each version created its own `software_title` and never
linked to the Fleet-maintained app installer's title (`Granola`), hiding
the uninstall action. macOS handles this via `bundle_identifier`;
Windows had no join key.

- Give matching Windows programs the canonical FMA name at ingestion
(name-prefix match), so all versions collapse onto the title the
installer owns. `software.name` is unchanged.
- Merge already-mismatched versioned titles onto the canonical title in
`ReconcileMaintainedAppSoftwareNames` (runs on FMA sync; no migration
needed).

---------

Co-authored-by: Tim Lee <timlee@fleetdm.com>
Co-authored-by: Juan Fernandez <juan@fleetdm.com>
2026-07-30 09:49:52 -04:00
Lucas Manuel Rodriguez 1397531199 Authorize packs before returning them in query responses (#50148)
- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

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

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

## Summary by CodeRabbit

* **Bug Fixes**
* Query responses now include pack details only when the requester has
permission to view them.
* Prevented pack metadata from being disclosed across fleets when query
names overlap.
* Corrected target selection labels and empty-state messaging for
fleet-based targets.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 13:35:47 -03:00
Jonathan Katz 1a0f0101cc Fix gitops not updating FMA installer (#50000)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49811 

# 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 Fleet-maintained app updates when a rebuilt installer keeps the
same version.
* Rebuilt installers now update their files, hashes, filenames, and
install scripts correctly.
* Prevented installers from being incorrectly skipped when their
contents differ despite matching versions.
* **Tests**
* Added coverage for same-version installer rebuilds and team-specific
caching behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 10:00:16 -04:00
Magnus Jensen e6118b4cc5 extra error message checks and correct escaping in error message (#50136)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40074 unreleased bug

<img width="539" height="141" alt="image"
src="https://github.com/user-attachments/assets/1ac8e2c2-236d-4567-a200-0eb35cce46e7"
/>


# Checklist for submitter

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

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

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

## Testing

- [x] Added/updated automated tests
- [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 configuration profile validation for unescaped special
characters in Apple payloads.
* Error messages now consistently indicate when characters like `&` and
`<` must be XML-escaped.
  * Updated error examples to show properly escaped guidance.
* Expanded test coverage to verify the standardized XML-escaping error
for additional failing scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 14:58:08 +02:00
Lucas Manuel RodriguezandCopilot Autofix powered by AI 9c2ef14947 Scrub device policy responses in Fleet Desktop (#50094)
- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

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

## fleetd/orbit/Fleet Desktop

- [X] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [X] Verified auto-update works from the released version of component
to the new version (see [tools/tuf/test](../tools/tuf/test/README.md))


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

* **Security Improvements**
* Updated device-authenticated policy and host-detail responses to omit
policy author identity fields and any raw SQL/query data.
* Device policy endpoints now return a device-safe policy representation
consistently.

* **Bug Fixes**
* Prevented administrative policy information from appearing in
device-authenticated host details and policy listings.

* **Tests**
* Strengthened integration coverage to verify device-safe responses
(required user-facing fields present; sensitive fields absent).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-29 09:34:31 -03:00
Nico f5ca4b5b0d Add Android support for custom host vitals (#49696)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49421

Custom host vitals (`$FLEET_HOST_VITAL_<id>`) already worked in scripts
and Apple/Windows configuration profiles, but Android configuration
profiles and managed app configuration explicitly rejected them at
upload to keep parity with `$FLEET_SECRET_*`. This left admins unable to
inject per-host vitals (e.g. an asset tag) into Android MDM
configuration the same way they can for every other platform.

For more context, prior PRs:
- https://github.com/fleetdm/fleet/pull/49334
- https://github.com/fleetdm/fleet/pull/49586

# 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

- Created an "Asset tag" host vital.
- Enrolled an Android device.
- Initially the test profile showed as "Failed" because no value was set
for the vital.
- Set a value for the vital, saw that it went from Enforcing to
Verified.

<img width="1446" height="510" alt="Screenshot 2026-07-24 at 8 57 46 AM"
src="https://github.com/user-attachments/assets/c0e2348c-e521-48f3-85cd-6f884689b2cd"
/>
<img width="1520" height="936" alt="Screenshot 2026-07-24 at 8 56 56 AM"
src="https://github.com/user-attachments/assets/169b9545-ec7a-429b-8f45-0e2740f61c77"
/>
<img width="1607" height="1136" alt="Screenshot 2026-07-24 at 8 57
30 AM"
src="https://github.com/user-attachments/assets/a8213745-b224-4a36-a54d-32152a15c377"
/>

Also tested the rejection cases:
- trying to upload a profile with an invalid custom host vital id
(either a non-numeric value, a numeric but non-existent ID, and
referencing a vital as a JSON key instead of a value)
- deleting a vital referenced in a profile



https://github.com/user-attachments/assets/e8b4acde-ddf4-41c0-b00a-5ab4945d0bc2



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

## Summary by CodeRabbit

* **New Features**
* Android app configurations and profiles now support custom host vital
placeholders (`$FLEET_HOST_VITAL_<id>`).
* Custom host vital values are expanded per device during Android
delivery.
* Managed Android profiles/configurations are automatically resent when
a referenced vital value changes.

* **Bug Fixes**
* Added validation for malformed, missing, or undefined vital references
during Android app association and profile/config uploads.
  * Prevented deletion of vitals referenced by Android profiles.
* Improved error handling and delivery failure details when a device
lacks a required vital value.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 08:22:57 -03:00
Rajendra Kadam 5983f9de40 Require Fleet MDM enrollment before escrowing macOS disk encryption key (#50042)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48965

## Description

Fleet was escrowing a macOS disk encryption key — and logging an
"escrowed a disk encryption key" activity — for hosts that aren't
enrolled in Fleet's MDM (e.g. still managed by Jamf, or with a leftover
`/var/db/FileVaultPRK.dat`). Because Fleet never installed its FileVault
escrow profile on such a host, the stored key is unusable: the cron
marks it `decryptable = 0` and `GET /hosts/:id/encryption_key` returns
422, so "Show disk encryption key" never appears. The result is a
misleading activity and a dead key row.

Root cause: the macOS key ingestion
(`directIngestDiskEncryptionKeyFileDarwin` and its `file_lines`
fallback) gated only on the disk being encrypted and disk encryption
being enabled for the host's team — it never checked Fleet MDM
enrollment. The Windows/orbit key path (`SetOrUpdateDiskEncryptionKey`)
already performs this check.

- **`server/service/osquery_utils/queries.go`** — added an
`IsHostConnectedToFleetMDM` guard to both macOS ingestion functions,
skipping archival (no key stored, no activity) when the host isn't
connected to Fleet MDM. Mirrors the existing Windows path.

Prevention only — this stops new bad escrows; it does not delete keys
previously escrowed for non-enrolled hosts.

## Testing

- **Unit** (`queries_test.go`): added a "host not connected to Fleet
MDM" case asserting neither ingestion function escrows when the host
isn't Fleet-MDM-connected, and initialized the
`IsHostConnectedToFleetMDM` mock so existing cases still pass.
- **Integration** (`integration_mdm_test.go`):
`TestMDMAppleHostDiskEncryptionWithDisabledEncryptionSetting` was
creating an orbit-only host (no Fleet MDM) and expecting escrow to
succeed — i.e. relied on the bug. Switched it to a Fleet-MDM-enrolled
host (`createHostThenEnrollMDM`), which is now required for escrow.
Passes.

# Checklist for submitter

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

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually <!-- covered by
automated integration test; live no-device repro is impractical, flagged
for reviewer -->


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

- **Bug Fixes**
- FileVault recovery keys are now archived/escrowed only for macOS hosts
that are connected to Fleet MDM.
- Hosts without an active Fleet MDM connection no longer attempt to
archive encryption keys.
- Disk-encryption key archival now cleanly reports MDM connectivity
errors when checks fail.
- **Tests**
- Added/updated coverage to verify both connected and disconnected host
scenarios, including ensuring no archival occurs when MDM is not
connected.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-29 14:01:35 +05:30
Victor Lyuboslavsky ffc85a42ae Add Windows admin account config (#49863)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48720 

Subtask of https://github.com/fleetdm/fleet/issues/43488
This PR only adds the Windows config, and doesn't mess with macOS
configs.

# Checklist for submitter

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.

## Testing

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

## 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)
- [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**
* Added managed local account settings for Windows to app and team
configuration, including GitOps support.
* Exposed an explicit enabled/disabled toggle in configuration output
and Fleet controls.
* Added licensing and Windows MDM prerequisites for enabling the
setting.

* **Bug Fixes**
* Managed local account enable/disable actions are now correctly
persisted and declaratively applied.
* Activity feed messages now display platform-specific (macOS vs
Windows) wording.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 12:10:33 -05:00
Andrew Mellor f1228c873d 47713 auld software update assets migration (#50036)
**Related issue:** Resolves #47713

- [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.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).


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

* **New Features**
* Added support for tracking available Apple OS update assets and
supported devices.
* Added per-host Apple OS update targets, deadlines, and resolution
status.
* Added configuration options for host target OS versions and deadlines.

* **Database**
* Updated the MySQL schema and migration seed data to include the new
tables and fleet variables, and to reflect updated migration/status
metadata.

* **Tests**
* Added migration tests to validate table creation, constraints,
defaults, and upsert behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 15:42:04 +01:00
Rajendra Kadam f2662ccaf5 Default setup experience account type to admin when serving team config (#50034)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49346

## Description

A fleet created before the managed local account keys existed (e.g. in
4.84.0) and never edited since has no `end_user_local_account_type` or
`enable_managed_local_account` in its stored config. `GET /teams/:id`
served these as `null`, so the *Setup experience → Users* card showed no
account-type selection and a wrongly checked, greyed-out "Create hidden
admin" box.

- **`server/fleet/teams.go`** — `Team.MarshalJSON` now falls back to
`"admin"` / `false` for these keys when they're unset, mirroring the
existing `AppConfig.MarshalJSON` fallback that already covers the global
("No team") config. This is the one serve path that was missing the
default; the save path (`TeamConfig.Value()`) already applied it, which
is why only untouched pre-4.84.0 fleets were affected.

Serve-time fallback only — no stored data is modified and the
account-provisioning logic is untouched.

> **Note for reviewers:** `Team.MarshalJSON` is also the serialization
used by `fleetctl get teams` / GitOps, which had the same `null` bug.
With this change those now emit `end_user_local_account_type: admin` and
`enable_create_local_admin_account: false` for teams that previously
showed `null` — matching what the global config already emits. The
get→apply roundtrip stays idempotent because the save path already
writes these defaults. Team goldens updated accordingly.

## Testing

- **Automated:** `TestTeamMarshalJSONMacOSSetupDefaults`
(`server/fleet`) — a team with the keys unset marshals to `"admin"` /
`false`, and explicitly set values still round-trip. Updated the
`fleetctl` team goldens (`TestGetTeams`, `TestApplyMacosSetup`,
`TestApplyMacosSetupDeprecatedKeys`) to reflect the defaulted output.
- **Manual:** simulated a 4.84.0 fleet by removing both keys from a
team's stored `config` JSON. On `main` the Users card showed no selected
radio and a checked, greyed hidden-admin box; on this branch the same
fleet shows **Admin** selected and the box unchecked, matching what
global "No team" already renders.

# Checklist for submitter

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

## 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**
* macOS device setup now applies correct defaults when managed local
account settings are missing from existing team configurations.
* The end-user local account type now defaults to **admin** and managed
local account creation defaults to **disabled** (false) unless
explicitly configured.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 20:04:41 +05:30
Rajendra KadamandMagnus Jensen 0504e5949e Add host_id and host_serial to Apple mdm_enrolled activity (#49969)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49777

## Description

Adds `host_id` and `host_serial` to the Apple `mdm_enrolled` activity so
IT admins can build automations on top of it, and surfaces the activity
on the individual host's activity timeline.

- **`server/fleet/activities.go`** — added `HostID` to
`ActivityTypeMDMEnrolled` and a `HostIDs()` method (mirrors the existing
`ActivityTypeMDMUnenrolled` pattern), so the activity is linked to the
host and appears on its timeline.
- **`server/mdm/lifecycle/lifecycle.go`** — populate `host_id` for
macOS/iOS/iPadOS enrollments. Account-driven user (BYOD) enrollments
have no hardware serial, so they report the enrollment ID as
`host_serial` too, keeping `host_serial` populated for automations
regardless of enrollment type.
- **Frontend** — new `MdmEnrolledActivityItem` component, registered in
the host past-activity component map (and the `IHostPastActivityType`
union), renders the now-host-linked `mdm_enrolled` activity on the host
details **Activity** card. There's no Figma, so the copy mirrors the
sibling `mdm_unenrolled` item (e.g. "Mobile device management (MDM) was
turned on for this host").

`host_id` uses `omitempty`, so Windows (`microsoft_mdm.go`) enrollments
keep their existing activity payload unchanged — Windows is
intentionally out of scope, handled in #47874, which also owns the
audit-log documentation update for the shared field.

> **For reviewer:** the ADUE `host_serial = enrollment_id` behavior
comes from the issue's test plan. It means `host_serial` and
`enrollment_id` carry the same value for BYOD. Flagging in case Product
would rather leave `host_serial` empty for ADUE and have automations
read `enrollment_id`.

## Testing

- **Automated:** `TestMDMEnrolledActivityHostIDAndSerial`
(`server/mdm/lifecycle`) covers device enrollment (`host_serial` =
hardware serial) and ADUE (`host_serial` = enrollment ID), both
asserting `host_id`/`HostIDs()`. Also verified `server/datastore/mysql`
`TestMDMEnrollment`, `server/activity/internal/mysql`
`TestListActivities`, and `server/service` `TestMDMTokenUpdate*` pass.
- **Live (simulated) manual macOS enrollment** via `osquery-perf`: the
`mdm_enrolled` activity recorded `host_id` + `host_serial`, and an
`activity_host_past` row linked it to the host (confirmed it shows on
the host timeline).
- **Frontend:** `MdmEnrolledActivityItem.tests.tsx` covers the rendered
copy for macOS/iOS/Android and the actor/no-actor variants; also
visually confirmed the activity renders on a host's Activity card in the
running app. `yarn jest`, `eslint`, and `tsc` pass.
- Updated the MDM integration tests (`integration_mdm_test.go`,
`integration_mdm_dep_test.go`, `integration_vpp_install_test.go`) whose
activity-detail and host-feed assertions changed now that `mdm_enrolled`
carries `host_id` and appears on the host timeline (feed assertions now
filter by activity type).
- **Pending on-device QA (next week):** DEP/ADE macOS and account-driven
user enrollment (iOS/iPadOS) on real hardware, per the issue's test
plan.
- Regression: Windows `mdm_enrolled` payload is unchanged (`host_id` is
omitted when zero); both platforms' `mdm_unenrolled` are unaffected.

# Screenshot for the frontend change

<img width="706" height="382" alt="Screenshot 2026-07-28 at 11 16 57 AM"
src="https://github.com/user-attachments/assets/8f57d129-f819-4399-8754-18b397a49db8"
/>

# Checklist for submitter

- [x] Changes file added for user-visible changes in `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
- [ ] QA'd all new/changed functionality manually <!-- manual macOS
verified via simulator; DEP + real-device ADUE pending next week -->


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

## Summary by CodeRabbit

* **New Features**
* Added support for rendering “MDM enrolled” in the host activity feed
with platform- and actor-aware messaging.

* **Bug Fixes**
* Updated Apple “MDM enrolled” activity details to include the correct
host identifier and serial/enrollment identifiers.
* Ensured host-scoped activity behavior applies only when the host is
known (host id present).

* **Tests**
* Expanded regression and integration coverage for “MDM enrolled”
activity details and feed contents, including VPP-related assertion
stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Magnus Jensen <magnus@fleetdm.com>
2026-07-28 14:27:42 +05:30
bf3e1bab99 Add Apple marketing names to backend, frontend, and an osquery table (#46482)
**Related issue:** Resolves
https://github.com/fleetdm/fleet/issues/46818 and
https://github.com/fleetdm/fleet/issues/48524.

# Checklist for submitter

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

## Testing

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

## fleetd/orbit/Fleet Desktop

- [x] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [x] If the change applies to only one platform, confirmed that
`runtime.GOOS` is used as needed to isolate changes
- [x] Verified that fleetd runs on macOS, Linux and Windows
- [x] Verified auto-update works from the released version of component
to the new version (see [tools/tuf/test](../tools/tuf/test/README.md))


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

* **New Features**
* Host lists and Host details now show human‑readable Apple hardware
marketing names (macOS, iOS, iPadOS) where available (e.g., "MacBook Pro
(16‑inch, 2021)"), replacing raw model identifiers.
* Hardware model displays fall back to the original model identifier for
non‑Apple or unmapped devices.

* **Bug Fixes / CSV**
* Exported host CSVs now align with the UI by using the marketing name
for Apple devices when available.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
2026-07-27 22:26:30 -03:00
Magnus Jensen 4c36caa453 pass validation for fleets gitops files for DDM assets (#49991)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49979 

# 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. (Unreleased bug)

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

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

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

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

## Summary by CodeRabbit

* **Bug Fixes**
* Prevented Fleet Free from attempting to apply premium-only Apple DDM
assets.
* Improved macOS DDM asset reconciliation so explicitly empty settings
can clear previously configured assets.
* Ensured GitOps and team configurations consistently recognize and
validate macOS asset settings.
* Restricted DDM asset processing to Premium deployments with configured
and enabled MDM.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 20:23:54 +02:00
Victor Lyuboslavsky 19efda2d1b Windows SCEP profiles now fail with non-printable chars (#49887)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47492 

Windows cert profile fails if challenge uses non-printable characters.
<img width="987" height="329" alt="image"
src="https://github.com/user-attachments/assets/04dc7c78-8e3e-41c8-823e-cb4a961a91eb"
/>


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

## Testing

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


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

## Summary by CodeRabbit

* **Bug Fixes**
* Windows SCEP profiles now fail with a clear error when the certificate
authority challenge includes characters not supported by Windows ASN.1
PrintableString.
* Prevents misleading “Verified” status when no certificate is
installed.
  * Preserves valid challenge values, including leading/trailing spaces.
* Improves Windows error tooltips by showing raw certificate-install
error details.

* **Tests**
* Added coverage for invalid/valid Windows SCEP challenge scenarios and
the updated error tooltip behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 12:49:14 -05:00
Andrew Mellor d06a4c222c 47700 abm token invalid errors (#49770)
**Related issue:** Resolves #47700

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

## Testing

- [x] Added/updated automated tests

- [ ] QA'd all new/changed functionality manually. **_Not able to do for
all code paths yet_**



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

* **New Features**
* Added `token_invalid` for Apple Business Manager tokens, automatically
tracked based on Apple responses.
* Enhanced host DEP assignment API responses with a structured
`dep_device_error` field to classify why device details couldn’t be
retrieved.
* **Bug Fixes**
* Improved error handling for DEP device lookup, distinguishing
invalid/rejected tokens, expired terms, not-found devices, server/API
errors, and unavailable/unspecified failures.
* Added regression and unit test coverage for ABM token invalidation and
DEP device error classification.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 10:35:27 +01:00
Jordan Montgomery 89f67544b4 Add support for fleet vars in scripts(controls scripts, software scripts/script-only packages and setup experience scripts) (#49781)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49511  and #46837 as a whole

# 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**
* Added support for Fleet built-in variables in host scripts, software
installer scripts, setup-experience scripts, and maintained-app
installer scripts.
* Variables are resolved per host at execution time; saved content
remains unexpanded.
* **Bug Fixes**
* Requests now validate Fleet variables up-front, with clear
script-specific error messages for unsupported variables.
* Added improved messaging when variable resolution fails during
execution.
* Enforced Fleet Premium licensing for script/installer flows that use
Fleet variables.
* **Documentation**
* Documented supported variables and Premium requirements, including
usage examples.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 11:24:42 +02:00
Sharon Katzandcopilot-swe-agent[bot] 5c127e5fe4 Fix software ingestion lock convoys and unbatched deletes (#49894)
**Related issue:** Resolves #49805, Resolves #48719

# 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

---

## Context

A customer (~2,500 hosts, v4.89.1) had their DB writer slammed with
`DELETE FROM host_software_installed_paths` statements carrying 30,000+
IDs each. These never completed, required repeated manual intervention,
and the table grew from 14.5M to 14.8M rows in 2 days. This is #49805.

While investigating, Victor linked #48719, a related `software_titles`
INSERT lock convoy issue seen in load tests. Both are in the same
software ingestion code path (`server/datastore/mysql/software.go`), so
this PR fixes both.

## Root cause

### #49805: Unbatched DELETEs on `host_software_installed_paths`

When a host's software changes, Fleet computes a delta and deletes stale
rows from `host_software_installed_paths`. The function
`deleteHostSoftwareInstalledPaths()` issued a **single** `DELETE FROM
host_software_installed_paths WHERE id IN (?)` with all IDs expanded by
`sqlx.In()`. With 30,000+ IDs and 14.8M rows in the table, these massive
statements held row locks for minutes, timed out, and never completed.
On the next agent check-in, the same (or larger) DELETE was retried,
creating a feedback loop where the table grew unboundedly.

Notably, the INSERT function for the same table
(`insertHostSoftwareInstalledPaths`) already batched at 500 rows. The
DELETE simply lacked the same treatment.

### #48719: INSERT IGNORE lock convoys on `software_titles` (related)

When a host reports software that Fleet hasn't seen before,
`preInsertSoftwareInventory()` runs `INSERT IGNORE INTO software_titles
(...)` inside a `withRetryTxx` transaction. For homogeneous fleets (many
hosts sharing the same software catalog, typical for imaged corporate
Windows machines), hundreds of concurrent goroutines try to INSERT
IGNORE the same title rows simultaneously.

Even though `INSERT IGNORE` is a no-op when the row already exists,
InnoDB still acquires row/gap locks on the unique index for the duration
of the enclosing transaction. With many goroutines holding or waiting on
the same index locks, the DB enters a "lock convoy" where sessions
serialize on locks they don't actually need. In load tests (40 Fleet
instances, 100K hosts, 141 identical Windows software items), this
produced 690 average active sessions on the writer and 85s fleet-wide
p99.

The existing read-first check
(`getIncomingSoftwareChecksumsToExistingTitles`) prevents the convoy at
steady state. But on cold start (empty `software_titles`, e.g. after
cleanup purges orphaned titles), the check finds nothing and all
goroutines race to INSERT the same titles.

## How I reproduced it

Started MySQL via `docker compose up -d mysql_test`, created a git
worktree.

### #49805

`TestHostSoftwareInstalledPathsDeleteExplosion`: Created a host with 500
software items and installed paths, then replaced all software with an
entirely new set. This triggers `deleteHostSoftwareInstalledPaths()`
with all 500 old IDs in a single unbatched DELETE statement. At 500 IDs
the local test completes quickly, but the structure confirms the
problem: at 30K+ IDs on production Aurora with 14M rows, these never
finish.

### #48719

`TestSoftwareTitlesInsertIgnoreLockConvoy`: Created 50 hosts, each
reporting 100 identical software items (simulating a homogeneous fleet).
Used a barrier to release all 50 goroutines simultaneously, then
measured two phases:

1. **Cold start** (empty `software_titles`): All 50 hosts concurrently
call `ds.UpdateHostSoftware()`.
2. **Steady state** (titles exist): Same 50 hosts re-ingest.

**Before fix:**
| Metric | Cold start | Steady state |
|--------|-----------|-------------|
| Wall time | 3.0s | 38ms |
| Avg per-host | 1,981ms | 29ms |
| **Convoy factor** | **79x** | |

The 79x slowdown confirms the lock convoy.

## How I fixed it

### #49805: Batch the DELETE at 500

Changed `deleteHostSoftwareInstalledPaths()` from a single `DELETE ...
WHERE id IN (all IDs)` to a loop that processes 500 IDs per batch,
matching the existing INSERT batching pattern in the same file.

### #48719: Three-layer defense against lock convoys

**Layer 1 - Move title INSERT IGNORE outside the transaction.**
Previously, `INSERT IGNORE INTO software_titles` ran inside
`withRetryTxx`, so locks were held for the full transaction duration.
Now each title INSERT is executed via `ds.writer(ctx).ExecContext()`
outside any transaction, auto-committing independently and holding locks
for microseconds.

**Layer 2 - singleflight per title key.** Added a `singleflight.Group`
on the `Datastore` struct. For each title, only one goroutine actually
executes the INSERT; concurrent goroutines wait and share the result.

**Layer 3 - In-process cache (`sync.Map`).** After a title is inserted,
its key is stored in `knownSoftwareTitleKeys`. Subsequent ingestions
check the cache first and skip the INSERT entirely.
`CleanupSoftwareTitles` clears the cache when it deletes orphaned
titles.

The three layers work together: the cache handles the common case (title
already known), singleflight handles the cold-start race (only one
INSERT per title), and auto-commit ensures even the winning INSERT holds
locks for microseconds.

## How I tested that it works

### New reproduction tests

- `TestSoftwareTitlesInsertIgnoreLockConvoy`: 50 concurrent hosts, 100
identical software items. Measures cold-start convoy factor and verifies
all 100 titles are created.
- `TestHostSoftwareInstalledPathsDeleteExplosion`: Full software
replacement path with 500 items per host, including concurrent hosts.

### Existing test suite

Ran all existing software tests including:
- `UpdateHostSoftware`, `UpdateHostSoftwareDeadlock`,
`PreInsertSoftwareInventory`
- `SoftwareTitleUpgradeCodeDriftMatch`,
`UpdateHostSoftwareSameBundleIDDifferentNames`
- `CleanupSoftwareTitles` (validates cache invalidation works correctly)
- `SaveHost`, `SyncHostsSoftware`, and ~80 other subtests

All pass.

### After-fix measurements

| Metric | Before fix | After fix |
|--------|-----------|-----------|
| Cold-start wall (50 hosts) | ~3.0s | ~1.4s |
| Cold-start avg per-host | ~1,981ms | ~594ms |
| Steady-state wall | ~38ms | ~7ms |
| Titles created correctly | 100/100 | 100/100 |

The remaining cold-start time is from other pipeline operations (`INSERT
IGNORE INTO software`, host_software linking), not from
`software_titles`.

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

- **Performance Improvements**
- Improved software inventory ingestion under large, concurrent
workloads, including more efficient handling of repeated software-title
inserts.
- Reduced lock contention when many devices report the same titles at
the same time.
- Batched deletions of installed software-path records to speed up large
updates.

- **Bug Fixes**
- Ensured deterministic, collation-safe software-title deduplication to
prevent incorrect or stale title mapping.
- Strengthened orphan cleanup behavior so caches are cleared when orphan
titles are removed.

- **Tests**
- Added stress/regression tests for software-title insert contention and
large installed-path delete workloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-07-24 15:46:14 -04:00
RachelElysia 09fea47ce4 Fleet UI: Handle long fleet names across the Fleets UI (#49216)
## Issue
Closes #47290

Also implements the "Cap free-text `maxLength` to the backend column
length" pattern established in [#49041 (patterns.md
thread)](https://github.com/fleetdm/fleet/pull/49041/files#r3572648691).

## Description
Fleet name inputs had no `maxLength` cap and no service-layer length
check, so a name >255 chars failed with a raw MySQL `Data too long`
error, and several UI surfaces didn't handle long names gracefully. This
PR fixes all four manifestations called out in the bug, plus a related
label-overflow case on the host details page, and hardens adjacent name
inputs across the app.

**Frontend fixes for #47290:**
- Create/Rename fleet name inputs now cap at 255 characters (matches
`teams.name varchar(255)`).
- Fleets table Name column uses `LinkCell` with `tooltipTruncate` +
`className="w400"` so long names truncate with an ellipsis and full-name
tooltip instead of overflowing across the Hosts/Users columns.
- Fleet-detail page header (`.team-details__team-header`): h1 gets
`overflow: hidden; text-overflow: ellipsis; white-space: nowrap;`,
`__team-details` gets `min-width: 0; flex: 1`, and `.action-buttons`
gets `flex-shrink: 0` + `white-space: nowrap` on buttons so *Manage
enroll secrets / Rename / Delete* no longer wrap to a second line when
the fleet name is long.
- Manage enroll secrets modal body — `__description` gets
`overflow-wrap: anywhere; min-width: 0` so a long `<b>{fleet name}</b>`
wraps within the modal instead of spilling out the right edge.

**Backend fixes for #47290:**
- New `fleet.MaxTeamNameLength = 255` constant.
- `NewTeam`, `ModifyTeam`, and `ApplyTeamSpecs` now return
`fleet.NewInvalidArgumentError("name", "may not exceed 255 characters")`
instead of surfacing a raw `Data too long` MySQL error. Covers UI, API,
and GitOps entry points.

**Broader consistency pass (per [#49041
thread](https://github.com/fleetdm/fleet/pull/49041/files#r3572648691)):**
- New shared `MAX_ENTITY_CHAR_LENGTH = 255` constant in
`frontend/utilities/constants.tsx`.
- Refactored 8 existing files that had ad-hoc `NAME_MAX_LENGTH = 255` /
`MAX_LABEL_NAME_LENGTH = 255` locals to use it.
- Slotted it into 16 additional `InputField` name/description inputs
that were missing a cap (API user, custom variable, certificate, label
name + description, pack name + description, and all 5 CA forms —
CustomEST, CustomSCEP, Smallstep, Digicert, Hydrant).
- Pruned dead FE length validators that can no longer fire now that the
DOM cap enforces the limit (certificate modal, custom variable modal,
both label helpers, both category modals). Unusual/shorter caps (e.g.
`varchar(64)`, custom business rules) still keep their inline validators
— silent truncation is only appropriate for the common 255-char norm.

**Bonus:** fixed the long-label overflow on the host details Labels card
by capping the pill button `max-width` at 300px.

## Screenrecording



https://github.com/user-attachments/assets/b917b72e-7437-4d0c-a1a1-c49b4b1c28ba



https://github.com/user-attachments/assets/3a3efbb2-09d8-4f47-9fd4-f158b3453b9e



https://github.com/user-attachments/assets/73e5e022-dc93-4381-82b3-be9549d050e6

Latest - max width 300px long label:

<img width="1106" height="262" alt="Screenshot 2026-07-23 at 11 29
24 AM"
src="https://github.com/user-attachments/assets/741fddbd-f78d-4578-a025-bddf64a81c25"
/>


## Testing

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

Test coverage:
- `CreateFleetModal.tests.tsx`, `RenameFleetModal.tests.tsx` — new case
per file asserting the name input's `maxLength === 255`.
- `AddCertificateModal.tests.tsx`, `Variables.tests.tsx` — the existing
"shows too-long error when pasting 256 chars" tests are now unreachable
via the DOM cap; converted to `maxLength === 255` assertions.
- `ee/server/service/teams_test.go` — `TestNewTeamNameValidation`,
`TestModifyTeamNameValidation`, and `TestApplyTeamSpecsNameValidation`
each get two new cases (accepts at the limit, rejects one over with the
expected error message).

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

* **Bug Fixes**
* Limited fleet, team, and other user-entered names and descriptions to
255 characters.
* Replaced database errors for oversized names with clear validation
messages.
* Prevented long fleet and label names from overflowing tables, headers,
modals, and host details.
  * Improved modal and dropdown layouts for long text.
* **Tests**
* Added coverage for character limits, boundary values, and multibyte
names.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-24 11:22:06 -04:00
Juan Fernandez aeb56916ee Block host enrollment with empty enroll secrets
VerifyEnrollSecret matched by exact string, so an empty enroll_secret
matched any stored empty secret and issued a valid node key. Guard the
shared chokepoint: reject empty/whitespace secrets before matching,
closing all enrollment paths (osquery, Orbit, Apple MDM, Android). Add a
migration to delete pre-existing empty secrets.
2026-07-24 10:29:46 -04:00
NicoandCopilot Autofix powered by AI 6bb0b1ea52 Stop leaking cross-team software title names via the hosts endpoint (#49638)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves: N/A

# 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


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

* **Bug Fixes**
* Corrected `software_title_id` filtering for hosts so software title
details are strictly scoped to the current team and never backfill
mismatched or out-of-scope data.
* Removed unintended debug output and ensured software title details
remain unset when the title isn’t accessible.
* **Tests**
* Added an enterprise integration test verifying cross-team
`software_title_id` behavior.
* Updated existing integration expectations for team-scoped visibility.
* Added datastore coverage for team-scoped software title name lookup
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-24 10:39:13 -03:00
Rajendra Kadam b22a046a96 Fix undeletable Apple DDM declarations when allowed types change (#49810)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #47535

## Description

`DeleteMDMAppleDeclaration` re-ran the upload-time validator
(`ValidateUserProvided`) on the delete path. That validator enforces
*upload-admission* rules — forbidden declaration types
(`ForbiddenDeclTypes`) and the `AllowAllDeclarations` config flag — so
any declaration that was accepted at upload time became **undeletable**
through the API once the accepted set later shrank. Two ways this
happens in practice:

- A server config flag that had been enabled at upload time is later
disabled (the original customer report, prod 4.86.1).
- A declaration type is added to `ForbiddenDeclTypes` in a later
release, after declarations of that type were already uploaded.

In both cases the UI showed "Couldn't delete. Please try again." and the
API returned `400` with an upload-validation message on a *delete*
request.

Whether a declaration is Fleet-managed (and therefore protected from
deletion through this endpoint) is already determined by the Fleet
reserved-name check that runs immediately above the offending block.
This PR removes the upload-time validation from the delete path and
relies solely on that reserved-name check, so:

- A user can delete any declaration they previously uploaded, regardless
of whether the current validator config would still accept it on upload.
- Fleet-managed declarations (reserved names) remain protected from
deletion.

The `AllowAllDeclarations` flag and `ValidateUserProvided` are unchanged
on the **add/upload** path — admission control still happens where it
belongs.

## Testing

Extended `TestMDMConfigProfileCRUD` (replacing the pre-existing `//
TODO: Add tests for create/delete forbidden declaration types?`) with
two cases:

- A declaration whose type is in `ForbiddenDeclTypes` can be deleted
(regression guard — fails before this change, passes after).
- A declaration with a Fleet-reserved name remains protected from
deletion (guards the reserved-name check that is now the sole
Fleet-managed gate — a boundary that was previously untested).

Manually verified end-to-end in the UI: reproduced the stuck declaration
(upload a forbidden type with `FLEET_MDM_ALLOW_ALL_DECLARATIONS=true`,
restart without the flag), confirmed the pre-fix `400`, then confirmed
deletion succeeds after the fix with the flag off.

# Checklist for submitter

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


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

* **Bug Fixes**
* Improved Apple MDM declaration deletion to avoid re-running
upload-time validation during the delete flow.
* Declaration deletion checks now rely on Fleet management status and
reserved naming, preserving protection for Fleet-managed declarations.
* Added/adjusted deletion behavior for restricted and Fleet-reserved
declaration types.
* **Tests**
* Added regression coverage for Apple declaration profile deletion via
the configuration profile delete endpoint, including strict-mode
scenarios and cleanup behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-24 11:10:38 +05:30
George Karr f75cd2e151 Bump migration timestamps after 4.89.2 cherry-picks (#49849) 2026-07-23 13:49:06 -05:00
Victor Lyuboslavsky f72de68f43 Fixed unreleased Windows cert ingestion perf issue (#49787)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49705 

Verified fix with 100k host Windows load test.

# Checklist for submitter

## Testing

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

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

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

- [x] Alerted the release DRI if additional load testing is needed

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved host certificate deduplication and automated self-healing
when duplicate certificate records are ingested.
* Updated source reconciliation to only change what’s stale, preventing
unnecessary rewrites of unchanged source entries.
* Ensured certificate sources consistently associate to the newest
canonical certificate record for each certificate hash.
* Improved certificate listing accuracy by returning a deduplicated set
of certificate/source pairs with correct usernames.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-23 13:16:48 -05:00
Magnus Jensen f7f0cfa98e test case and doc to ensure bootstrap package comes before profiles (#49808)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49750 

# 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. (Already a part of something else, this is just
further solidifying the current behaviour)

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

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

* **Tests**
* Added coverage to verify the command sequence during Apple device
enrollment.
* Ensures the fleet management agent installation happens first,
followed by the bootstrap package, and then configuration profile and
management commands.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-23 19:21:11 +02:00
Victor Lyuboslavsky e42dc7accf Improved the performance of the configuration profiles status summary (#48873)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48340 

Windows only. The fix is to use a rollup status table instead of
recalculating the host profile summary on demand.

Verified the fix in load test with 100k Windows MDM hosts. Note that
this does not improve the host details page filtered by OS settings,
which will be handled by the follow up
https://github.com/fleetdm/fleet/issues/48996

# 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

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

* **Bug Fixes**
* Improved Windows fleet configuration profile status summaries to avoid
timeouts on large fleets.
* Kept per-host Windows profile statuses accurate after profile updates,
resends, certificate changes, cleanup, unenrollment, and host deletion.
* Added automatic reconciliation to correct stale or orphaned status
data.
* **Data Integrity**
* Improved Windows profile status reporting, including profile and
BitLocker summaries, for more reliable results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-23 11:59:37 -05:00
Carlo 83f3f4b560 Add clear error for Firefox / Firefox ESR conflict (#49714)
**Related issue:** Resolves #49682

Mozilla Firefox and Firefox ESR are distinct Fleet-maintained apps that
share the macOS bundle identifier `org.mozilla.firefox`, so they resolve
to one software title. Adding both to a fleet previously gave a generic
conflict error (or no error at all). This adds a clear message — "Only
one of Mozilla Firefox or Mozilla Firefox ESR can be added to the same
fleet." — on both the single-add and GitOps/batch paths. The check is
general (any two FMAs sharing a bundle identifier), with the app names
filled in dynamically.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements).

## Testing

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


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

- **Bug Fixes**
- Prevented adding both Mozilla Firefox and Firefox ESR to the same
fleet when they share a bundle identifier.
- Updated the UI to show a specific conflict message explaining that
only one of the two can be added.
- Ensured existing workflows still work for adding new versions of the
already-selected app.
- **Tests**
- Added backend and frontend test coverage for the new conflict
detection and error-message formatting.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-23 12:39:17 -04:00
Sharon Katz f492a6a41d Enforce API-only endpoint restrictions on chart routes (#49477)
# 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

## Summary

Enforced API-only endpoint restrictions on chart endpoints, matching the
pattern already used by the activity bounded context. Also added
`RouteTemplateRequestFunc` to chart route server options so the
middleware can read the matched mux route template from context.

## Testing

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

### Reproduction

Created an API-only user with a restrictive endpoint allow-list (only
`GET /api/v1/fleet/hosts`). Confirmed that:
- Allowed endpoint (`/api/latest/fleet/hosts`) returns 200
- Non-allowed cataloged endpoint (`/api/latest/fleet/users`) returns 403
- Chart endpoint (`/api/latest/fleet/charts/uptime`) returned 200 before
the fix (the bug)
- After the fix, chart endpoint correctly returns 403

### Unit test

Added a test case in `server/service/middleware/auth/api_only_test.go`
that verifies an API-only user with endpoint restrictions is denied
access to chart endpoints not in their allow-list. The chart endpoint is
included in the test catalog (matching production), so the test
exercises the allow-list rejection path.

All 17 tests in the auth middleware package pass.

### Local verification

1. Confirmed the chart middleware in `cmd/fleet/serve.go` previously
called `auth.AuthenticatedUser(svc, next)` without
`APIOnlyEndpointCheck` wrapping
2. Verified the activity bounded context (same file) already uses
`auth.APIOnlyEndpointCheck(next)` as the correct pattern
3. Applied the same wrapping to the chart middleware
4. Added `RouteTemplateRequestFunc` to
`server/chart/internal/service/endpoint_utils.go` so the route template
is available in context (required by `APIOnlyEndpointCheck`)
5. Ran `go test ./server/service/middleware/auth/ -v` with all 17 tests
passing
6. Ran `make lint-go-incremental` with 0 issues
2026-07-23 10:44:13 -04:00
9d6f25acd7 Make MFA token redemption atomic to prevent multiple sessions
Resolves #16770

The MFA login token redemption path (`POST /api/latest/fleet/sessions`)
read the one-time verification token with a non-locking `SELECT` on the
read replica, then created a session and deleted the token in a
*separate* transaction without verifying the token was still present.
Concurrent requests carrying the same token each passed the `SELECT` and
each minted a distinct session, breaking the single-use guarantee.

`SessionByMFAToken` now consumes the token and creates the session
inside a single transaction:

- The token row is locked with `SELECT ... FOR UPDATE`, then deleted,
and the delete's rows-affected count is confirmed non-zero before the
session is created.
- Concurrent redemptions serialize on the row lock; the loser re-reads
after the winner commits the delete, finds no row, and aborts before
creating a session.
- The user is still loaded *before* the transaction, so a
concurrently-deleted user or a transient read error leaves the token
intact for retry (preserving the pre-fix atomicity behavior).

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Juan Fernandez <juan@fleetdm.com>
2026-07-23 09:52:07 -04:00
Juan Fernandez 4972732c75 Fix test failures by avoiding mutating shared state
Fix test failures by avoiding mutating shared state
2026-07-23 08:10:55 -04:00
Juan Fernandez fbccb8cc59 Emit created/deleted activities for setup experience scripts
Setup experience script add/replace/delete now record activities (API
and GitOps), skipping no-op re-submissions.
2026-07-23 06:41:51 -04:00
Juan Fernandez e91a0b2987 Normalize login responses for MFA-enabled accounts
Make failed logins for MFA-enabled accounts return a consistent response
and timing regardless of the cause, in line with authentication best
practices. Guidance for CLI users whose client can't complete email
verification is now surfaced by fleetctl on any login failure.

Added a `user_mfa_requested` activity, recorded when valid credentials
are submitted for an MFA-enabled account and a verification email is
sent.
2026-07-23 06:41:27 -04:00
Juan Fernandez d903ec58e1 Fix label update consistency issue
Persist label metadata and membership changes together in a single
transaction so a failed update can't leave a partial change behind.
2026-07-23 06:41:07 -04:00
Noah TalermanandRachael Shaw 42c0e4f408 Move "Install self-service software" endpoint to public REST API docs (#49618)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** N/A

# Checklist for submitter

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

## Summary

Moves the `POST
/api/v1/fleet/device/{token}/software/install/{software_title_id}`
("Install self-service software") endpoint out of the contributor-only
API reference (`docs/Contributing/reference/api-for-contributors.md`)
and into the public REST API docs (`docs/REST API/rest-api.md`), nested
under the existing `## Software` section.

- Added `### Install self-service software` to `docs/REST
API/rest-api.md`, right after `### Uninstall software`, with a TOC entry
and a note that it uses the device's authentication token instead of the
usual Fleet API token.
- Removed the TOC entry and body section for this endpoint from
`docs/Contributing/reference/api-for-contributors.md`. Sibling
device-authenticated self-service endpoints were left in place there
since only this one endpoint was moved.

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

## Summary by CodeRabbit

* **New Features**
* Added an API route allowing Fleet Desktop users to initiate
self-service software installations using a device token and software
title.

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

---------

Co-authored-by: Rachael Shaw <r@rachael.wtf>
2026-07-22 18:51:50 -05:00
Magnus Jensen b9136f4da5 Release from AB backend support (#49680)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49367 

# 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**
* Added “Release from Apple Business” for eligible Apple hosts,
including per-device success/failure reporting and activity logging.
* Added a new API endpoint to trigger the action and return results for
each selected host with clear error details.
* Introduced authorization rules for global admins and team admins to
release only within allowed scope.
* **Bug Fixes**
* Improved validation and error handling: rejects oversized selections,
reports unknown/ineligible hosts and DEP-related failures per device,
and treats assignment-cleanup failures as non-blocking.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 20:01:41 +02:00
Magnus Jensen 940706c9e8 log command UUIDs for fleetd and bootstrap in DEP release flow (#49754)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #

Super small change to make it easier to correlate bootstrap and fleetd
`InstallEnterpriseApplication` command UUID with log statements

# Checklist for submitter

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



## Testing


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


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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved logging for Apple MDM command delivery by including the
associated command identifier when application and bootstrap package
installation commands are sent.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 19:31:21 +02:00
Magnus Jensen ccaea1373b trim MDM SSO whitespace in GitOps and API (#49378)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48003 

# 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 MDM SSO configuration handling to automatically remove leading
and trailing whitespace from provider fields.
- GitOps-applied MDM SSO settings are now normalized before validation,
preventing otherwise invalid configurations caused by extra spaces.
  - Required-field and URL validation now operate on the cleaned values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 19:19:48 +02:00
Juan Fernandez 10ac3c73a3 Harden password reset token handling
Ensure a password reset token can only be used once.
2026-07-22 12:46:16 -04:00
Nico e017eb6176 Allow bypassing network blocking in production via config (#49747)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49751

A customer's egress proxy (an Envoy sidecar bound to loopback) was
getting blocked by Fleet's SSRF network-blocking check, since the check
applies to whatever address the HTTP transport dials, including the
proxy hop itself, not just the ultimate destination. There was no
supported way to disable this in production (the existing full-bypass
mode was dev-only), leaving no path forward for environments where
egress is already constrained by external infrastructure.

# 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

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

## Summary by CodeRabbit

* **New Features**
* Added a production server setting to bypass outbound network blocking
for integration requests when external egress controls are already in
place.
* The setting is disabled by default and can be configured through the
server configuration.

* **Documentation**
* Clarified that bypassing network blocking disables SSRF protections
for all outbound integration requests.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 13:23:21 -03:00
Victor LyuboslavskyandSharon Katz f04430a5a6 Added redirect_uri validation for Windows TOS endpoint. (#49699)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves
https://github.com/fleetdm/confidential/issues/16880

# Checklist for submitter

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

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

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

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

## Summary by CodeRabbit

* **Bug Fixes**
* Secured the Windows MDM Terms of Service endpoint against reflected
cross-site scripting.
* Strengthened validation for the `redirect_uri` used in Terms of
Service rendering, allowing only approved `https` and `ms-appx-web`
schemes.
* Unsafe, malformed, or non-allowlisted redirect values are now rejected
and not displayed.

* **Tests**
* Added integration and unit coverage to verify unsafe redirects are
blocked while valid ones continue to work.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Sharon Katz <121527325+sharon-fdm@users.noreply.github.com>
2026-07-22 09:24:40 -05:00
George Karr 3b32a526ee Fix 500 on Apple MDM enroll when host has no DEP assignment (#47963) (#49623)
**Related issue:** Resolves #47963

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements), JS
inline code is prevented especially for url redirects, and untrusted
data interpolated into shell scripts/commands is validated against shell
metacharacters.

## Testing

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

## Summary

Fixes a 500 seen via monitoring during `POST /api/mdm/apple/enroll`:

```
checking os updates settings serial [redacted]: getting team id for host: sql: no rows in result set
```

### Root cause

During DEP enrollment, `CheckMDMAppleEnrollmentWithMinimumOSVersion` →
`shouldOSUpdateForDEPEnrollment` calls
`GetMDMAppleOSUpdatesSettingsByHostSerial`, which joins `hosts` to
`host_dep_assignments` by serial. When no matching row exists yet — e.g.
the enrollment request arrives before the host / DEP assignment row is
created or replicated (replica lag / ordering) — `sqlx.GetContext`
returns `sql.ErrNoRows`.

The service layer already handles this case gracefully (skip the
OS-update check, allow enrollment to proceed) via
`fleet.IsNotFound(err)`. But the datastore wrapped the raw
`sql.ErrNoRows` with a plain `ctxerr.Wrap`, which does not implement the
`IsNotFound()` interface, so the graceful path never triggered and the
request 500'd.

### Fix

Convert `sql.ErrNoRows` into a proper `notFound` error in the datastore
method, matching the existing pattern used throughout `apple_mdm.go`.
This lets the existing service-layer graceful-skip path take over so
enrollment proceeds.


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

* **Bug Fixes**
* Fixed Apple MDM enrollment to continue gracefully when OS update
settings are missing because a host’s DEP assignment hasn’t been created
yet or hasn’t replicated.
* Prevented enrollment from failing with an unexpected 500 error by
returning a clear “not found” outcome instead.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 09:18:57 -05:00
Nico edf4a45df9 Stop the delete host endpoint from revealing out-of-fleet host existence (#49645)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves: N/A

# 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


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

* **Bug Fixes**
* Updated host deletion behavior to return consistent “not found”
responses when the host doesn’t exist or isn’t visible to the requester.
* Prevented out-of-scope delete attempts from disclosing whether the
target host exists (now returns “not found” instead of “forbidden”).
* Preserved “forbidden” errors when the host is visible but the
requester lacks delete permission.
* **Tests**
* Added/updated authorization and deletion coverage to verify the new
response-masking behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 11:12:16 -03:00
Nico 87c1a719c1 Support custom host vitals in host name templates (#49586)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #49489

Custom host vitals were skipped when host name template enforcement
(#38806) shipped, since both features were in development at the same
time. This adds `$FLEET_HOST_VITAL_<id>` support to host name templates,
matching the existing secret-variable pattern (validation, per-host
resolution, resend on value change).

I also introduced a new `IsInvalidReferencedCustomHostVitalsError` call
after Copilot's comment below.

# 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


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

## Summary by CodeRabbit

* **New Features**
* Added support for `$FLEET_HOST_VITAL_<id>` in Apple host name
templates.
* Device-name template reconciliation now expands referenced per-host
vital values and updates automatically when those values change.

* **Bug Fixes**
* Prevents deleting custom host vitals that are referenced by host name
templates.
* If a referenced vital has no value for a host, device-name delivery is
marked failed for that host (retryable).

* **Improved Error Handling**
* Refined validation behavior so unknown/malformed vital references
return user-facing invalid-argument errors, while infrastructure errors
propagate unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 09:38:24 -03:00