main
3640
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4e6591e09d |
Reconcile stuck Android MDM commands via AMAPI operations.get (#50177)
**Related issue:** Resolves #46145 |
||
|
|
051d12718c |
Add JetBrains ReSharper as a Windows Fleet-maintained app (#50659)
**Related issue:** Resolves #50567 Adds JetBrains ReSharper as a Windows Fleet-maintained app (winget `JetBrains.ReSharper`, version `2026.2.0.2`). ReSharper is a Visual Studio extension rather than a standalone app, so it does not follow the pattern of the other JetBrains FMAs (Rider, PhpStorm, DataGrip, etc.), which are plain NSIS installers that take `/S`. Reviewers should read the risks below before approving — a couple of things can only be confirmed from a validator run. ## What's here - `ee/maintained-apps/inputs/winget/resharper.json` - `ee/maintained-apps/inputs/winget/scripts/resharper_install.ps1` / `resharper_uninstall.ps1` - Generated `ee/maintained-apps/outputs/resharper/windows.json` + `apps.json` entry - Icon (`Resharper.tsx`, website PNG, alphabetical `index.ts` entries), generated from JetBrains' own brand asset ## Decisions that differ from the other JetBrains FMAs **`use_display_version_for_patch` is omitted.** Every other JetBrains winget input sets it, but the ReSharper manifest has no `AppsAndFeaturesEntries`, so the ingester hard-errors with `use_display_version_for_patch is set but no DisplayVersion found in winget manifest`. The patch policy therefore compares against winget's `2026.2.0.2`. **Custom `exists_query` instead of `fuzzy_match_name`.** The prefix is loose enough to match a possible per-VS-instance suffix, and excludes the separate ReSharper C++ and ReSharper SDK products: ```sql SELECT 1 FROM programs WHERE name LIKE 'JetBrains ReSharper%' AND name NOT LIKE 'JetBrains ReSharper C++%' AND name NOT LIKE 'JetBrains ReSharper SDK%' AND publisher = 'JetBrains s.r.o.'; ``` **Install script detects Visual Studio.** It builds `/VsVersion` from the instances `vswhere` reports and runs the installer with `/Silent=True /PerMachine=True /SkipEtwService=True`: - `/PerMachine=True` — the installer otherwise targets `%LocalAppData%`, which under Fleet's SYSTEM context would land in the SYSTEM profile instead of the developer's. The path is not configurable ([RSRP-428991](https://youtrack.jetbrains.com/issue/RSRP-428991)). - `/SkipEtwService=True` — JetBrains documents that `EtwHostService.msi` always raises a UAC prompt, so a fully silent install of every component is not possible ([SUPPORT-A-3189](https://youtrack.jetbrains.com/articles/SUPPORT-A-3189)). - It then waits for the uninstall registry entry (what osquery reads), because the web bootstrapper can outlive its own exit code, and logs the resulting ARP entries. **Uninstall removes every matching entry**, since ReSharper registers one per Visual Studio instance, using the defensive `UninstallString` parser and appending `/Silent=True` rather than the NSIS `/S`. Switches come from [JetBrains' silent install/uninstall article](https://resharper-support.jetbrains.com/hc/en-us/articles/207241485-How-to-use-silent-install-and-silent-uninstall-of-ReSharper-via-Command-Line), not guesswork. ## `unique_identifier` is provisional `program_publisher` is verified — `JetBrains s.r.o.` is hard-coded next to the ARP value names (`DisplayName`, `DisplayVersion`, `UninstallString`, `Publisher`) in `JetBrains.Platform.Installer.exe`, extracted from the installer. The **DisplayName is not verifiable offline.** The winget URL is a two-stage web bootstrapper: the 69 MB `.web.exe` contains `JetBrains.Platform.Installer.Bootstrap.exe`, which downloads the JetBrains dotUltimate installer, which downloads the product packages. The ARP entry is written by that downloaded stage under `Software\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}`, with `DisplayName` taken from a per-VS-host `PresentableName`. So `JetBrains ReSharper` is a best-supported guess. It can be confirmed from a validator run: `cmd/maintained-apps/validate/windows.go` searches `programs` with a loose `LOWER(name) LIKE '%…%'` on both the catalog name and `unique_identifier`, and logs `Found app: '<DisplayName>' … Version: <ver>` after running `MutateSoftwareOnIngestion`. That reveals both the true DisplayName and the post-mutation version. The install script prints the same information. **Expect a follow-up commit correcting `unique_identifier` (and possibly the exists query) once that log lands.** ## Risks 1. **Payload is not pinned.** The SHA covers only the 69 MB bootstrapper; roughly 1.7 GB is fetched from `download.jetbrains.com` at install time. JetBrains publishes only a `windowsWeb` download for ReSharper, so there is no offline installer to point at. Install duration may exceed script timeouts. 2. **Requires Visual Studio.** With no VS present the installer has nothing to install, so the script exits 1 with a clear message. `windows-latest` runners ship Visual Studio 2022 Enterprise, so validation should be able to install. 3. **`/PerMachine=True` conflicts with pre-existing per-user installs.** JetBrains states machine-wide mode "is not compatible with existing installations in user profiles"; one must be removed first. 4. **`/SkipEtwService=True` omits the ETW host service**, so dotTrace/dotMemory profiling integration is incomplete. This is the documented tradeoff for an unattended install. 5. **Version reconciliation unconfirmed.** If the DisplayName ends in a marketing version, the JetBrains name-based version mutation fires and the validator's prefix check passes; a VS-suffixed name would instead fall back to the registry `DisplayVersion`. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] `go test ./ee/maintained-apps/...` passes; both output JSON files parse; generated SHA matches the winget manifest. - [ ] QA'd all new/changed functionality manually — **not done.** Install/uninstall need a Windows host with Visual Studio; relying on the FMA Windows validator, which is also how `unique_identifier` gets confirmed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added ReSharper to the maintained Windows applications catalog. * Added support for silent machine-wide installation and uninstallation. * Added Visual Studio compatibility checks and installation failure reporting. * Added a ReSharper icon for display in the software catalog. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
d9a2445832 |
Bump socket.io-parser from 4.2.6 to 4.2.7 in /website (#50504)
Bumps [socket.io-parser](https://github.com/socketio/socket.io) from 4.2.6 to 4.2.7. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/socketio/socket.io/releases">socket.io-parser's releases</a>.</em></p> <blockquote> <h2>socket.io-parser@4.2.7</h2> <h3>Bug Fixes</h3> <ul> <li>honor toJSON() when deconstructing a binary packet (<a href="https://redirect.github.com/socketio/socket.io/issues/5518">#5518</a>) (<a href="https://github.com/socketio/socket.io/commit/57f111439513809c633f2554be2f3104e4ad432c">57f1114</a>)</li> <li>reject binary packets with zero attachments (<a href="https://github.com/socketio/socket.io/commit/7c6ef571a00656718e9e05e3b948fd1758b2a7b4">7c6ef57</a>)</li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/spokodev"><code>@spokodev</code></a> made their first contribution in <a href="https://redirect.github.com/socketio/socket.io/pull/5518">socketio/socket.io#5518</a></li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/socketio/socket.io/commit/4054894738817f5a2125e6e6b18e79d92c75ab33"><code>4054894</code></a> chore(release): socket.io-parser@4.2.7</li> <li><a href="https://github.com/socketio/socket.io/commit/7c6ef571a00656718e9e05e3b948fd1758b2a7b4"><code>7c6ef57</code></a> fix(parser): reject binary packets with zero attachments</li> <li><a href="https://github.com/socketio/socket.io/commit/57f111439513809c633f2554be2f3104e4ad432c"><code>57f1114</code></a> fix(parser): honor toJSON() when deconstructing a binary packet (<a href="https://redirect.github.com/socketio/socket.io/issues/5518">#5518</a>)</li> <li><a href="https://github.com/socketio/socket.io/commit/8d2e4f7b8671ed2c4fa1fca9ec6f68e5cf82266e"><code>8d2e4f7</code></a> docs(security): add CVE-2026-59724 and CVE-2026-59725</li> <li><a href="https://github.com/socketio/socket.io/commit/d2d753fed4435015c2d83fe62e676b44e07fa3f7"><code>d2d753f</code></a> refactor(sio): align client file matching in Node.js HTTP server</li> <li><a href="https://github.com/socketio/socket.io/commit/dfb5ab3b6a2f00d1ecb226939022ea56e5701124"><code>dfb5ab3</code></a> fix(sio): prevent uWebSockets.js from serving missing client files</li> <li><a href="https://github.com/socketio/socket.io/commit/dcbd96154352dd84c189b41e5c0ec0df4b7fe6a5"><code>dcbd961</code></a> perf(eio): optimize polling request body buffering</li> <li><a href="https://github.com/socketio/socket.io/commit/6bb2e7f5fb7c548f0776328b4a19719723e27efd"><code>6bb2e7f</code></a> refactor(sio): internalize base64id dependency</li> <li><a href="https://github.com/socketio/socket.io/commit/a80711a8c3b11a3e75a84de567ac4d84820f4714"><code>a80711a</code></a> refactor(eio): internalize base64id dependency</li> <li><a href="https://github.com/socketio/socket.io/commit/8bead0f4c6fba027996bb9eab02f05120f35513d"><code>8bead0f</code></a> chore: upgrade to TypeScript 6</li> <li>Additional commits viewable in <a href="https://github.com/socketio/socket.io/compare/socket.io-parser@4.2.6...socket.io-parser@4.2.7">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/fleetdm/fleet/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
f145c778b8 |
Website: log number of android enterprise requests in the past minute (#50780)
Related to: https://github.com/fleetdm/fleet/issues/49212 Changes: - Updated the custom hook to create `sails.androidProxyApiRequestCount`, and to log and reset the value every minute - Updated android proxy endpoints to increment `sails.androidProxyApiRequestCount` every time a request to the Android management API is sent <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Monitoring** * Added comprehensive tracking for Android Management API requests across enrollment, enterprise, device, application, policy, and command operations. * Added periodic request-count logging and automatic resets when Android Enterprise credentials are configured. * Improved reporting alignment with minute-based API limits while keeping logs quiet during periods without requests. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5db78a63e3 |
Add Visual Studio 2022 (Community/Professional/Enterprise) as Windows FMAs (#50717)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #50653 Adds **Visual Studio 2022 Community, Professional, and Enterprise** as Windows Fleet-maintained apps. customer-universitas needs all three editions. ## What's here - Three input files, one per edition, each pointing at its own winget package (`Microsoft.VisualStudio.2022.{Community,Professional,Enterprise}`, all at `17.14.37`). - A shared install script (`visual_studio_2022_install.ps1`) — the downloaded file is a ~4 MB bootstrapper, not the IDE. The real multi-GB payload downloads from Microsoft *during* the install script, so install time depends on the host's network speed and counts against Fleet's 1-hour software-install timeout. `--wait` is required or the bootstrapper forks the real install to a background process and returns almost immediately. - Three uninstall scripts (one per edition) that resolve the install path via `vswhere.exe -products Microsoft.VisualStudio.Product.<Edition>` and call `vs_installer.exe uninstall --installPath <path> --quiet --norestart --wait`, since VS has no normal `UninstallString`. - Both scripts map winget's documented `3010`/`1641` (reboot pending/initiated) to a successful exit, and fail clearly on `1001`/`1618` (another VS Installer operation already running). - Default install ships the bare IDE shell (no `--add` workloads) — matches plain `winget install` behavior, per the issue's own conclusion that this needs no special-casing. - Icons: no scriptable source (no Windows host to extract the real per-edition `.exe` icon, and Microsoft's own download pages don't expose one) turned up distinct Community/Professional/Enterprise badge art, so all three currently use the same public Visual Studio mark ([Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Visual_Studio_Icon_2022.svg), marked public domain). **Flagging for #g-software Product Designer** to swap in the real per-edition badges if we have them. ## What I could not verify (no Windows host in this environment) - `unique_identifier`/publisher (`Visual Studio Community/Professional/Enterprise 2022`, publisher `Microsoft Corporation`) are taken from the winget locale manifest, not confirmed against a live registry entry. - The version-string quirk the issue calls out: winget's `AppsAndFeaturesEntries.DisplayVersion` is `"17.14.37 (July 2026)"`, not a clean version. I deliberately did **not** set `use_display_version_for_patch` — feeding that non-numeric string in as the patch target would break `version_compare` ordering across future version bumps (see the comment in `ingester.go`). Instead the patch policy compares against the plain winget `PackageVersion` (`17.14.37`), same as most winget FMAs. This should hold up if `version_compare` reads leading numeric-dot segments and ignores the trailing text, but I can't confirm that against real `programs.version` output without a host. - Whether `vs_installer.exe` actually honors `--wait` for `uninstall` the way the bootstrapper does for `install` — Microsoft's own docs say `--wait` "can only be passed into the bootstrapper; the installer (setup.exe) doesn't support it," which is in tension with the exact command this issue asked for and what I've seen used in the wild. Worth watching in validation logs. - End-to-end install timing on a normal (non-datacenter) connection, within the 1-hour timeout. # Checklist for submitter - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. <!-- Not added — no precedent for a changes file on FMA-addition PRs (e.g. #50553, TeamViewer Host). --> - [ ] 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] `apps.json` is valid JSON with descriptions filled in for all three editions - [x] Generator output reviewed: exists/patched queries, SHA256 (matches the live winget manifest), installer URLs - [x] `go build`/`go test ./ee/maintained-apps/...` pass; no shared ingester/validator code changed - [ ] FMA validator: install → detect → uninstall on a Windows host — **pending, needs a Windows host** - [ ] QA'd all new/changed functionality manually — **pending, same reason** ## FMA-specific (from issue #50653's acceptance criteria) - [x] Edition scope decided and recorded on the issue (all three: Community, Professional, Enterprise) - [x] Input added under `ee/maintained-apps/inputs/winget/` - [x] Custom install script handles `3010`/`1641` as success and fails clearly on `1618`/`1001` - [x] Custom uninstall script resolves the install path via `vswhere` and calls `vs_installer.exe uninstall` - [ ] Identity fields verified against a real installed host — **not yet, see above** - [ ] Patch policy verified against actual `programs.version` — **not yet, see above** - [ ] Install verified end to end within the 1-hour timeout on a normal-speed connection — **not yet** - [ ] Passes the FMA validator: install → detect → uninstall — **not yet** - [x] Icon exists (shared placeholder mark across all three editions — flagged for PD) No shared/ingester/validator code changed. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added Visual Studio 2022 Community, Professional, and Enterprise editions to the software catalog. - Added support for installing and uninstalling each edition with quiet execution, installation detection, error handling, and reboot handling. - Added version 17.14.37 metadata and update detection. - Added Visual Studio branding and edition-specific icons throughout the software interface. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
b5940c8eca |
Bump brace-expansion in /website (#50453)
Bumps and [brace-expansion](https://github.com/juliangruber/brace-expansion). These dependencies needed to be updated together. Updates `brace-expansion` from 2.1.2 to 2.1.4 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/juliangruber/brace-expansion/commit/b25213dff0446d622f97d736420b9830ee1abc32"><code>b25213d</code></a> 2.1.4</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/1e30c930238d7162802d88a94189182def178dac"><code>1e30c93</code></a> Merge commit from fork</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/878df3989e816dfb28cbe0d64de0b88738ff0ed6"><code>878df39</code></a> 2.1.3</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/c8bd93cfff4e45cb295557d2be17e1d1d4e52a11"><code>c8bd93c</code></a> npm ignore .claude</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/d13ff455a58b0d56704f0111e3c2a0b16ceb06eb"><code>d13ff45</code></a> fix: backport GHSA-mh99-v99m-4gvg (<a href="https://redirect.github.com/juliangruber/brace-expansion/issues/130">#130</a>)</li> <li>See full diff in <a href="https://github.com/juliangruber/brace-expansion/compare/v2.1.2...v2.1.4">compare view</a></li> </ul> </details> <br /> Updates `brace-expansion` from 1.1.16 to 1.1.18 <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/juliangruber/brace-expansion/commit/b25213dff0446d622f97d736420b9830ee1abc32"><code>b25213d</code></a> 2.1.4</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/1e30c930238d7162802d88a94189182def178dac"><code>1e30c93</code></a> Merge commit from fork</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/878df3989e816dfb28cbe0d64de0b88738ff0ed6"><code>878df39</code></a> 2.1.3</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/c8bd93cfff4e45cb295557d2be17e1d1d4e52a11"><code>c8bd93c</code></a> npm ignore .claude</li> <li><a href="https://github.com/juliangruber/brace-expansion/commit/d13ff455a58b0d56704f0111e3c2a0b16ceb06eb"><code>d13ff45</code></a> fix: backport GHSA-mh99-v99m-4gvg (<a href="https://redirect.github.com/juliangruber/brace-expansion/issues/130">#130</a>)</li> <li>See full diff in <a href="https://github.com/juliangruber/brace-expansion/compare/v2.1.2...v2.1.4">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/fleetdm/fleet/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Luke Heath <luke@fleetdm.com> Co-authored-by: Eric <eashaw@sailsjs.com> |
||
|
|
e6f1abe5a2 |
[Route] Add route for DDM activations learn more page (#50465)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #48222 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added a redirect from the DDM activations information page to the relevant section of the Custom OS Settings article. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Eric <eashaw@sailsjs.com> |
||
|
|
7e22e9f811 |
Add route for manual sync guide (#50693)
**Related issue:** #50001 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a redirect from the Android manual sync learn-more page to the updated setup guide. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
392f62dc08 |
[Route] Add route for Linux disk encryption guide (#50191)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #48654 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a redirect from the Linux disk encryption information page to the relevant enforcement guide section. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
39d57caa09 | Release article: Fleet 4.90.0 (#50240) | ||
|
|
ecdf1ff003 |
Add TeamViewer Host as a Windows Fleet-maintained app (#50553)
**Related issue:** Resolves #50332 Adds **TeamViewer Host** as a Windows Fleet-maintained app. ## The issue's premise was wrong — this needed no new capability #50332 was blocked on "not in winget." It is in winget: `TeamViewer.TeamViewer.Host`, published continuously since **June 2023** (v15.42.8), currently **15.80.4** (merged upstream as microsoft/winget-pkgs#409335, 2026-07-29). I downloaded the live x64 installer and its SHA256 matches that manifest byte-for-byte. It reads as absent because it's nested a level deeper than you'd expect — `manifests/t/TeamViewer/TeamViewer/Host/`, a sibling of the full client's *version* directories rather than of the publisher's package directories. Consequence: this comes off the dependency on #50364, and that FR loses one of its three examples. The other two (#50328, #50329, OLE DB Driver 18/19) still hold — there is no OLE DB package under any winget publisher. ## Identity verified against the installer, not winget metadata Per the `new-fma` golden rule, from the x64 MSI's `Property` and `Registry` tables: | Field | Value | Evidence | |---|---|---| | `unique_identifier` | `TeamViewer Host` | MSI `ProductName`; no `ARPDISPLAYNAME`, empty `Registry` table, so this is the ARP DisplayName | | publisher | `TeamViewer` | MSI `Manufacturer`, equal to the winget locale `Publisher` → no `program_publisher` override | | `installer_scope` | `machine` | `ALLUSERS=1` | | bootstrapper? | No | no `ARPSYSTEMCOMPONENT` | Corroborated independently by [silentinstallhq's PSADT script](https://silentinstallhq.com/teamviewer-host-install-and-uninstall-powershell/), which detects the app with `Get-InstalledApplication -Name 'TeamViewer Host'` and uses `/S` for both install and uninstall. Generated exists query: ```sql SELECT 1 FROM programs WHERE name = 'TeamViewer Host' AND publisher = 'TeamViewer'; ``` No collision with the existing `teamviewer/windows` FMA, which generates an exact `name = 'TeamViewer'`. ## Why exe + `ignore_hash`, matching the full client winget offers Host as an NSIS exe and as a nested `wix` MSI inside a zip. The ingester can't select the zip (`installer.InstallerType` is `zip`, which never normalizes to `msi`), so the exe is the only reachable installer. TeamViewer publishes no version-pinned Host exe — `TeamViewer_Host_Setup_x64_15.80.4.exe` and the x86 equivalent both 404 — so the manifest's URL is the unpinned `TeamViewer_Host_Setup_x64.exe` and `ignore_hash: true` is required. This is vendor asymmetry, not a fixable winget defect: the *full* client does publish pinned exe URLs. Same reason `teamviewer/windows` already sets `ignore_hash`. ## Correcting the coexistence note in #50332 The issue assumed Host and the full client can co-exist. They can't. The Host MSI's `LaunchCondition` table blocks the install outright: > Error 25001: An incompatible TeamViewer package was detected that conflicts with the current MSI package: TeamViewer_Full 64-bit. Please manually uninstall this package. …plus equivalents for Full 32-bit/ARM64, Host ARM64, and the NSIS installs. **Relevant to validation: the validator host must not already have `teamviewer/windows` installed.** ## Icons No `index.ts` change needed — `matchLoosePrefixToKey` treats keys as whole words at the start, so `"teamviewer host"` matches the existing `teamviewer` key and inherits the TeamViewer brand icon. Verified against the real 1,163-key map. The website resolves its icon from the slug (`app-icon-${slug}-60x60@2x.png`) with no such fallback, so `app-icon-teamviewer-host-60x60@2x.png` is added — a copy of the existing TeamViewer brand asset, since Host ships the same logo. ## Two things for review 1. **The PowerShell is unverified.** Authored on macOS with no PowerShell available, so install/uninstall have not been executed. The uninstall script searches ARP by DisplayName instead of a hardcoded key, and uses the three-shape `UninstallString` parser (TeamViewer's is unquoted and contains a space in `C:\Program Files\...`, which the older `.Split('"')` approach in `teamviewer_uninstall.ps1` mishandles). Validator run is the real check. 2. **Category mismatch with the macOS side.** This uses `Communication`, matching the merged `teamviewer/windows`. #47121 adds `teamviewer-host/darwin` with `Productivity`. Worth reconciling — and that PR will want this same website PNG, so expect a trivial conflict. `name` is `TeamViewer Host`, matching #47121 so both platforms group together in the FMA library. ## Testing - [x] `go test ./cmd/maintained-apps/... ./ee/maintained-apps/...` passes - [x] Generator is idempotent — re-running produces no diff and preserves the `apps.json` description - [x] `apps.json` is valid JSON with no empty descriptions - [x] Live installer SHA256 confirmed against the winget manifest - [ ] FMA validator: install → detect → uninstall on a Windows host **(pending — needs a host without the full TeamViewer client)** No shared code changed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
bc3eee5f32 |
Re-add Dell Display and Peripheral Manager Windows FMA, validate on client-OS runner (#50313)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** NA (Windows FMA workstream; follow-up to #49127, which dropped DDPM) Re-adds **Dell Display and Peripheral Manager** (`Dell.DisplayAndPeripheralManager` 2.2.2.8) as a Windows Fleet-maintained app, and adds a `requires_client_os` routing override so its CI validation always runs on the `windows-11-arm` runner. ## Why DDPM was dropped before, and why it's viable now DDPM was dropped from the earlier re-add because its InstallShield setup aborted with `0x80042000` under every documented silent switch, which was diagnosed at the time as a .NET-prerequisite/headless-chaining problem. A new debug run with Dell's own `/CreateDebugLog` switch shows the real cause: the setup evaluates the OS at `OFUIBefore` and terminates because the runner reports **Microsoft Windows Server 2025**. DDPM is a Windows 10/11 client application and refuses to install on Server SKUs — which is exactly what GitHub's x64 `windows-latest` image is. ``` OSetUMode() 0 AP:2.2.2.8 OFUIBefore Os Major10 Minor0 OS - 44444 // End Log File... ``` ## `requires_client_os` CI routing - New optional winget input field `requires_client_os: true` (documented in `ee/maintained-apps/README.md` and on the Go input struct; ignored by ingestion). - `.github/scripts/partition-fma-apps.sh` routes any app with this flag to `windows-11-arm` — the only GitHub-hosted client-OS Windows runner — regardless of `installer_arch`. The x64 installer runs there under Prism emulation; DDPM's gate is the OS SKU, not the architecture. - Verified locally: partitioning the full 421-app Windows catalog reroutes only `dell-display-and-peripheral-manager/windows`. ## App identity (verified against the real installer) - Downloaded `DDPM-Setup_2.2.2.8.exe` from `dl.dell.com` (Chrome UA per #49123); SHA256 matches the winget manifest. - Embedded InstallShield `[Application]` block: `Name=Dell Display and Peripheral Manager`, `Company=Dell Technologies`; ProductCode matches the manifest GUID. The setup log reports `AP:2.2.2.8` as the registering version. - Installs with Dell's documented managed-deployment switches `/Silent /HeadlessMode=true /TelemetryConsent=false /TurnOffCA` — the final pre-drop iteration (6d0f2c00af), which also declines telemetry and disables DDPM's self-updater on Fleet-managed hosts. Uninstalls via `msiexec /x` on the ProductCode looked up in the registry by DisplayName. Input/uninstall script/icon are restored from the pre-drop state; the install script is the final pre-drop iteration with its root-cause comment corrected (Server-SKU OS gate, not headless-SYSTEM chaining). Output regenerated (winget still at 2.2.2.8; script refs verified). # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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] QA'd all new/changed functionality manually (partition script exercised locally over the full catalog and a mixed PR-style slug list; ingester regenerated with no output drift; `go test ./ee/maintained-apps/ingesters/winget/` passes) - [ ] `test-fma-windows-pr-only` validates DDPM on the `windows-11-arm` runner in this PR's CI <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added Dell Display and Peripheral Manager to the Windows software catalog, including installation, uninstallation, detection, metadata, and an app icon. * Added support for routing applications that require a Windows client operating system to the appropriate Windows 11 ARM test environment. * **Documentation** * Documented Windows client operating system routing behavior and test environment architecture details. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
1563aeb70c |
Website: improve speed of deliver-talk-to-us-form-submission action (#50593)
Changes: - Updated the model used in the prompt helper calls in the deliver-talk-to-us-form-submission action and the get-enriched helper to improve the speed of routing users booking a demo. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Updated location and address enrichment to use an updated language model, improving the processing of submitted information while preserving existing form behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
85692f8238 |
Website: set historical event source (#50546)
Changes: - Updated the website's createHistoricalEvent helper to accept an eventSource input that is used to set the historical event source on created records. - Updated places where we create historical events to set a historical event source - Updated the accepted contact sources values in the receive-from-clay webhook - Updated the deliver-gitops-workshop-request action to log a warning when a campaign member record cannot be created <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Improvements** - Improved activity tracking for newsletter subscriptions, signups, contact forms, workshop requests, webinars, gated content, and page views. - Added clearer source details to records for more accurate attribution. - Expanded support for website, webinar, event, LinkedIn, prospecting, and GitHub activity sources. - **Bug Fixes** - Workshop requests now continue successfully if campaign updates encounter an error. - Corrected warning messages and preserved relevant submission details for troubleshooting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8a11bd58db |
[Route] Update routes.js (#50472)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #48894 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a redirect from `/learn-more-about/removal-behavior` to the relevant section of the custom OS settings guide. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b668734d5c |
Remove Fig FMA (cask removed from Homebrew upstream) (#50483)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** NA Removes the **Fig** Fleet-maintained app. Its Homebrew cask no longer exists. Homebrew deleted the `fig` cask in [`a36fac3b75`](https://github.com/Homebrew/homebrew-cask/commit/a36fac3b75f633a9c787c4c73fc46606939947a3) on **2026-08-04**, the end of a long deprecation: | Date | Upstream change | |---|---| | 2024-08-03 | `fig: deprecate` | | 2025-08-02 | `fig: disable` | | 2026-08-04 | `fig: remove cask` | `https://formulae.brew.sh/api/cask/fig.json` now returns **404**, so the nightly ingester panics and no maintained apps are generated at all: ``` {"time":"2026-08-04T02:38:58.15272663Z","level":"INFO","msg":"ingesting homebrew app","name":"fig"} panic: ingesting homebrew app: app not found in brew API ``` Note that `"frozen": true` does **not** fix this — that flag only gates the output write, and the ingester still fetches the cask first and panics on the 404. Removing the input is the fix. There is no successor cask to migrate to. Fig was acquired by AWS and folded into Amazon Q Developer CLI, which is not distributed via Homebrew (`amazon-q`, `amazon-q-developer-cli`, `q-cli`, and `codewhisperer` all 404), and `fig.io` itself now returns 503. ### Changes - Deleted `ee/maintained-apps/inputs/homebrew/fig.json` - Deleted `ee/maintained-apps/outputs/fig/darwin.json` - Removed the `fig/darwin` entry from `ee/maintained-apps/outputs/apps.json` - Deleted the `Fig` icon component and its `index.ts` import/map entry - Deleted `website/assets/images/app-icon-fig-60x60@2x.png` The output file is deleted rather than orphaned so the PR validator's changed-app detector doesn't keep validating a removed app. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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 Verified `apps.json` still parses and the `fig/darwin` slug is gone (1390 apps remain, `figma` untouched), `tsc --noEmit` is clean after removing the `Fig` icon import, and no references to `fig`, `com.mschrage.fig`, `fig/darwin`, or `repo.fig.io` remain anywhere in the repo. > [!NOTE] > Existing hosts with Fig installed will no longer see it as a Fleet-maintained app. The app is end-of-life upstream, so there is no version for Fleet to track or patch to. |
||
|
|
19af21dd1a |
Upgrade query-generator SQL step to Claude Sonnet 5 (#49187)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** N/A ## What this does The `/query-generator` page's osquery-SQL-generation step ([get-llm-generated-sql.js](website/api/controllers/query-generator/get-llm-generated-sql.js)) was on `claude-sonnet-4-6`, which is now one generation behind. This PR: - Bumps that call to `claude-sonnet-5`. The schema-filtration step stays on `claude-haiku-4-5`, which is already the latest Haiku release, so no change needed there. - Adds `effort` support to the shared [`ai.prompt` helper](website/api/helpers/ai/prompt.js), forwarded as `output_config.effort` on Anthropic requests, and sets it to `"low"` for the SQL-generation call. Effort controls how much the model deliberates (and how many tokens/how much latency that costs). `"low"` was chosen because the Haiku pre-filtering step already narrows the osquery schema down to relevant tables, so the Sonnet step isn't starting from scratch and doesn't need to spend much effort re-deriving that context. - Bumps `max_tokens` in the Anthropic branch of the helper from 4096 to 8192. Claude Sonnet 5 turns on adaptive thinking by default when the `thinking` param is omitted (which this helper does), and `max_tokens` is a hard cap on *total* output including thinking tokens — at 4096 there was a real risk of thinking tokens eating into the budget and truncating the JSON response the SQL step needs to return. - **Fixes a pre-existing bug found while making the above changes:** the `sqlReport` call passed the system prompt as a bare object-shorthand key named `systemPromptForQueryGeneration`, but the `ai.prompt` helper's declared input is `systemPrompt`. Sails silently drops unrecognized keys passed to `.with(...)`, so the "Return ONLY a raw JSON object..." system prompt was never actually reaching the model for this call. This has been broken since the query generator was switched to Anthropic (`f7c20c4731`); the sibling `filteredTables` call above it was unaffected since it passes `systemPrompt` positionally. Now fixed to `systemPrompt: systemPromptForQueryGeneration`. ## Why Claude Sonnet 5 follows structured/constrained instructions (don't alias tables, use `LIKE` with wildcards, only reference documented columns, etc.) more literally than 4.6, which should make the generated SQL more reliable. It's priced the same or cheaper than 4.6 during the current introductory period. ## Trade-offs called out for review - Thinking being on by default adds some latency versus the old (thinking-off) behavior on 4.6. This call is not currently streamed (`sails.helpers.http.post`, single blocking call over a socket), so any added thinking time is invisible wait time for the user rather than a visible "thinking" indicator. `effort: "low"` should keep this modest, but worth confirming with a manual QA pass on a few representative questions before merging. - Only the SQL-generation call was migrated. The schema-filtration call also runs on an Anthropic model, but Haiku 4.5 doesn't support `output_config.effort` (added `effort` is a no-op if passed to it), so it was left as-is. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops - [ ] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [ ] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [ ] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Improvements** * Improved AI-generated SQL responses with an updated language model. * Added adaptive effort controls for supported AI requests. * Increased response capacity to support more detailed generated results. * Improved handling of AI responses to provide more reliable results when content includes different response formats. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Eric <eashaw@sailsjs.com> |
||
|
|
033cb8843c |
Fix image captions breaking onto multiple lines when they contain links (#50441)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** NA Image captions on articles broke onto multiple lines whenever the caption contained an inline link. Reported on [Linux crossed 10% in North America, and your inventory might have missed it](https://fleetdm.com/articles/linux-crossed-10-percent-and-your-inventory-missed-it), where the source line under the Cloudflare Radar chart rendered as: ``` Source: Cloudflare Radar , captured August 3, 2026. ``` ## Cause Image captions (`img + em`) were styled as a column flex container: ```less display: flex; align-items: center; flex-direction: column; ``` Every child of a flex container becomes its own flex item on its own line — including bare text nodes, which get wrapped in anonymous flex items. That caption has three children (`"Source: "`, the `<a>`, and `", captured August 3, 2026."`), so it stacked into three rows. Every other image caption in `articles/` is a single plain-text run, which is why this hasn't surfaced before. This is the first caption on the site with an inline link. ## Fix ```less img + em { // Image captions position: relative; top: -12px; display: block; text-align: center; margin-bottom: 16px; } ``` `display: block` + `text-align: center` preserves the existing centered appearance for plain-text captions (visually identical) while letting inline content flow normally. ## Notes for the reviewer - **Six files, one rule.** The identical caption rule was copy-pasted into `basic-article.less`, `basic-webinar.less`, `basic-whitepaper.less`, `case-study.less`, `legal/privacy.less`, and `legal/terms.less`. Only `basic-article.less` is needed to fix the reported page; the other five carry the same defect, so all six are updated rather than leaving the trap for the next caption with a link. - **No markdown change.** The article source (`*Source: [Cloudflare Radar](...), captured August 3, 2026.*`) was already correct. This is purely a stylesheet fix. - **Mobile overrides untouched.** The second `img + em` block in each file (inside a media query) only adjusts `top` and `margin-bottom`, so it needed no change. - **Existing captions are unaffected.** All current captions are single text runs; block + centered text renders them the same as column flex + `align-items: center` did. # Checklist for submitter - [x] QA'd all new/changed functionality manually Verified on the live article by injecting the new rule into the rendered page: the caption went from 72px tall (three stacked lines) to 24px (one centered line), rendering as `Source: Cloudflare Radar, captured August 3, 2026.` I was not able to run `npm run lint` locally — `website/node_modules` is not installed in my working copy. The change follows `.lesshintrc` (one space after `:`, no `!important`), but CI's lint run is the authority here. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Improved image caption layout across articles and legal content. * Captions now display consistently as centered block text for better readability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
b2b2081ad4 |
Remove Gadwin PrintScreen, PrintScreen Pro, and ScreenRecorder FMAs (expired TLS cert on download host) (#50470)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** NA Removes the three Gadwin Fleet-maintained apps: **Gadwin PrintScreen**, **Gadwin PrintScreen Pro**, and **Gadwin ScreenRecorder**. All three download their installers from `www.gadwin.com`, whose Let's Encrypt certificate **expired 2026-08-02** and has not been renewed: ``` subject=CN=gadwin.com issuer=C=US, O=Let's Encrypt, CN=R13 notBefore=May 4 13:06:54 2026 GMT notAfter=Aug 2 13:06:53 2026 GMT ``` Every FMA validation run now fails these three apps: ``` level=ERROR msg="Error downloading maintained app: downloading installer: performing request for URL https://www.gadwin.com/download/PrintScreen650_Win64.msi: tls: failed to verify certificate: x509: certificate has expired or is not yet valid" app="Gadwin PrintScreen" ``` This is not just CI: Fleet fetches the installer from that same URL when a user installs the app, so all three are currently uninstallable for customers. > [!NOTE] > The certificate expired only one day before this PR was opened. If Gadwin renews it, these apps become viable again and the alternative fix is `"frozen": true` in each winget input (which skips validation) rather than removal. Removing was chosen because a lapsed auto-renewing certificate means no one is maintaining the download host, and a broken installer URL is worse for users than an absent app. Happy to switch to a freeze if reviewers prefer to wait it out. ### What's removed Per app, all locations that applied: | Location | PrintScreen | PrintScreen Pro | ScreenRecorder | |---|---|---|---| | `ee/maintained-apps/inputs/winget/<slug>.json` | ✅ | ✅ | ✅ | | `ee/maintained-apps/outputs/<slug>/windows.json` | ✅ | ✅ | ✅ | | `ee/maintained-apps/outputs/apps.json` entry | ✅ | ✅ | ✅ | | `frontend/.../icons/<Name>.tsx` | ✅ | ✅ | — (none existed) | | import + mapping in `icons/index.ts` | ✅ | ✅ | — | | `website/assets/images/app-icon-<slug>-60x60@2x.png` | ✅ | ✅ | — (none existed) | All three are Windows-only (no Homebrew input or `darwin.json`), so no macOS counterpart is affected and no shared icons needed to be retained. `apps.json` goes from 1393 to 1390 apps. The diff is deletion-only. # 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] QA'd all new/changed functionality manually Verification performed: - `apps.json` parses as valid JSON; no `gadwin` slugs remain. - Repo-wide grep for `gadwin` returns no dangling references. (The one remaining hit, `cmd/osquery-perf/software-library/software.sql`, is a simulated host-inventory fixture, not an FMA definition — intentionally left in place.) - `npx tsc --noEmit` reports no errors related to the removed icon components or the icon index. - `npx prettier --check` passes on `icons/index.ts` and `apps.json`. - `go build ./cmd/maintained-apps/... ./ee/maintained-apps/...` and `go test ./ee/maintained-apps/...` pass. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Removed Gadwin PrintScreen, Gadwin PrintScreen Pro, and Gadwin ScreenRecorder from the maintained Windows application catalog. * Removed their associated software listings, installation details, and product icons from the application interface. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
2431d580b0 |
Add QEMU as a Windows Fleet-maintained app (#50471)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #50126 Adds **QEMU** (`SoftwareFreedomConservancy.QEMU`) as a Windows Fleet-maintained app, requested by a customer in #50126. Windows-only: on macOS, Homebrew ships QEMU as a formula (CLI tools, no `.app` bundle), so it isn't a viable macOS FMA. ## App identity (verified against the real installer) - Downloaded `qemu-w64-setup-20260501.exe` from `qemu.weilnetz.de`; SHA256 matches the winget manifest. - QEMU's NSIS definition ([`qemu.nsi`](https://gitlab.com/qemu-project/qemu/-/blob/master/qemu.nsi)) writes the uninstall key `HKLM\...\Uninstall\QEMU` (64-bit view via `SetRegView 64`) with `DisplayName "QEMU"` and `DisplayVersion` set to the meson project version — which equals the winget `PackageVersion`, so the patch policy reconciles cleanly. - **No `Publisher` value is written to the registry**, so the default generated exists query (`... AND publisher = 'QEMU Community'`) would never match. The input overrides it with `exists_query: SELECT 1 FROM programs WHERE name = 'QEMU';` — exact name match, so entries like "QEMU guest agent" (virtio-win) are left alone. ## Install/uninstall scripts - NSIS installer, machine scope, x64. Install: standard silent `/S` (same pattern as AnyBurn). - Uninstall: registry lookup by exact DisplayName across both registry views, defensive UninstallString parsing, `/S _?=<installdir>` so the uninstaller runs in place (instead of relaunching from `%TEMP%` and returning immediately), verification that the ARP entry is actually gone, then sweep of the leftover uninstaller/install dir, `HKLM\SOFTWARE\QEMU`, and shortcuts. ## Version caveat winget's newest version dir for this package is **11.0.50 — a QEMU development snapshot** (QEMU uses `x.y.50` for post-release dev builds; only x64, no arm64), added upstream alongside the 11.0.0 stable release. The ingester picks the highest version, so this FMA currently ships the snapshot build; it will move to the next stable (e.g. 11.0.1/11.1.0) as soon as winget has it. The installer URL is date-pinned (`.../2026/qemu-w64-setup-20260501.exe`), so there's no hash-drift risk. If we'd rather not offer dev snapshots, that needs an ingester-level version filter — flagging for maintainer input rather than building it into this PR. ## Icon Official 128×128 QEMU icon from the upstream source tree (`ui/icons/qemu_128x128.png`), generated via `tools/software/icons/generate-icons.sh`. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [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] Verified installer SHA256, registry identity (DisplayName/DisplayVersion/no Publisher), and silent switches against the real installer and upstream `qemu.nsi`/`meson.build` - [ ] `test-fma-windows-pr-only` validates QEMU install/uninstall in this PR's CI <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added QEMU to the Windows software catalog. * Added support for silent QEMU installation and reliable uninstallation. * Added QEMU version detection and upgrade validation. * Added a QEMU icon to the software interface. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f736356f0e |
Website: update contact form button (#50477)
Changes:
- updated the submit button on the "Talk to us" form ("Talk to an
engineer" » "Pick a time")
- Updated the GitHub webhook to add ~ga4-annotation labels to pull
requests that change the contact page
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Updated the contact form button label to “Pick a time” for clearer
scheduling intent.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
|
||
|
|
74c2b79f05 |
Website: add support for new usage statistics (#50466)
Changes: - Updated the website to support two new usage statistics sent by Fleet servers: `numHostsFleetMDMEnrolledWindows` and `numHostsFleetMDMEnrolledMacOS` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Usage analytics now track Fleet MDM-enrolled host counts separately for macOS and Windows. * Historical usage records include these platform-specific enrollment totals. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
89acb97395 |
Add NVDA as a Windows Fleet-maintained app (#50450)
**Related issue:** Resolves #50125 Adds NVDA as a Windows Fleet-maintained app, from winget `NVAccess.NVDA` (2026.1.1, NSIS/nullsoft, x86 launcher). ## Identity — read out of the shipped installer, not the manifest I downloaded the 60 MB installer, extracted the NSIS payload, and read the identity fields from `_buildVersion.pyc` and the PE headers. The winget manifest is misleading in two ways: | Field | winget says | Actually is | Source | |---|---|---|---| | Architecture | `x86` | **x64** app behind a 32-bit NSIS launcher stub | `nvda_noUIAccess.exe` / `nvda_slave.exe` PE headers | | Registry DisplayName | PackageName `NVDA` | **`NVDA 2026.1.1`** | `source/installer.py` `getUninstallerRegInfo()`: `DisplayName=f"{name} {version}"` | | Publisher | `NV Access` | `NV Access` (matches) | `_buildVersion.pyc`: `publisher = "NV Access"` | Two consequences: - Because NVDA itself is a **64-bit** process, it registers under the native registry view, **not** `Wow6432Node` (the launcher's 32-bit-ness is irrelevant). Both scripts check both views anyway, for legacy 32-bit copies. - DisplayName carries the version, so this needs `fuzzy_match_name: true` → `name LIKE 'NVDA %'`. Publisher matches the locale manifest, so no `program_publisher` override. `installer_arch` stays `x86` because that's what the manifest declares and the ingester matches on it. ## Version reconciles without a validator exception DisplayVersion is the 4-part `2026.1.1.55980` (`version_detailed`) against winget's `2026.1.1`: - **Validator:** passes via the existing `strings.HasPrefix(result.Version, appVersion+".")` branch in `cmd/maintained-apps/validate/windows.go`. No new skip added — deliberately, since existence-only skips make patch policies always report "patched". - **Patch policy:** `version_compare('2026.1.1.55980', '2026.1.1')` is `> 0`, so an installed copy reads as newer, not outdated. No perpetual false "update available". ## The install script can't trust the exit code `source/gui/installerGui.py` `doInstall()` pops `winUser.MessageBox` / `gui.messageBox` on **every** install failure path with **no `if silent` guard**, and then falls through and exits **0**. Under SYSTEM in session 0 that means: 1. a failure **hangs forever** — nobody can click Retry/Cancel; and 2. if it were dismissed, a failed install would report **success**. So `nvda_install.ps1` uses a watchdog plus an Add/Remove Programs registration poll as the real success signal — the same shape as the existing `azure_data_studio_install.ps1`. Timeouts are 420 + 120 + 30 = 570s, under the caller's 10-minute cap. On timeout it kills only the launcher's `%TEMP%` children (`nvda_noUIAccess` / `nvda_uiAccess`), **deliberately not `nvda.exe`** — an installed NVDA runs as `nvda.exe`, and force-killing it would cut off a signed-in user's screen reader with no warning. ## Uninstall Vendor-documented `/S` (NVDA user guide, "Uninstalling NVDA"), plus `_?=` last so the NSIS uninstaller runs in place instead of relaunching from `%TEMP%` and returning immediately. NVDA writes **no** `QuietUninstallString`, and its `UninstallString` is an **unquoted path containing spaces** (`C:\Program Files\NVDA\uninstall.exe`), so the parser handles that form. The directory comes from NVDA's `InstallDir` value (not `InstallLocation`). Absence of the ARP entry is the success signal, since NVDA removes it via `nvda_slave.exe unregisterInstall`. ## Reviewer notes - **`installer_scope` is `""`, not `"machine"`.** NVDA genuinely installs machine-wide (`%ProgramFiles%\NVDA` + HKLM), but the winget manifest declares no `Scope`, so the ingester derives `""` and `"machine"` panics with "failed to find installer". The one-line ingester fix for this is designed in #48248 but isn't in `main`; I chose not to change shared installer-selection code for a single-app addition. Happy to land that fix here instead if preferred. - **Upgrade caveat:** if NVDA is running for a signed-in user, `--install-silent` refuses to overwrite its own running files by design (`installer.py` `install()`). The script fails with an actionable message rather than force-killing the screen reader. - Installer URL is version-pinned (`download.nvaccess.org/releases/2026.1.1/...`), not a "latest" redirect. SHA verified against my own download of the file. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [ ] QA'd all new/changed functionality manually `go test ./ee/maintained-apps/...` passes; prettier and `tsc --noEmit` are clean. I have no Windows host or `pwsh`, so **the install/uninstall scripts are unexercised** until FMA validation CI runs them on a Windows runner. No changes file — consistent with other FMA additions (#50415, #50348, #50352). |
||
|
|
05ec868641 |
Website: Update logos on contact page (#50368)
Changes: - Replaced the logo carousel on the contact page with a grid of six logos. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * Updated the contact page to display partner logos in responsive two- and three-column grids. * Improved logo sizing, alignment, spacing, and borders across desktop and mobile screen sizes. * Replaced the previous carousel presentation with a static, more consistent layout. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
4036f9bfcb |
Add Microsoft ODBC Driver 18 for SQL Server as a Windows Fleet-maintained app (#50348)
**Related issue:** Resolves #50327 Adds Microsoft ODBC Driver 18 for SQL Server as a Windows Fleet-maintained app, from winget `Microsoft.msodbcsql.18` (18.6.2.1, MSI, machine scope, x64, en-US). Sibling of #50342 (driver 17); the two install side by side and are separate products. ## Verification Identity read from the MSI Property table: ``` ProductName Microsoft ODBC Driver 18 for SQL Server Manufacturer Microsoft Corporation ProductCode {820A3DEC-9783-42AE-B12D-750FCCF07E10} UpgradeCode {ADA68B65-BFF8-4E6A-B082-CC6682D425B8} ALLUSERS 1 ``` - Installer SHA confirmed against a local download (`20314529…4b82`). - The UpgradeCode differs from driver 17's (`{0123A210-…}`), which confirms they are independent products rather than upgrades of one another. The generated uninstall script picked up 18's, so uninstalling one will not touch the other. - Because `ProductName` already carries the major version, the exists query is a simple equality — no `version LIKE '18.%'` pinning needed, and no risk of 17 and 18 matching each other. Like driver 17, the MSI refuses to install without `IACCEPTMSODBCSQLLICENSETERMS=YES`, which Fleet's default MSI script does not pass, so this ships a small custom install script. It is deliberately a **separate file** from driver 17's rather than a shared path, following the repo's one-script-per-app convention and keeping the two PRs independently mergeable. The icon comes from the MSI's own `ARPPRODUCTICON` stream. As with #50342 it is natively 32×32, so the 128×128 asset is an upscale — happy to drop it in favour of the generic fallback if reviewers prefer. The icon map key also needed the same manual correction (the generator derives it from the slug, which omits "for sql server"). The manifest declares a `Microsoft.VCRedist.2015+.x64` dependency, which the ingester ignores; we ship `vc-redist-x64/windows` and the redistributable is present on most hosts. # Checklist for submitter - [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 Microsoft ODBC Driver 18 for SQL Server to the maintained Windows software catalog. * Added support for version 18.6.2.1, including installation, upgrade, uninstall, detection, and license acceptance handling. * Added the software’s icon and catalog display details. * Included support for SQL Server and Azure SQL connectivity scenarios. * Installation now provides appropriate handling for successful completion, restart requirements, and installation failures. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
a4d058384c |
Add CrystalDiskMark as a Windows FMA (#50415)
**Related issue:** Resolves #50322 Adds CrystalDiskMark as a Windows Fleet-maintained app, from winget `CrystalDewWorld.CrystalDiskMark` (9.0.3, Inno Setup, machine scope, x64). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent. ## Identity — read out of the Inno header, not the manifest The winget manifest carries no `AppsAndFeaturesEntries`, so the ARP identity had to come from the installer itself. I LZMA-decompressed the Inno setup-data block and read the header directly: | Header field | Value | |---|---| | `AppName` | `CrystalDiskMark` | | `AppVerName` | `CrystalDiskMark 9.0.3` | | `AppVersion` | `9.0.3` | | `AppPublisher` | `Crystal Dew World` | | `AppId` | `CrystalDiskMark9` | | `UninstallDisplayName` | *(empty)* | | `DefaultDirName` | `{pf}\CrystalDiskMark9` | With `UninstallDisplayName` empty, Inno falls back to `AppVerName` for the ARP `DisplayName` — so this registers as **`CrystalDiskMark 9.0.3`**, version-suffixed, and the exists query is a prefix match (`fuzzy_match_name: true`). That the name is version-suffixed isn't inferred from "Inno usually does this." The Aoi edition's header sets `AppVerName` to `CrystalDiskMark 9.0.3 Aoi Edition` while its `AppName` is `CrystalDiskMark Aoi Edition` — the compiler's synthesized default would have been `CrystalDiskMark Aoi Edition 9.0.3`, so the script is setting `AppVerName` deliberately, and Setup will write exactly that string. `AppPublisher` matches the winget locale `Publisher` verbatim, so no `program_publisher` override is needed, and `DisplayVersion` is `AppVersion` = `9.0.3`, which reconciles with the FMA version with no validator exception. Generated query: ```sql SELECT 1 FROM programs WHERE name LIKE 'CrystalDiskMark %' AND publisher = 'Crystal Dew World'; ``` ## Silent flags — the `[Run]` entries were decoded, not assumed The header contains three `[Run]` entries with `{cm:LaunchProgram,CrystalDiskMark9}` descriptions (one per architecture), which is exactly the shape that has hung silent Inno installs for us before. I parsed the entry structure through to the flag bytes: - wait enum = `1` → `nowait` - flags = `0x14` → bit 2 `postinstall` + bit 4 `skipifsilent` So `/VERYSILENT /SUPPRESSMSGBOXES /NORESTART` will not launch the GUI, and there is no post-install hang to work around. The install script still uses the poll-and-kill wrapper rather than `-Wait`, plus an ARP-registration wait, since the installer can return before the registry entry lands. `/ALLUSERS` is deliberately omitted: `DefaultDirName` is `{pf}`, which requires admin install mode already, so the switch would be a no-op. ## Uninstall `UninstallString` is Inno's quoted `unins000.exe` path, but the script uses the three-branch defensive parser (quoted / unquoted-with-spaces / bare token) anyway. Two app-specific bits: - The 12 `DiskMark*.exe` binaries are stopped first — the uninstaller won't proceed while the `CrystalDiskMark9` mutex is held. - The script waits for the registry entry to disappear rather than trusting the exit code. Inno's uninstaller relaunches itself from a temp copy and the original process returns early, so its exit code is not a completion signal. ## Icon The setup exe's `MAINICON` is Inno's stock CD-and-box artwork, not the app's, so the icon was extracted from `DiskMark64.exe`'s own resources (`innoextract` → `wrestool -t 14 -n 130` → `icotool`, 256×256 32-bit). ## Two things for reviewers 1. **This carries a shared-code change to the winget ingester** (`normalizeSourceForgeURL`). A bare SourceForge project file URL serves non-browser clients a 133KB HTML landing page with a 200, so the first validator run downloaded that instead of the installer and failed on the hash. Only the `.../download` form serves the binary. WinSCP works today only because its manifest happens to carry the suffix, so the fix normalizes in the ingester rather than depending on manifest authors. WinSCP regenerates byte-identical and is the only other SourceForge-hosted app in the catalog; `TestNormalizeSourceForgeURL` covers both shapes. Details in [this comment](https://github.com/fleetdm/fleet/pull/50415#issuecomment-5162501140). 2. **The Aoi and Shizuku editions share this package's `AppId` and install directory.** They occupy the same ARP slot (`CrystalDiskMark9_is1`), can't be co-installed, and will match the exists query as `CrystalDiskMark 9.0.3 <Edition>`. I treated that as correct rather than something to exclude — it is the same product at the same version — but flagging it in case we'd rather pin to the plain edition. ## Verification - Inno header dumped offline; `AppVerName` / `AppPublisher` / `AppVersion` read directly, `[Run]` flag bytes decoded. - Generated SHA matches the winget installer manifest (`1a255154…917e5e`), and the download was verified through the exact code path the validator uses (`maintained_apps.DownloadInstaller` with `http.DefaultClient`) — 4,523,144 bytes, hash matches. - `apps.json` valid, description filled, icon generated and inserted alphabetically in `index.ts` under the key `crystaldiskmark`. - `go test ./ee/maintained-apps/...` passes; `GOOS=windows go build ./cmd/maintained-apps/validate/` builds; `gofmt`/`go vet` clean. `make lint-go-incremental` could not run in my environment (`custom-gcl` fails to clone golangci-lint, pre-existing and unrelated). - First validator run failed on the download issue above and is fixed; the full validator pass (install → detect → uninstall on a Windows host) still needs to go green before this leaves draft. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [ ] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added CrystalDiskMark as a supported Windows application, including installation, uninstallation, detection, categorization, and software catalog details. * Added CrystalDiskMark branding to the software interface. * Improved SourceForge installer URL handling by automatically appending download paths when needed. * **Bug Fixes** * Preserved existing download URLs, query parameters, and unsupported or malformed URLs during normalization. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
7abc0f6e7f |
Add article: Linux crossed 10% in North America, and your inventory might have missed it (#50412)
**Related issue:** NA New thought-leadership article pegged to Statcounter's July 2026 data showing Linux desktop share at 10.65% in North America. The angle is deliberately honest rather than triumphant: most of the month-over-month jump came from Statcounter reclassifying an "Unknown" traffic bucket, not from a mass migration. That measurement problem is the bridge into Fleet's argument, since the same blind spot that hid Linux from web analytics hides Linux hosts from asset inventories that discover devices through Apple and Windows enrollment channels. Includes a Cloudflare Radar chart as a second, independent data source. Cloudflare puts North American Linux desktop requests at 6.3% over the trailing year, well below Statcounter's figure, but both show a step change in the same weeks. The disagreement between the two sources reinforces the article's point that Linux is hard to count from the outside. Intended for use in ads and other traffic-driving placements. Notes for reviewers: - Capability claims (LUKS2 enforcement on Ubuntu/Kubuntu/Fedora with escrowed recovery keys, `.deb`/`.rpm`/`.tar.gz` and script-only packages, vulnerability detection including CISA KEV, remote script execution, remote lock and wipe, self-service, supported distro list) are grounded against `website/views/pages/linux-management.ejs`. Please confirm they are still accurate. - The article states that encryption enforcement covers a narrower set of distributions than the overall support list, which is intentional. - Statcounter figures are cited via Linuxiac; a Sources section attributes both Statcounter and Cloudflare Radar, and the chart carries inline attribution and a capture date. - `publishedOn` is set to `2026-08-02`. Bump if this merges later. - Byline is set to Allen Houchins. # Checklist for submitter - [ ] Content reviewed for Fleet voice and style (`content-style`) and article format (`fleet-article-formatting`) - [ ] Capability claims verified against the Linux management page and docs - [ ] Source attribution confirmed for Statcounter, Linuxiac, and Cloudflare Radar - [ ] Article renders correctly on the website, including the chart image |
||
|
|
86f4619faf |
Add Microsoft ODBC Driver 17 for SQL Server as a Windows FMA (#50342)
**Related issue:** Resolves #50326 Adds Microsoft ODBC Driver 17 for SQL Server as a Windows Fleet-maintained app, from winget `Microsoft.msodbcsql.17` (17.11.1.1, MSI, machine scope, x64, en-US). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent. ## The install would fail without a custom script The MSI refuses to install unless `IACCEPTMSODBCSQLLICENSETERMS=YES` is passed. This is not just a winget convention — the MSI carries the condition and the matching error string: ``` IACCEPTMSODBCSQLLICENSETERMS ~= IACCEPTMSODBCSQLLICENSETERMS=YES command-line parameter is missing. ``` Fleet's default MSI install script passes only `/quiet /norestart /i`, so without the small custom script in this PR every install would fail. Uninstall is still the auto-generated upgrade-code script, which is correct as-is. ## Verification Identity read from the MSI Property table: ``` ProductName Microsoft ODBC Driver 17 for SQL Server Manufacturer Microsoft Corporation ProductCode {ACBA503E-64A9-4A8A-A6CC-63656F395618} UpgradeCode {0123A210-9B73-46E7-B5CE-7F33630300E7} ALLUSERS 1 ``` - Installer SHA confirmed against a local download (`0f642870…bd4e`). - `ALLUSERS=1` means it always installs per-machine. - The generated uninstall script correctly picked up the **x64** UpgradeCode — note the x86 build uses a different one, so an arch mix-up here would silently fail to uninstall. **No version pinning is needed in the exists query.** When I scoped this I expected to need `AND version LIKE '17.%'` the way the Amazon Corretto FMAs do, but the `ProductName` already carries the major version, so `Microsoft ODBC Driver 17 for SQL Server` and `...18...` are naturally distinct entries. Simple equality is correct and unambiguous. ## Two notes for reviewers **Icon quality.** The MSI's `ARPPRODUCTICON` stream tops out at 32×32, so the 128×128 asset here is an upscale and looks soft. It is the authentic Add/Remove Programs icon, but if we would rather fall back to the generic software icon than ship a blurry one, drop the icon files and I will regenerate without them. **Icon map key needed a manual fix.** The generator derives its key from the slug and produced `"microsoft odbc driver 17"`, but lookups use the lowercased catalog name — `"microsoft odbc driver 17 for sql server"`. Corrected by hand. Same generator gap hit Paint.NET in #50340. **Dependency.** The manifest declares `Microsoft.VCRedist.2015+.x64`. The ingester ignores winget `Dependencies`, but unlike HandBrake (#50323) we do ship a matching FMA (`vc-redist-x64/windows`), and the redistributable is present on most Windows hosts already. Noting it rather than treating it as a blocker. # Checklist for submitter - [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 Microsoft ODBC Driver 17 for SQL Server to the maintained software catalog. * Added Windows installation and upgrade support for version 17.11.1.1. * Added the software’s icon and catalog display details. * Included silent installation, logging, license acceptance, and reboot handling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
5fa8a0a9ea |
Add Microsoft .NET Desktop Runtime 10 as a Windows FMA (#50361)
**Related issue:** Resolves #50360 Adds Microsoft .NET Desktop Runtime 10 as a Windows Fleet-maintained app, from winget `Microsoft.DotNet.DesktopRuntime.10` (10.0.10, WiX burn bundle, x64). This closes the gap opened by HandBrake (#50323 / #50352). HandBrake requires the .NET **Desktop** Runtime and its install script now hard-fails without it, but Fleet had no FMA that could satisfy that — we ship `microsoft-dotnet-runtime-8`/`-10`, which are the *base* runtime, a different package with its own Add/Remove Programs entry. The customer's ManageEngine catalog also deploys the Desktop Runtime directly. ## Verification The winget manifest supplies `AppsAndFeaturesEntries`, and I confirmed each value against the real installer by extracting the burn bundle's registration data: ``` Microsoft Windows Desktop Runtime 10.0.10 (x64) Publisher="Microsoft Corporation" Version="10.0.10.50000" ``` - Installer SHA confirmed against a local download (`e82fc901…84d1`). - The `DisplayName` carries both version and architecture, so the exists query uses the same `LIKE 'Microsoft Windows Desktop Runtime 10.%' AND name LIKE '%(x64)'` shape as the existing base-runtime FMAs. - The bundle exposes several ProductCodes (the bundle plus its MSI components), which is exactly the shape the existing uninstall script already documents and handles. **`use_display_version_for_patch` is required here.** The registry `DisplayVersion` is `10.0.10.50000` but the winget package version is `10.0.10`. Without the flag the patch policy would compare against the marketing version and mis-order against what osquery reports. The generated patched query correctly compares against `10.0.10.50000`. ## Reuse rather than duplication - **Scripts:** this reuses `microsoft_dotnet_runtime_install.ps1` / `_uninstall.ps1` unchanged. The Desktop Runtime is the same burn bundle shape, and the uninstaller already resolves the bundle from the injected `$PACKAGE_ID` with a Package Cache fallback. Those scripts are already shared by the two base-runtime FMAs, so this follows the existing pattern rather than adding a near-identical copy. - **Icon:** reuses the existing `MicrosoftDotnetRuntime` component and its `.NET` artwork. The burn bundle only carries a 32×32 icon, so extracting one would have meant shipping a blurry upscale of the same logo. The new map key is `"microsoft .net desktop runtime"` (no version). Icon lookup is a loose *prefix* match — `s === key || s.startsWith(key + " ")` — so one key covers 10 and any future major, mirroring how `"microsoft .net runtime"` already serves both base-runtime FMAs. It is longer than that key, and lookup sorts longest-first, so the desktop runtime cannot be mis-matched to the base runtime icon. # Checklist for submitter - [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 Microsoft .NET Desktop Runtime 10 to the Windows software catalog. * Added support for installing and uninstalling the x64 desktop runtime, including version detection and reboot handling. * Added a dedicated Microsoft .NET Runtime icon for the software listing. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
194df72c90 |
Add Scribe as a Windows Fleet-maintained app (#50341)
**Related issue:** Resolves #50331 Adds Scribe as a Windows Fleet-maintained app, from winget `ColonyLabs.ScribeDesktopCapture` (6.7.23.0, MSI, machine scope, x64). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent. The simplest of this batch: a plain machine-scope MSI, so the install and upgrade-code uninstall scripts are auto-generated and no custom scripts are needed. ## Verification Identity read directly from the MSI Property table rather than inferred from winget: ``` ProductName Scribe Manufacturer Colony Labs, Inc ProductCode {87a51b1f-554d-414d-92a4-002a0916c91c} UpgradeCode {351EF756-3AF5-4117-8697-53AB61427040} ALLUSERS 2 ``` `Manufacturer` matches the winget locale `Publisher` exactly, so no `program_publisher` override is needed. `ALLUSERS=2` confirms per-machine install when elevated, which is how Fleet runs it. No `ARPSYSTEMCOMPONENT`, so this is a real product entry and not a bootstrapper. - Installer SHA confirmed against a local download of `Scribe_6.7.23.msi` (`41004c21…9b74`). - Icon extracted from the MSI's own `Scribe for Windows.exe` resource, not sourced from the web. ## Note on the name `Scribe` is a generic `DisplayName`, so the exists query pins `publisher = 'Colony Labs, Inc'` to avoid matching an unrelated product of the same name. This is **not** related to `timescribe/darwin`, which is already in the catalog — different vendor, different product. It deliberately does not share that catalog name or icon. # Checklist for submitter - [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 Scribe to the maintained applications catalog for Windows. * Added support for installing and uninstalling Scribe MSI packages, including version detection and installer metadata. * Added reliable installation handling with logging, elevated execution, restart control, and support for standard successful installer exit codes. * Added the Scribe app icon to the software interface. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
da1947cd1f |
Add Paint.NET as a Windows Fleet-maintained app (#50340)
**Related issue:** Resolves #50330 Adds Paint.NET as a Windows Fleet-maintained app, from winget `dotPDN.PaintDotNet` (5.1.12, machine scope, x64). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent. ## Identity: winget's metadata is wrong here The winget locale manifest gives `PackageName: paint.net` (lowercase). The actual registry `DisplayName` is **`Paint.NET`**, read straight out of the MSI Property table: ``` ProductName Paint.NET Manufacturer dotPDN LLC UpgradeCode {04A40F40-A207-4B48-AED7-6AA532E43275} ALLUSERS 2 ``` There is no `ARPDISPLAYNAME` override and no `ARPSYSTEMCOMPONENT`, so `ProductName` is what lands in Add/Remove Programs. Taking the winget name at face value would have produced an exists query that silently never matches. `ALLUSERS=2` confirms it installs per-machine when run elevated, which is how Fleet runs it. ## This is a zip-wrapped installer Paint.NET publishes **only** `.zip` assets — there is no bare `.exe` or `.msi` on the vendor's GitHub releases. So this uses `installer_type: zip` with custom scripts, following the existing precedent of `agent-ransack`, `adobe-acrobat-pro`, `vnc-server`, and `vnc-viewer`. The install script extracts the archive and runs the nested installer with `/auto`, the vendor's silent switch per the manifest's `InstallerSwitches`. **Uninstall resolves the product from the UpgradeCode, not the ProductCode.** Paint.NET's ProductCode changes with every release, and the `.exe` and `.msi` variants register *different* ProductCodes. The UpgradeCode is stable — I verified it is identical across 5.1.10 and 5.1.12 — so `RelatedProducts` on it removes whichever variant is present. ## One thing reviewers may want to change The manifest offers six installers; three are x64/machine/zip and differ only by `NestedInstallerType` (`exe`, `wix`, `portable`). The ingester's selection loop takes the **first** match and breaks, so it picks the `.install.x64.exe` bootstrapper. The `.winmsi.x64.zip` variant is arguably the better FMA target — a plain MSI with predictable ARP behaviour — but there is no way to express "prefer this nested type" in the input today. Selecting it would need an ingester change, so I did not do it here. Worth a follow-up if we hit trouble with the bootstrapper. ## Verification - Zip SHA confirmed against a local download (`3cd861b5…c867`); archive contains exactly one file, `paint.net.5.1.12.install.x64.exe`. - Icon extracted from that installer's own 256px resource, not sourced from the web. - Icon map key is `"paint.net"`, the lowercased catalog name. The icon generator derives its key from the slug and produced `"paint dot net"`, which would never have matched at runtime — corrected by hand. # Checklist for submitter - [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 Paint.NET to the Windows software catalog. * Added support for installing, upgrading, detecting, and uninstalling Paint.NET. * Added Paint.NET branding and an icon to the software interface. * Included Paint.NET version 5.1.12 with verified download metadata and Productivity categorization. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
2833401d12 |
Add Dante Controller as a macOS FMA (#50378)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #50377 # What this does Adds **Dante Controller** as a macOS Fleet-maintained app, from Homebrew cask [`dante-controller`](https://formulae.brew.sh/cask/dante-controller) (4.18.1.1). The cask ships a DMG containing `DanteController.pkg`, so `installer_format: "dmg"` with the standard mount-and-`installer -pkg` install script — the same shape as AdGuard, Adobe Acrobat Pro, and other existing DMG+pkg FMAs. Uninstall is fully generated from the cask's directives (2 `launchctl` services, 8 `pkgutil` receipt IDs, 3 zap trash paths) — no custom scripts. Windows is out of scope: no `Audinate.DanteController` winget package exists. See #50377 for the full feasibility analysis, including why Dante Virtual Soundcard was excluded on both platforms. ## Notes - **Identity verified against the real installer**, not cask metadata. Extracted `DanteController.pkg` from the DMG and read the app bundle's `Info.plist`: `CFBundleIdentifier` = `com.audinate.dante.DanteController`, confirming the input's `unique_identifier`. This needed checking because the cask's preferences path uses a *different* domain (`com.audinate.dante.controller`). - `CFBundleShortVersionString` and `CFBundleVersion` are both `4.18.1.1`, matching the cask version, so the exists/patched queries reconcile with osquery's `apps` table. - Installer SHA confirmed against a local download of the DMG (`4515cd12…38ff`) — matches both the cask and the generated output. - **Arch split.** The cask ships separate arm64/x64 DMGs; the brew API's top-level URL (which the ingester pins) is the arm64 build. This matches the behavior of the ~173 existing darwin FMAs with arch-specific URLs. - **`auto_updates true`.** Dante Updater self-patches, so hosts may drift ahead of the FMA-pinned version. - Ships a new catalog icon, extracted from the app bundle's own `.icns` — not sourced from the web. # 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] FMA CI validator (install → detect → uninstall) **passes** on the macOS runner — [run 30682752130](https://github.com/fleetdm/fleet/actions/runs/30682752130/job/91322957939) (`Found app: 'Dante Controller' at /Applications/Dante Controller.app, Version: 4.18.1.1` → `All 1 apps were successfully validated.`) - [x] Generated output verified locally: installer SHA matches the cask, exists/patched queries checked against the app bundle's `Info.plist`, `apps.json` is valid JSON with a description filled in. - [x] QA'd all new/changed functionality manually --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
f082237518 |
Add Lenovo System Update as a Windows FMA (#50339)
**Related issue:** Resolves #50324 Adds Lenovo System Update as a Windows Fleet-maintained app, from winget `Lenovo.SystemUpdate` (5.08.03.59, Inno Setup, machine scope, x86-only). Found in a customer's ManageEngine ServiceDesk Plus Windows deployment catalog with no Fleet equivalent. Distinct from `lenovo-dock-manager/windows`, which we already ship. ## Verification - Installer SHA confirmed against a local download of `system_update_5.08.03.59.exe` (`e66794dc…53e0d`), served from `download.lenovo.com` — a pinned vendor URL, so none of the SourceForge mirror trouble from #50322 applies. - Registry `DisplayName` determined offline as a bare `Lenovo System Update`: `innoextract --info` reports `AppVerName` when set and falls back to `AppName`, and Inno writes that same value to `DisplayName`. This installer reports no version suffix, unlike CrystalDiskMark in #50322 which reports `"CrystalDiskMark 9.0.3"`. That is why the exists query here is an exact match rather than a prefix. - Icon extracted from the installer's own `Tvsukernel.exe` resource, not sourced from the web. - The uninstall script targets the Inno registry key directly via the manifest's `ProductCode` (`TVSU_is1` — a key name, not a GUID) using the `$PACKAGE_ID` substitution, rather than string-matching `DisplayName`. ## Two things reviewers should weigh in on **1. The exists query deliberately omits the publisher.** House style usually pins `publisher = '...'`, but the registry `Publisher` is not determinable offline for Inno, and the validator's log prints only the name and version — so I could not confirm it. A wrong publisher makes the exists query silently never match while the validator still passes, which is the exact failure mode called out in the FMA docs. `name = 'Lenovo System Update'` is unambiguous on its own. Happy to add the publisher clause if someone can confirm the registry value on a real Lenovo host. **2. This may not be validatable on the CI runner.** Lenovo System Update is a vendor tool for Lenovo hardware, and the runner is a generic Azure VM. If the installer refuses to run on non-Lenovo hardware this will fail the way Dell Display and Peripheral Manager did in #50020 (which was dropped for exactly this reason, and is being retried on a client-OS runner in #50313). Leaving this in draft until the validator reports. # Checklist for submitter - [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 Lenovo System Update to the maintained Windows software catalog. * Added support for silent installation and uninstallation, including status verification and reboot-success handling. * Added Lenovo System Update metadata, download information, categorization, and application icon. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
bf26771ff1 | Delete articles/seamless-mdm-migration.md (#49029) | ||
|
|
ba7168f54b |
Website: update contact source (#50356)
Changes: - Updated the contact source used for contacts created by the deliver-webinar-access-request action (Webinar » Website - Gated video) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Webinar access requests are now categorized as “Website - Gated video” in Salesforce. - Updated contact and account records use the new source label consistently. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
46aed2da6b |
Website: Update software-management.ejs so that we mention the 1000+ apps in the catalog (#50345)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated the software management page to link to Fleet’s software catalog. * Clarified that the catalog includes 1,000+ apps. * Noted automatic updates across macOS, Windows, and additional platforms. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
45fa8be602 |
Website: add press logos to /ai (#50281)
Adds press logos below "The road to AI in IT" report card. <!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** NA # Checklist for submitter - [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 press coverage section to the AI in IT page. * Displays logos for Apple World Today, Cybersecurity Insiders, and IT Brief, linking to related articles. * Added responsive layouts for desktop, tablet, and mobile screens. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7837b8ec8f |
Add AOMEI Backupper Standard as a Windows Fleet-maintained app (#50021)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** #50020 # What this does Adds **AOMEI Backupper Standard** as a Windows Fleet-maintained app. One of the 11 apps split out of #48501 that failed the FMA validator; #50016 shipped the 6 that passed. ## Why it was failing Install and detection were already fine on the SYSTEM-context Windows runner — the validator installed it and osquery found `AOMEI Backupper` 8.4.0. **Uninstall** was the failure: ``` ERROR msg="Error uninstalling app: exit status 1" app="AOMEI Backupper Standard" ERROR msg="Output: Uninstaller for 'AOMEI Backupper Standard' not found." ``` AOMEI unified the ARP `DisplayName` across editions around v7.4 — the registry entry reads `AOMEI Backupper`, with no `Standard` suffix. The uninstall script searched for the catalog name and matched nothing. `unique_identifier` was already corrected to `AOMEI Backupper`; this fixes the uninstall script to match. ## Notes - **Edition matching.** `AOMEI Backupper` also matches the paid Pro/Workstation/Server editions — AOMEI shares the DisplayName across editions and no registry value distinguishes them. Detecting the free edition specifically isn't possible from inventory. - **Non-pinned installer URL.** `https://www2.aomeisoftware.com/download/adb/AOMEIBackupperStd.exe` is a "latest" URL, so the pinned SHA will drift when AOMEI ships a new build until the FMA auto-update bumps it. - x86-only installer, so it lands in `C:\Program Files (x86)`. The validator's "no changes detected in `C:\Program Files`" line is an expected warning, not a failure. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] FMA CI validator (install → detect → uninstall) **passes** on the SYSTEM-context Windows runner — [run 30383902487](https://github.com/fleetdm/fleet/actions/runs/30383902487) (`All checks passed`) - [x] Generated output verified locally: manifest SHA matches the winget manifest, exists/patched queries reviewed for name + publisher correctness, `apps.json` is valid JSON with a description filled in. - [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 **AOMEI Backupper Standard** (version **8.4.0.0.0**) to the Windows software catalog, including verified installer download (SHA-256). - Introduced dedicated silent **install** and **uninstall** support, with version gating and idempotent uninstall behavior when the app isn’t present. - Added a **new software icon** and updated the listing so the correct icon now appears for this product. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Allen Houchins <allenhouchins@mac.com> |
||
|
|
43bff98998 |
Add Gpg4win as a Windows FMA (#50026)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** #50020 # What this does Adds **Gpg4win** as a Windows Fleet-maintained app. One of the 11 apps split out of #48501 that failed the FMA validator; #50016 shipped the 6 that passed. ## Why it was failing Same root cause as GNU Privacy Guard (#50025) — Gpg4win bundles GnuPG. The install worked; the *script* never returned: ``` 20:30:53 INFO msg="Executing install script..." app=Gpg4win 20:40:53 ERROR msg="Error executing install script: exit status 1" # exactly 10:00 later 20:40:53 INFO msg="New application detected at: C:\Program Files\Gpg4win" ``` Ten minutes on the nose is the validator's `executeScript` timeout. **`Start-Process -Wait` waits for the process *and all of its descendants***, and Gpg4win leaves `gpg-agent`, `dirmngr`, `keyboxd` and `scdaemon` resident (plus Kleopatra), so `-Wait` never returns. The same run left `gpg4win-5.0.2.exe` locked in the validator's temp dir, confirming a live child process. The install script now follows the pattern already established by [`ollama_install.ps1`](ee/maintained-apps/inputs/winget/scripts/ollama_install.ps1): start with `-PassThru` (no `-Wait`), wait on the installer process alone with a 7-minute cap (below the caller's 10-minute script budget), poll for the Add/Remove Programs entry, then stop the leftovers. The uninstall script stops those processes up front (they hold file locks that make the uninstall fail), uses NSIS's `_?=<dir>` switch so the uninstaller runs in place rather than relaunching itself detached from `%TEMP%`, and polls the ARP key to confirm removal. ## Notes - **Versioned ARP name.** The registry `DisplayName` is `Gpg4win (5.0.2)`, so the input uses `fuzzy_match_name` and the exists query is `name LIKE 'Gpg4win %'`. The uninstall script matches the same prefix. - x86-only installer. Publisher `The Gpg4win Project`. - Ships a new catalog icon and website asset. # Checklist for submitter - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops ## Testing - [x] FMA CI validator (install → detect → uninstall) **passes** on the SYSTEM-context Windows runner — [run 30384125610](https://github.com/fleetdm/fleet/actions/runs/30384125610) (`All checks passed`) - [x] Generated output verified locally: manifest SHA matches the winget manifest, exists/patched queries reviewed for name + publisher correctness, `apps.json` is valid JSON with a description filled in. - [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 Gpg4win as a supported Windows application. - Added Gpg4win version 5.0.2 with Security categorization. - Added a Gpg4win icon to the software interface. - Introduced silent install and uninstall support with process cleanup, timeouts, and registry-based verification to confirm install/removal outcomes. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8af6eb8ad9 |
fix: correct tooling stat to match survey data (#50284)
**Related issue:** Resolves # # Checklist for submitter - [x] QA'd all new/changed functionality manually Updated stat to lead with 87% and fixed wording to match actual data from the report. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Content Updates** * Updated the AI in IT report statistic to state that 87% of organizations manage devices with multiple tools. * Clarified that 13% use a single platform. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ccfcc65fc5 |
Website: add /capex-savings page (#50272)
Changes: - Added a new landing page: /capex-savings <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a CapEx savings page available at `/capex-savings`. * Added guidance on device refresh cycles, hardware performance, and recommended replacement timing. * Added a savings calculator for device cost, refresh cycle, and fleet size, with estimated annual savings and assumptions. * Added responsive layouts, imagery, calls to action, and mobile-friendly calculator controls. * Added supporting content on refresh recommendations, Fleet hardware performance, and device lifecycle planning. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
db323dcc0a |
Website: Swap quote on contact page (#50259)
Changes: - updated the quote shown on the /contact page <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Content Updates** * Updated the contact page testimonial with refreshed customer feedback and revised attribution. * Updated the testimonial’s company logo, as well as the author portrait, name, and job title. * **Style** * Adjusted the testimonial profile image styling to render as a fully circular image. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
58642183c6 |
Website: update contact source for workshop requests (#50072)
Changes: - Updated the contact source used for contacts created from workshop requests <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Workshop requests are now correctly identified as “Website - Workshop request” in CRM records, improving source tracking and reporting. * Workshop request contacts are treated as form-based, ensuring the most recent campaign member status is set to **Registered**. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c69bb929d1 |
Website: update fleet-leave-behind-deck.pdf (#50206)
Changes: - Updated fleet-leave-behind-deck.pdf to fix an issue where the file was not loading on Chrome. |
||
|
|
7f07651f97 |
Add Hawx case study (#50152)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** N/A # Checklist for submitter This PR adds one markdown file under `articles/` (a customer case study) — no product code, so most of the template below doesn't apply. ## What changed Adds `articles/hawx.md`, a case study on Hawx Pest Control. **The story:** Hawx is a technology-first pest control company whose field technicians can't be dispatched without a provisioned phone. Hiring ramps up hard every summer, so onboarding and offboarding run constantly. With Jamf, phones sat on the MDM screen until the technician logged in, nobody remembered their credentials, and the helpdesk got flooded from personal phones every season. Identifying which device belonged to which technician took 5 to 10 minutes per call, 5 to 10 calls a day. Hawx now drives Fleet entirely through the API, paired with Tines and Okta, so a device lands in the right fleet with the correct profiles, policies, and apps the moment the technician verifies their identity. Offboarding wipes or locks based on role. Migration took a month. **Source:** the 2026-07-20 customer interview with Loren Farr, IT Manager at Hawx. Loren confirmed on the call that Fleet may use the company name and his name and title, and was told nothing publishes without his approval. **Format:** drafted against the proposed `fleet-case-study-formatting` skill in #49917 and the `content-style` skill: - Three-act narrative (the challenge → why Fleet → the solution → the results), headings in sentence case. - Four `attribution-quote` divs spaced through the narrative rather than clustered. - A `checklist` div for the headline results. - Full endmatter including the build-enforced `summaryChallenge` / `summarySolution` / `summaryKeyResults` (semicolon-separated) plus the company and hero-quote tags. - "About Hawx" lives in `companyInfo` / `companyInfoLineTwo` rather than the body, matching every published case study. ## Why Hawx is a strong story in a segment Fleet's published case studies don't yet cover: iOS-only, a small IT team (3 people, ~500 devices), a seasonal workforce, and an API-only usage pattern where the customer never touches the Fleet UI. It's also a clean Jamf migration narrative with a quantified helpdesk result. ## ⚠️ Blockers before this can be published This is a **draft PR on purpose**. Two items must be resolved first: 1. **The quotes are not verbatim yet.** The interview record is bullet notes, not a transcript, so the four quotes are faithful reconstructions of what Loren described, not transcribed speech. The case-study skill's rule is that quotes are verbatim and never reconstructed. **Loren needs to approve these as his words before merge.** Reviewers should not treat them as citable until he has. 2. **Both image assets are missing.** There is no Hawx logo and no Loren Farr headshot in `website/assets/images/`. `companyLogoFilename` and `quoteAuthorImageFilename` are deliberately stubbed with `TODO-` prefixes so the website build fails loudly rather than shipping broken image references. Real files are needed following the `{descriptor}-{css-width}x{css-height}@2x.{ext}` convention. ## Open questions for reviewers - **The "more than 90%" figure was dropped.** An earlier draft said credentials were forgotten in more than 90% of cases. That number isn't in the interview notes (the notes say "nobody knew their username or password"), so it's omitted. If Loren sourced it, it can go back in. - **The warehoused-device problem is omitted.** The notes describe devices offline more than 30 days needing a reset, currently a 30-minute call, listed as a *current* problem. That would fit a "Looking ahead" section if Fleet is the plan for it, but it doesn't belong in results as an achieved outcome. - **Hero quote choice.** `quoteContent` uses the "slam dunk ... control over the phone itself" quote because it names the differentiator. Loren's closer, "As long as you're not shy about getting into the code, this is a fantastic platform," is arguably the more trustworthy line for Fleet's audience. Easy swap if marketing prefers it. - **Follow-up, not in this PR:** the pull quote could be added to `handbook/company/testimonials.yml` for the `/customers` carousel. Left alone since that file is curated by marketing. - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. — N/A, website content only, not a product change ## Testing - [ ] Added/updated automated tests — N/A, markdown content only - [x] QA'd manually: cross-checked the structure, custom div syntax, and required meta tags against the published case studies (`articles/fastly.md`, `articles/primo.md`) and against the validation logic in `website/scripts/build-static-content.js`; confirmed `summaryKeyResults` is semicolon-separated; confirmed `articleTitle` matches the H1 exactly; grepped `website/assets/images/` and confirmed both referenced image files are absent (hence the `TODO-` stubs). --------- Co-authored-by: Mike Thomas <78363703+mike-j-thomas@users.noreply.github.com> |
||
|
|
bf95fda79e |
Website: update sales leave-behind deck (#50123)
**Related issue:** NA # Checklist for submitter - [x] QA'd all new/changed functionality manually --- Updates the leave-behind deck with revisions from design review: * Updated headings on slide 2 to address pain sooner * Moved "The world has evolved rapidly" heading to slide 3 * Incorporated platform icons into illustration graphic |
||
|
|
fb06efcaac |
Website: redesign /support page with unified card grid and testimonial (#50132)
**Related issue:** Related to fleetdm/confidential#16762 (not a full close — see Notes) # Checklist for submitter ## Testing - [x] QA'd all new/changed functionality manually ## Summary Redesigns fleetdm.com/support: - Merges the previous "Ask the community" and "Support" sections into one unified "How can we help?" section with a single 6-card grid - Adds a new "Professional support" card linking to the SLA section of the go-to-market-operations handbook page - Adds a customer testimonial - Consolidates responsive breakpoints (single-column switch and card centering now both happen at 991px) - Removes target="_blank" from the two cards linking to fleetdm.com itself (Professional support, Ask us anything). ## Related - fleetdm/confidential#16762 ## Notes This is a first iteration toward the broader vision in https://github.com/fleetdm/confidential/issues/16762, discussed in design review but intentionally deferred. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **New Features** * Redesigned the Support page header into a single “How can we help?” entry point. * Added a Professional Support card linking to service-level agreement details. * Introduced a customer quote section with author information. * **Improvements** * Refreshed support card design (cleaner layout, consistent spacing, updated typography and link hover behavior). * **Responsive** * Improved the support card layout across breakpoints, including 3-column to 2-column to single-column grid behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: Eric <eashaw@sailsjs.com> |
||
|
|
bd419cd2de |
Website: update fallback talk to us meeting link (#50182)
Changes: - Updated deliver-talk-to-us-form-submission to return a different Salesforce user ID if territory information could not be found. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Updated the fallback “Talk to us” form submission contact-routing behavior when territory information is unavailable or returns an unexpected value, including changes to the default Calendly meeting link and related warning messaging. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
f25b1ec58d |
Website: update territory routing for talk to us submissions (#50101)
https://github.com/fleetdm/confidential/issues/16947 Changes: - Updated the get-territory-user-id helper to accept `website` and `numberOfEmployees` inputs - Updated the deliver-talk-to-us-form-submission action to send website and number of employees to the get-territory-user-id helper, and updated the `bookingUrlByUserId` dictionary. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved territory routing for “Talk to us” submissions by using employee count, city, and website details. * Updated routing logic to direct submissions to the appropriate Calendly booking link. * Preserved existing validation and error handling for incomplete or unexpected territory lookup results. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
40bc83daa9 |
Add rachaelshaw (API design DRI) as auto reviewer for usage stasts (#50169)
- Context: https://github.com/fleetdm/fleet/pull/49980#issuecomment-5121775909 |