Files
fleet/pkg/patch_policy/patch_policy.go
T
Allen Houchins 98f82ce19f Wrap FMA exists query in parens to fix OR precedence in patched policy (#45647)
## Summary

- `pkg/patch_policy/GenerateQueryForManifest` now wraps the
caller-supplied exists query in an inner set of parentheses before
appending the trailing `AND version_compare(...) < 0` clause. Without
the wrap, any `OR` in the exists body binds *after* the appended `AND`
(SQL precedence: `AND` > `OR`), producing an incorrect `patched` query.
The bug is currently only observable on `codex-cli` (uses `path = ... OR
path LIKE ...`) but would silently break any future FMA whose exists
query contains `OR`.
- All FMA outputs regenerated via `cmd/maintained-apps`. For AND-only
exists queries (the vast majority of existing FMAs), the new patched SQL
is semantically identical to the previous form — just with extra parens
around the WHERE body. `codex-cli/windows.json`'s OR clause is now
correctly grouped.
- `docker-desktop` is unchanged: its patched SQL is constructed inline
in the homebrew ingester at
[ingester.go:198-201](https://github.com/fleetdm/fleet/blob/claude/compassionate-merkle-afbd8a/ee/maintained-apps/ingesters/homebrew/ingester.go#L198-L201)
and bypasses the generator.

### Heads-up: upstream version drift bundled in

The regeneration also pulled in a handful of upstream version bumps that
landed since the last FMA run. These are real upstream changes, not
generator artifacts:

| App | Platform | Old → New |
|---|---|---|
| Figma | windows | 126.3.12 → 126.4.9 |
| GoLand | darwin | 2026.1.1 → 2026.1.2 |
| IntelliJ IDEA | darwin | 2026.1.1 → 2026.1.2 |
| RubyMine | darwin | 2026.1.1 → 2026.1.2 |
| Zed | darwin | 1.2.5 → 1.2.6 |

If you'd prefer these isolated from the paren-only change, let me know
and I'll split the PR.

### Code changes

- [pkg/patch_policy/patch_policy.go](pkg/patch_policy/patch_policy.go):
added `(` to `templateStart` and `)` to `templateEnd{Darwin,Windows}` so
`GenerateQueryForManifest` emits `... NOT EXISTS ((<before>) AND
version_compare(...) < 0);`.
-
[pkg/patch_policy/patch_policy_test.go](pkg/patch_policy/patch_policy_test.go):
updated existing expectations and added an OR-precedence case mirroring
codex-cli's exists query.
-
[ee/maintained-apps/ingesters/homebrew/ingester_test.go](ee/maintained-apps/ingesters/homebrew/ingester_test.go):
updated the generic `Patched` assertion (docker-desktop's hardcoded
expectation is unchanged — it bypasses the generator).
- 282 regenerated files under `ee/maintained-apps/outputs/**/*.json`.

## Test plan

- [x] `go test ./pkg/patch_policy/...` passes (incl. new OR case).
- [x] `go test ./ee/maintained-apps/...` passes.
- [x] `go vet ./pkg/patch_policy/... ./ee/maintained-apps/...` clean.
- [x] `cmd/maintained-apps` runs end-to-end with no errors against the
live Homebrew/winget APIs (with `NETWORK_TEST_GITHUB_TOKEN` set).
- [x] `git diff` audited: every diffed `patched` line on
`outputs/**/*.json` is a paren-only delta; non-`patched` deltas confined
to the 5 upstream version bumps listed above.
- [x] `docker-desktop/darwin.json` unchanged after regeneration.
- [ ] CI green.
2026-05-15 15:49:30 -05:00

98 lines
3.2 KiB
Go

package patch_policy
import (
"errors"
"fmt"
"strings"
"github.com/fleetdm/fleet/v4/server/fleet"
)
type PolicyData struct {
Name string
Platform string
Description string
Resolution string
Query string
ExistsQuery string
Version string
}
const (
// templateStart and templateEnd* wrap the caller-supplied exists query in an
// inner set of parentheses so that any OR in the WHERE body binds before the
// appended AND version_compare(...) clause.
templateStart = "SELECT 1 WHERE NOT EXISTS (("
templateEndDarwin = ") AND version_compare(bundle_short_version, '%s') < 0);"
templateEndWindows = ") AND version_compare(version, '%s') < 0);"
)
var (
ErrWrongPlatform = errors.New("platform should be darwin or windows")
ErrNoExistsQuery = errors.New("exists query was not provided")
)
// GenerateQueryForManifest wraps the "exists" query to create a patch policy query
func GenerateQueryForManifest(p PolicyData) (string, error) {
if p.ExistsQuery == "" {
return "", ErrNoExistsQuery
}
before, _ := strings.CutSuffix(p.ExistsQuery, ";")
// Escape any literal '%' in the exists query (e.g. SQL LIKE patterns)
// so fmt.Sprintf doesn't interpret them as format verbs.
before = strings.ReplaceAll(before, "%", "%%")
switch p.Platform {
case "darwin":
return fmt.Sprintf(templateStart+before+templateEndDarwin, p.Version), nil
case "windows":
return fmt.Sprintf(templateStart+before+templateEndWindows, p.Version), nil
}
return "", ErrWrongPlatform
}
// GenerateFromInstaller creates a patch policy with all fields from an installer
func GenerateFromInstaller(p PolicyData, installer *fleet.SoftwareInstaller) (*PolicyData, error) {
// use the patch policy query from the app manifest if available
query := installer.PatchQuery
if p.Description == "" {
p.Description = "Outdated software might introduce security vulnerabilities or compatibility issues."
}
if p.Resolution == "" {
p.Resolution = "Install the latest version from self-service."
}
switch installer.Platform {
case "darwin":
if p.Name == "" {
p.Name = fmt.Sprintf("macOS - %s up to date", installer.SoftwareTitle)
}
if installer.PatchQuery == "" {
query = defaultMacOSQuery(installer.BundleIdentifier, installer.Version)
}
case "windows":
if p.Name == "" {
p.Name = fmt.Sprintf("Windows - %s up to date", installer.SoftwareTitle)
}
if installer.PatchQuery == "" {
query = defaultWindowsQuery(installer.SoftwareTitle, installer.Version)
}
default:
return nil, ErrWrongPlatform
}
return &PolicyData{Query: query, Platform: installer.Platform, Name: p.Name, Description: p.Description, Resolution: p.Resolution}, nil
}
func defaultMacOSQuery(bundleIdentifier string, version string) string {
patchTemplate := "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = '%s' AND version_compare(bundle_short_version, '%s') < 0);"
return fmt.Sprintf(patchTemplate, bundleIdentifier, version)
}
func defaultWindowsQuery(softwareTitle string, version string) string {
patchTemplate := "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = '%s' AND version_compare(version, '%s') < 0);"
return fmt.Sprintf(patchTemplate, softwareTitle, version)
}