Commit Graph
4684 Commits
Author SHA1 Message Date
Carlo 3660b546f2 Fix FMA auto-update keeping the stale install script (#50200)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #50097

## Summary

FMA auto-update preserves an admin-customized install script by
comparing the active script against the new manifest's, but FMA scripts
hardcode the versioned installer filename, so a routine version bump
looked like an edit and the old script (old filename) was kept against
the newly downloaded installer, and the install failed. The fix
neutralizes the installer filename in both scripts before comparing
(mirroring the existing uninstall `$PACKAGE_ID` handling), so a
filename-only difference adopts the new script while a genuine edit is
still preserved.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`
(`changes/50097-fma-auto-update-keeps-stale-install-script`).

## Testing

- [x] Added/updated automated tests (adopt-on-version-bump regression +
preserve-genuine-edit counterpart).
- [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 auto-updates that could keep an outdated
install script after downloading a newer version, causing install
failures.
* Improved install-script change detection by ignoring version-only
installer filename differences.
* Continued to preserve administrator-customized install scripts when
updates change more than just the installer filename.
* **Tests**
* Added and expanded coverage for installer-script normalization and
auto-update install-script selection behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-30 17:42:55 -04:00
CarloandAllen Houchins 0594f653dd Propagate errors in macOS FMA install scripts (#50198)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #50056

## Summary

macOS FMA install scripts never checked the exit code of the install
command (`installer -pkg` / `cp -R`) — the script's last statement is
always `relaunch_application`, which exits 0 — so a failed install
exited 0 and Fleet reported it installed.

**Generated scripts.** The generator now propagates failure: both
`installer -pkg` variants end with `|| exit $?`, and the `cp -R` path
exits non-zero on a failed copy, removes the partial copy (so a failed
fresh install isn't inventoried as the new version), and restores the
app it moved aside. Regenerated `outputs/` for non-frozen generated apps
are produced by the `ingest-maintained-apps` job, so they aren't
committed here.

**Custom scripts.** 9 of the 18 custom input scripts had the same bug
and are fixed with the same pattern: Google Chrome, Zoom, Microsoft
Edge, GitHub Desktop, Webex, Cycling '74 Max, Pd, Grammarly Desktop, and
P4V. The DMG-based ones also now fail before removing/moving the
existing app when the mount or staging copy fails, so a bad download
can't leave a host with nothing. Their `outputs/*/darwin.json` are
updated in the same commit (script content + recomputed 8-char sha256
ref, versions untouched), following the precedent of #49033. Docker
Desktop (`set -euo pipefail`), 1Password/Slack/LogiTune (installer is
the last statement), and the rest already propagated errors.

**Frozen apps.** The ingest job never rewrites frozen outputs, so the 10
frozen apps with generated scripts (adobe-acrobat-pro, comet, evernote,
firealpaca, keeper-password-manager, nvidia-geforce-now, pritunl,
vnc-viewer, wins, worksheet-crafter) had the fix applied directly to
their published `darwin.json` scripts — the exact text the current
generator would emit, with pinned versions/URLs/hashes untouched. The
11th frozen app (logi-options+) uses a custom script that was already
correct and in sync.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`
(`changes/50056-fma-install-scripts-ignore-errors`).
- [x] Untrusted data interpolated into shell scripts is validated
against shell metacharacters. (No new untrusted interpolation: the guard
reuses the same curated cask-derived name the adjacent lines already
interpolate.)

## Testing

- [x] Added/updated automated tests (three generator tests: pkg,
pkg-with-choices, cp-R restore — the last now pins the exact emitted
block).
- [x] All 19 updated output manifests validated: embedded scripts pass
`bash -n`, refs match `sha256(script)[:8]`, refs map stays key-sorted
like Go's encoder.
- [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 Fleet-maintained app installations now fail fast when
installers, DMG extraction/mounting, or app copy steps error.
* If an upgrade fails, the system removes any partial app and restores
the previously installed version when available.
* Improved robustness during app staging/copying, including safer
handling of paths with spaces or special characters.
* **Tests**
* Added unit coverage to verify installer failure propagation and
rollback behavior.
* **Documentation**
* Clarified that the install-script error handling applies to both
generated and custom scripts, including already-published frozen apps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Allen Houchins <allenhouchins@mac.com>
2026-07-30 14:22:02 -05:00
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 554c62e470 Add apps installed through Windows app store to software inventory
Resolves #14717 

Add apps installed through Windows app store to software inventory
2026-07-30 14:12:55 -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
Juan Fernandez 7f4ae81dc9 Moved changes file
46227-luks-key-escrow-any-slot changes were added as a Fleet server
change, should be an orbit change.
2026-07-30 12:19:46 -04: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
Magnus Jensen a1b35aeb23 fix icon text gap across product (#50209)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48217 

This does update a couple of places, but I checked and it should only be
for Status indicators.
Figma spec with 4px rather than 8px:
https://www.figma.com/design/gxvU745LfOdkE9AuRg64wi/%F0%9F%A7%A9-Product-design-system?node-id=858-23&t=QwTqGmpK0V3inArL-0

<img width="1119" height="407" alt="image"
src="https://github.com/user-attachments/assets/8cae52d6-dbb9-4b8d-80d6-1a899790ad82"
/>


# 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

- [ ] 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**
* Reduced the spacing between icons and text for a more compact,
consistent layout across the product.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-30 14:21:08 +02: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
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
Rahul Raghunathan 8f7a4ba2a0 Redirect to software inventory on 404 when switching fleets on title (#48389) 2026-07-28 13:58:55 -07:00
Rahul Raghunathan 5bdbcc8495 48792 fix actions dropdown layout shift (#49430) 2026-07-28 13:56:50 -07:00
Juan Fernandez 360e4b74ff Update macOS disk encryption banner copy for ADE-enrolled hosts
Relates #47832

Both the Host details and My device pages told the reader to log out or
restart when Fleet didn't have a Mac's FileVault key. That's wrong for
ADE-enrolled hosts: they escrow the key automatically, and the only
thing standing between the host and a cleared banner is the next vitals
refetch. It stays correct for manual enrollment, where Escrow Buddy only
generates a new key at next login.
2026-07-28 15:33:26 -04:00
LeAnn 418fd60e9c Clarify "Not supported" on Hosts page by adding tooltip (#49301)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #39987

- Added tooltips to the "Agent," "Last restarted," and "Status" column
headers on the Hosts page explaining which platforms are supported and
why.
- On the Host details page, vitals with a "Not supported" value are now
hidden instead of shown.
- Fixed the "Last restarted" vital showing on ChromeOS hosts, where it's
not actually collected.
- Updated the "Last opened" tooltip on the Host details Software table
to explain why it's only supported for native macOS, Windows, and Linux
apps and packages.
- Remove cellProps.rows.length === 1 workaround (which suppresses the
tooltip whenever the table has exactly one row) by adding the correct
CSS which removes the tooltip overflowing if host table is only 1 row

# Checklist for submitter

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

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

## Testing

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

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

- **New Features**
- Added/updated explanatory tooltips for Hosts table column headers
(Agent, Last restarted, Status) with clearer supported-platform wording.
- Clarified “Last opened” tooltip scope to native macOS, Windows, and
Linux app/package entries.

- **Bug Fixes**
- Removed “Last restarted” from Host details for ChromeOS hosts when the
value isn’t collected.
- Prevented vitals rows from rendering when their values resolve to “Not
supported,” and tightened “Last restarted” platform visibility.
- Fixed tooltip overflow/positioning in the single-row Host software
table case.

- **Tests**
- Updated and expanded vitals/header coverage to match the new display
rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 10:30:21 -07:00
Carlo 5250936179 Consistent URL/email on-blur validation across forms (#40410 follow-up) (#49932)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #40410

Follow-up to #48854, which added on-blur validation to the software
vulnerability automations webhook. QA found three more forms with
inconsistent validation, in two flavors:

- **Validated only on save** (no on-blur feedback): Policies > Manage
automations > Other workflows > Destination URL.
- **Errored before any input** (validation fired on mount/enable):
Settings > Users > Add/edit user > Email, and the host status webhook
Destination URL (both global and fleet settings).

This makes them consistent with the rest of the app: no error on
open/enable → validate on blur → clear the field's error as the user
edits → validate on submit.

- `OtherWorkflowsModal` — added an on-blur handler for the Destination
URL (guarded by the field's disabled condition).
- `UserForm` — on-blur now validates only the blurred field (so blurring
the autofocused Name no longer flags the empty Email/Password); submit
validates all fields.
- `GlobalHostStatusWebhook` — removed the `useEffect` that validated the
moment the webhook was enabled; validation now runs on blur and submit.
- `TeamSettings` (fleet host status webhook) — the Destination URL error
is no longer surfaced on change/enable; it validates on blur and submit.

# 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 (URL and email fields validate on
blur and on submit; no errors are shown before the user interacts).

## 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 webhook destination URL validation across integrations, team
settings, and workflow automation forms.
* Validation messages now appear after leaving the URL field, rather
than prematurely while enabling or editing.
* Prevented saving or submitting forms with missing or invalid
destination URLs.
  * Correctly clears validation errors once a valid URL is entered.
* Improved user form validation so field-specific errors appear only for
the field being reviewed, while submit continues to validate the full
form.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 13:23:48 -04:00
Sharon KatzandLucas Manuel Rodriguez 24e64614aa Fix resource exhaustion in MSI metadata parser (#49739)
Resolves https://github.com/fleetdm/confidential/issues/16902

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

## Reproduction

**Attack vector:** An authenticated Fleet admin uploads a crafted `.msi`
file via the software upload API. The server's MSI metadata parser
(`pkg/file/msi.go` > `ExtractMSIMetadata` > `decodeStrings`) processes
the file's `_StringPool` and `_StringData` streams during upload. A
malicious `_StringPool` can claim arbitrarily large string sizes that
trigger speculative memory allocation before the actual data is read.

**Manual reproduction:** Wrote a standalone Go program that constructs
the two raw byte streams the parser consumes (a 12-byte `_StringPool`
claiming a 64 MB string, and an empty 0-byte `_StringData`), then
measures heap allocation via `runtime.MemStats` before and after calling
the vulnerable code path.

**Before fix (vulnerable):**

```
Pool input size: 12 bytes
StringData size: 0 bytes
Claimed string size: 67108864 bytes (64 MB)

Error returned: failed to read string data: EOF
Heap allocated: 67117016 bytes (64.0 MB)
Amplification: 12 input bytes -> 67117016 byte allocation (5593084x)
```

12 bytes of pool input forced a 64 MB heap allocation via `buf.Grow()`
before `io.CopyN` discovered there was no data to read.

**After fix:**

```
Error returned: failed to read string data: EOF
Heap allocated: 3072 bytes (3.0 KB)
Memory amplification eliminated: true
```

Same input, 3 KB allocated instead of 64 MB. Without the speculative
`buf.Grow()`, `io.CopyN` grows the buffer incrementally based on actual
available data and immediately hits EOF.

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

## Summary by CodeRabbit

- **Bug Fixes**
- Fixed a potential resource exhaustion issue when processing MSI
metadata with unusually large string-size declarations.
- MSI files with missing string data are now handled without excessive
memory allocation.

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

---------

Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com>
2026-07-28 12:26:40 -04: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
LeAnn 84896a5687 Add tooltip explaining missing Refetch button for Android hosts (#50017)
<img width="632" height="196" alt="Screenshot 2026-07-27 at 3 09 16 PM"
src="https://github.com/user-attachments/assets/7bfc1440-48a4-4baf-8747-cc5e848a3a53"
/>

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

# Checklist for submitter

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

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

## Testing

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

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

## Summary by CodeRabbit

* **New Features**
* Added an explanatory tooltip to the “Last fetched” field for Android
hosts.
* The tooltip clarifies that Android hosts sync automatically and
therefore do not have a “Refetch” button.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 15:39:22 -07: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
Rajendra Kadam 17669eca02 Center-align setup experience app icons (#49962)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #46973

> **Draft:** on-device QA (Mac/iPhone/iPad) is pending hardware,
expected next week. Opening as draft for early review of the approach.

## Description

FMA and custom-package app icons were misaligned on the macOS setup
experience ("Setting up your device") screen — icons rendered at
different sizes and their "Install …" labels didn't line up.

**Root cause:** `SetupSoftwareProcessCell` forced `.software-icon__small
{ width: $pad-xlarge }` (32px, width only). That class lands on
different elements in `SoftwareIcon`'s two render paths, so it hit them
differently:
- **Fleet-maintained / VPP apps (icon URL)** → the class is on the
wrapper `<div>`; the 24px `<img>` inside stayed 24px, left-aligned.
- **Custom packages (no URL)** → the class is on the fallback **SVG**
itself, which got stretched to 32px wide.

Result: different icon sizes/positions by app type → the misalignment.

**Fix:** remove the width override so every app type renders
`SoftwareIcon` at its consistent, vertically-centered 24px "small" size.

## Testing

Verified in Storybook (added `SetupSoftwareProcessCell.stories.tsx`,
`MixedAlignment` story) with real matched brand icons (Chrome,
1Password, VS Code, Zoom), a generic custom-package icon, and a
URL/`<img>` icon stacked together:
- **Before** (with the override): icons render at mixed 24/32px sizes;
"Install …" labels don't align.

<img width="1135" height="398" alt="before"
src="https://github.com/user-attachments/assets/fe216ab4-0a7b-4211-b8aa-96e65e6ca3f5"
/>

- **After** (this change): all icons render at 24px, centered, labels
aligned.

<img width="1185" height="425" alt="after"
src="https://github.com/user-attachments/assets/28257eb8-df6e-4168-a0bb-28dda4bb40ba"
/>


On-device QA to follow once hardware is available.

## Notes for reviewer
- The `width: $pad-xlarge` (32px) was added in #33770, so 32px may have
been the *intended* icon size. This change makes them a consistent
**24px**. If a larger icon is desired, that's a follow-up done properly
via a real `SoftwareIcon` size (not a width-only override) — flagging
for PD input since this is `:product`-labeled.
- Included a Storybook story for visual verification/regression; happy
to drop it if that's not wanted here.

# 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

- [ ] QA'd all new/changed functionality manually <!-- pending on-device
QA -->
- Added a Storybook story for visual verification (not an automated
test).


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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved software process cell styling for more consistent icon sizing
and alignment.

* **Tests**
* Added Storybook scenarios covering fleet-maintained apps, custom
packages, uploaded icons, and mixed app layouts.


<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 19:42:22 +05:30
melpike af65282522 Align helper text with checkbox/radio labels (#49977)
Moves changes from #49920 to `main`.

Originally targeting `docs-v4.91.0` — retargeted to this branch.

**Related:** #49920

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

## Summary by CodeRabbit

* **Bug Fixes**
* Improved checkbox and radio button helper-text alignment so it lines
up with the associated label.
* Adjusted spacing between controls, labels, and helper text for a
cleaner form layout.

* **Documentation**
* Added component examples demonstrating checkbox and radio buttons with
helper text.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-27 08:07:54 -06: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
RachelElysia 3e3097cf49 Fleet UI: Drop I-beam cursor on non-underlined tooltips (#49859) 2026-07-24 07:01:48 -07: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 aa572dcca4 Show ABM organization name in edit-fleets success toast (#49877)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #48914

## Description

The success toast shown after editing fleet assignments for an Apple
Business Manager (ABM) organization read:

> Successfully updated fleets for AB token.

The trailing "AB token" made the message unclear. It now names the
organization instead, matching the modal's title:

> Successfully updated fleets for `<org name>`.

The organization name (`token.org_name`) was already available in the
component (it's used as the modal title), so this is a copy-only change
with no new data plumbing.

Note: the issue's expected behavior left the exact wording to Product
("TODO — Product to decide"). This implements Product's written
suggestion (`Successfully updated fleets for {org name}`) so the awkward
wording isn't blocking; the string is trivial to adjust if
Product/design prefer different phrasing in review.

## Testing

- Manually QA'd in the UI: with a configured ABM organization, edited a
fleet assignment and confirmed the toast now shows the org name.
- Existing unit tests for the modal's helpers (`getOptions`,
`getSelectedTeamIds`) still pass; no test asserts the toast string.
- `eslint` and `prettier` pass on the changed file (added
`token.org_name` to the `useCallback` dependency array to satisfy
`react-hooks/exhaustive-deps`).

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


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

## Summary by CodeRabbit

* **Bug Fixes**
* Updated the success notification shown after fleet teams are saved to
include the associated organization name.
* Ensured the notification always reflects the currently selected
organization.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-24 13:30:57 +05:30
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
RachelElysia 9cfd907be6 Fleet UI: Hide empty host summary card on Free-tier Android hosts (#49848) 2026-07-23 12:54:18 -07: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
RachelElysia 968ea20aeb Fleet UI: Searchable fleets dropdown with add-fleet affordance (#49690) 2026-07-23 07:26:04 -07: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
Steven Palmesano 62ed3583e6 Clear button styles (#49292)
**Related issue:** Resolves #49276

**New features**
- Added new "Secondary" (bordered, off-white fill) and "Subdued"
(borderless, low-emphasis) button variants to match the Figma spec,
alongside the existing Primary style.
- Allowed rows to be selected in Controls > OS updates.

**Cleanup**
- Once nothing referenced the old styles anymore, fully removed the old
`text-icon`, `brand-inverse-icon`, `inverse-alert`, `inverse`, and
`icon` button variants (type, styles, and Storybook entries) from the
shared `Button` component.
- Removed the `iconStroke` prop, which had become a no-op once the old
variants it supported were gone.
- Renamed `ActionsDropdown`'s variants
(`button`/`brand-button`/`small-button`) to
`subdued`/`primary`/`secondary` to match the same naming used everywhere
else.
- Replaced a one-off dropdown implementation on the Software title page
with the shared `ActionsDropdown` component, instead of maintaining
duplicate styling logic.
- Changed the button name on Host details > Reports > Report details
from "View data for all hosts" to "View report for all hosts" (to match
the previous page's Actions drop-down options).


# 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

<img width="1475" height="241" alt="Screenshot 2026-07-21 at 06 35 49"
src="https://github.com/user-attachments/assets/7cfbd444-7837-40e8-854e-bc5989d57d85"
/>
<img width="661" height="306" alt="Screenshot 2026-07-21 at 06 37 18"
src="https://github.com/user-attachments/assets/5d0c4873-8179-4089-b115-7e8cd3a53b4d"
/>
<img width="1427" height="423" alt="Screenshot 2026-07-21 at 06 37 30"
src="https://github.com/user-attachments/assets/a4850a60-f44a-4902-b45e-0094f23a52f8"
/>
<img width="1427" height="640" alt="Screenshot 2026-07-21 at 06 37 46"
src="https://github.com/user-attachments/assets/738a4a7f-cd7d-4162-b659-6f649c32204d"
/>
<img width="1445" height="479" alt="Screenshot 2026-07-22 at 07 03 22"
src="https://github.com/user-attachments/assets/4f672dc0-5c6d-4eb8-8465-ed5233fcd1b2"
/>
<img width="811" height="871" alt="Screenshot 2026-07-21 at 06 41 20"
src="https://github.com/user-attachments/assets/5421c96e-2dab-492a-af26-be0e5a7791ca"
/>
2026-07-23 07:11:59 -05: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
Victor Lyuboslavsky a7eb747faf Flag to bypass end user auth (#49683)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #46644 

Demo video: https://www.youtube.com/watch?v=svCaA-820yc
Docs: https://github.com/fleetdm/fleet/pull/49713/changes

# Checklist for submitter

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

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

## 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
  - Did not verify macOS.
- [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**
  * Added `--bypass-end-user-auth` to `fleetctl package` and Orbit.
* Generated Linux and Windows installers can skip the end-user
authentication prompt during enrollment.
* Added `ORBIT_BYPASS_END_USER_AUTH` for environment-based
configuration.
* End-user authentication remains enabled when a supported EUA token is
provided.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-22 15:32:58 -05:00