## 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.
98 lines
3.2 KiB
Go
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)
|
|
}
|