fleet/ee/maintained-apps/maintained_apps.go
Jonathan Katz 0d15fd6cd6
Override patch policy query (#42322)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #41815
### Changes
- Extracted patch policy creation to `pkg/patch_policy`
- Added a `patch_query` column to the `software_installers` table
- By default that column is empty, and patch policies will generate with
the default query if so
- On app manifest ingestion, the appropriate entry in
`software_installers` will save the override "patch" query from the
manifest in patch_query

# Checklist for submitter

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

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

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

## Testing

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

- [ ] QA'd all new/changed functionality manually
- Relied on integration test for FMA version pinning

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [ ] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
2026-03-25 10:32:41 -04:00

91 lines
2.5 KiB
Go

package maintained_apps
import (
"context"
"crypto/sha256"
"encoding/hex"
"io"
"log/slog"
"strings"
)
// Ingester is responsible for ingesting the metadata for maintained apps for a given platform.
// Each platform may have multiple sources for metadata (e.g. homebrew and autopkg for macOS). Each
// source must have its own Ingester implementation.
type Ingester func(ctx context.Context, logger *slog.Logger, inputsPath string, slugFilter string) ([]*FMAManifestApp, error)
const OutputPath = "ee/maintained-apps/outputs"
type FMAQueries struct {
Exists string `json:"exists"`
Patch string `json:"patch"`
}
type FMAManifestApp struct {
Version string `json:"version"`
Queries FMAQueries `json:"queries"`
InstallerURL string `json:"installer_url"`
UniqueIdentifier string `json:"unique_identifier,omitempty"`
InstallScriptRef string `json:"install_script_ref"`
UninstallScriptRef string `json:"uninstall_script_ref"`
InstallScript string `json:"-"`
UninstallScript string `json:"-"`
SHA256 string `json:"sha256"`
Slug string `json:"-"`
Name string `json:"-"`
DefaultCategories []string `json:"default_categories"`
Frozen bool `json:"-"`
UpgradeCode string `json:"upgrade_code,omitempty"`
}
func (a *FMAManifestApp) Platform() string {
parts := strings.Split(a.Slug, "/")
if len(parts) != 2 {
return ""
}
return parts[1]
}
func (a *FMAManifestApp) SlugAppName() string {
parts := strings.Split(a.Slug, "/")
if len(parts) != 2 {
return ""
}
return parts[0]
}
func (a *FMAManifestApp) IsEmpty() bool {
return a.Version == "" &&
a.InstallerURL == "" &&
a.UniqueIdentifier == "" &&
a.InstallScriptRef == "" &&
a.UninstallScriptRef == "" &&
a.SHA256 == "" &&
a.Queries == (FMAQueries{})
}
type FMAManifestFile struct {
Versions []*FMAManifestApp `json:"versions"`
Refs map[string]string `json:"refs"`
}
type FMAListFileApp struct {
Name string `json:"name"`
Slug string `json:"slug"`
Platform string `json:"platform"`
UniqueIdentifier string `json:"unique_identifier"`
Description string `json:"description"`
}
type FMAListFile struct {
Version int `json:"version"`
Apps []FMAListFileApp `json:"apps"`
}
func GetScriptRef(script string) string {
h := sha256.New()
_, _ = io.Copy(h, strings.NewReader(script)) // writes to a Hash can never fail
return hex.EncodeToString(h.Sum(nil))[:8]
}