From 0d15fd6cd660d0ca747db4a5cfdfd54ec33d1e10 Mon Sep 17 00:00:00 2001 From: Jonathan Katz <44128041+jkatz01@users.noreply.github.com> Date: Wed, 25 Mar 2026 10:32:41 -0400 Subject: [PATCH] Override patch policy query (#42322) **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`). --- changes/41815-override-patch-policy-query | 1 + .../ingesters/homebrew/ingester.go | 23 ++++ .../ingesters/homebrew/ingester_test.go | 10 +- .../ingesters/winget/ingester.go | 22 ++++ ee/maintained-apps/maintained_apps.go | 1 + ee/server/service/maintained_apps.go | 1 + ee/server/service/software_installers.go | 4 +- pkg/patch_policy/patch_policy.go | 87 +++++++++++++ server/datastore/mysql/activities_test.go | 12 +- .../20260324161944_AddPatchQueryColumn.go | 26 ++++ ...20260324161944_AddPatchQueryColumn_test.go | 55 ++++++++ server/datastore/mysql/policies.go | 89 +++++-------- server/datastore/mysql/policies_test.go | 40 +++++- server/datastore/mysql/schema.sql | 5 +- server/datastore/mysql/software_installers.go | 22 ++-- .../mysql/software_installers_test.go | 4 +- server/datastore/mysql/software_test.go | 31 ++--- server/datastore/mysql/software_titles.go | 3 +- server/datastore/mysql/statistics_test.go | 42 +++---- server/fleet/maintained_apps.go | 1 + server/fleet/software_installer.go | 3 + server/mdm/maintainedapps/sync.go | 2 + server/service/integration_enterprise_test.go | 117 +++++++++++++++++- server/service/integration_mdm_test.go | 1 + server/service/testing_utils.go | 8 +- 25 files changed, 487 insertions(+), 123 deletions(-) create mode 100644 changes/41815-override-patch-policy-query create mode 100644 pkg/patch_policy/patch_policy.go create mode 100644 server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn.go create mode 100644 server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn_test.go diff --git a/changes/41815-override-patch-policy-query b/changes/41815-override-patch-policy-query new file mode 100644 index 0000000000..c2d7106004 --- /dev/null +++ b/changes/41815-override-patch-policy-query @@ -0,0 +1 @@ +- Added ability to specify custom patch policy query in an FMA manifest diff --git a/ee/maintained-apps/ingesters/homebrew/ingester.go b/ee/maintained-apps/ingesters/homebrew/ingester.go index 5d8fe639a3..8b7c5ff5f6 100644 --- a/ee/maintained-apps/ingesters/homebrew/ingester.go +++ b/ee/maintained-apps/ingesters/homebrew/ingester.go @@ -17,7 +17,9 @@ import ( external_refs "github.com/fleetdm/fleet/v4/ee/maintained-apps/ingesters/homebrew/external_refs" "github.com/fleetdm/fleet/v4/pkg/fleethttp" "github.com/fleetdm/fleet/v4/pkg/optjson" + "github.com/fleetdm/fleet/v4/pkg/patch_policy" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" + "github.com/ghodss/yaml" ) func IngestApps(ctx context.Context, logger *slog.Logger, inputsPath, slugFilter string) ([]*maintained_apps.FMAManifestApp, error) { @@ -213,6 +215,26 @@ func (i *brewIngester) ingestOne(ctx context.Context, input inputApp) (*maintain external_refs.EnrichManifest(out) + // create patch policy + if input.PatchPolicyPath != "" { + policyBytes, err := os.ReadFile(input.PatchPolicyPath) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "reading provided patch policy path") + } + + p := patch_policy.PolicyData{} + if err := yaml.Unmarshal(policyBytes, &p); err != nil { + return nil, ctxerr.Wrap(ctx, err, "unmarshaling patch policy") + } + + p.Platform = "darwin" + p.Version = out.Version + out.Queries.Patch, err = patch_policy.GenerateFromManifest(p) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "creating patch policy") + } + } + return out, nil } @@ -233,6 +255,7 @@ type inputApp struct { Frozen bool `json:"frozen"` InstallScriptPath string `json:"install_script_path"` UninstallScriptPath string `json:"uninstall_script_path"` + PatchPolicyPath string `json:"patch_policy_path"` } type brewCask struct { diff --git a/ee/maintained-apps/ingesters/homebrew/ingester_test.go b/ee/maintained-apps/ingesters/homebrew/ingester_test.go index 9423fc6365..f4645d9319 100644 --- a/ee/maintained-apps/ingesters/homebrew/ingester_test.go +++ b/ee/maintained-apps/ingesters/homebrew/ingester_test.go @@ -25,6 +25,9 @@ func TestIngestValidations(t *testing.T) { testUninstallScriptContents := "this is a test uninstall script" require.NoError(t, os.WriteFile(path.Join(tempDir, "uninstall_script.sh"), []byte(testUninstallScriptContents), 0644)) + testPatchPolicyContents := `query: "SELECT 1;"` + require.NoError(t, os.WriteFile(path.Join(tempDir, "policy.yml"), []byte(testPatchPolicyContents), 0644)) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var cask brewCask @@ -86,7 +89,7 @@ func TestIngestValidations(t *testing.T) { Version: "1.0", } - case "ok", "install_script_path", "uninstall_script_path", "uninstall_script_path_with_pre", "uninstall_script_path_with_post": + case "ok", "install_script_path", "uninstall_script_path", "uninstall_script_path_with_pre", "uninstall_script_path_with_post", "patch_policy_path": cask = brewCask{ Token: appToken, Name: []string{appToken}, @@ -123,6 +126,7 @@ func TestIngestValidations(t *testing.T) { {"", inputApp{Token: "uninstall_script_path", UniqueIdentifier: "abc", InstallerFormat: "pkg", UninstallScriptPath: path.Join(tempDir, "uninstall_script.sh")}}, {"cannot provide pre-uninstall scripts if uninstall script is provided", inputApp{Token: "uninstall_script_path_with_pre", UniqueIdentifier: "abc", InstallerFormat: "pkg", UninstallScriptPath: path.Join(tempDir, "uninstall_script.sh"), PreUninstallScripts: []string{"foo", "bar"}}}, {"cannot provide post-uninstall scripts if uninstall script is provided", inputApp{Token: "uninstall_script_path_with_post", UniqueIdentifier: "abc", InstallerFormat: "pkg", UninstallScriptPath: path.Join(tempDir, "uninstall_script.sh"), PostUninstallScripts: []string{"foo", "bar"}}}, + {"", inputApp{Token: "patch_policy_path", UniqueIdentifier: "abc", InstallerFormat: "pkg", PatchPolicyPath: path.Join(tempDir, "policy.yml")}}, } for _, c := range cases { t.Run(c.inputApp.Token, func(t *testing.T) { @@ -148,6 +152,10 @@ func TestIngestValidations(t *testing.T) { require.Equal(t, testUninstallScriptContents, out.UninstallScript) } + if c.inputApp.PatchPolicyPath != "" { + require.Equal(t, "SELECT 1;", out.Queries.Patch) + } + }) } } diff --git a/ee/maintained-apps/ingesters/winget/ingester.go b/ee/maintained-apps/ingesters/winget/ingester.go index 766928df82..e8e9e2da5e 100644 --- a/ee/maintained-apps/ingesters/winget/ingester.go +++ b/ee/maintained-apps/ingesters/winget/ingester.go @@ -16,6 +16,7 @@ import ( external_refs "github.com/fleetdm/fleet/v4/ee/maintained-apps/ingesters/winget/external_refs" "github.com/fleetdm/fleet/v4/pkg/file" "github.com/fleetdm/fleet/v4/pkg/fleethttp" + "github.com/fleetdm/fleet/v4/pkg/patch_policy" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" feednvd "github.com/fleetdm/fleet/v4/server/vulnerabilities/nvd/tools/cvefeed/nvd" "github.com/google/go-github/v37/github" @@ -366,6 +367,26 @@ func (i *wingetIngester) ingestOne(ctx context.Context, input inputApp) (*mainta external_refs.EnrichManifest(&out) + // create patch policy + if input.PatchPolicyPath != "" { + policyBytes, err := os.ReadFile(input.PatchPolicyPath) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "reading provided patch policy path") + } + + p := patch_policy.PolicyData{} + if err := yaml.Unmarshal(policyBytes, &p); err != nil { + return nil, ctxerr.Wrap(ctx, err, "unmarshaling patch policy") + } + + p.Platform = "windows" + p.Version = out.Version + out.Queries.Patch, err = patch_policy.GenerateFromManifest(p) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "creating patch policy") + } + } + return &out, nil } @@ -433,6 +454,7 @@ type inputApp struct { IgnoreHash bool `json:"ignore_hash"` DefaultCategories []string `json:"default_categories"` Frozen bool `json:"frozen"` + PatchPolicyPath string `json:"patch_policy_path"` } type installerManifest struct { diff --git a/ee/maintained-apps/maintained_apps.go b/ee/maintained-apps/maintained_apps.go index 6a6f4a6150..1f42b09be0 100644 --- a/ee/maintained-apps/maintained_apps.go +++ b/ee/maintained-apps/maintained_apps.go @@ -18,6 +18,7 @@ const OutputPath = "ee/maintained-apps/outputs" type FMAQueries struct { Exists string `json:"exists"` + Patch string `json:"patch"` } type FMAManifestApp struct { diff --git a/ee/server/service/maintained_apps.go b/ee/server/service/maintained_apps.go index 6668b965e0..b0023dff68 100644 --- a/ee/server/service/maintained_apps.go +++ b/ee/server/service/maintained_apps.go @@ -159,6 +159,7 @@ func (svc *Service) AddFleetMaintainedApp( AutomaticInstallQuery: app.AutomaticInstallQuery, Categories: app.Categories, URL: app.InstallerURL, + PatchQuery: app.PatchQuery, } payload.Categories = server.RemoveDuplicatesFromSlice(payload.Categories) diff --git a/ee/server/service/software_installers.go b/ee/server/service/software_installers.go index 862d378947..e29eddec60 100644 --- a/ee/server/service/software_installers.go +++ b/ee/server/service/software_installers.go @@ -2144,7 +2144,7 @@ func (svc *Service) softwareInstallerPayloadFromSlug(ctx context.Context, payloa } return err } - _, err = maintained_apps.Hydrate(ctx, app, payload.RollbackVersion, teamID, svc.ds) + fma, err := maintained_apps.Hydrate(ctx, app, payload.RollbackVersion, teamID, svc.ds) if err != nil { return err } @@ -2164,6 +2164,7 @@ func (svc *Service) softwareInstallerPayloadFromSlug(ctx context.Context, payloa if len(payload.Categories) == 0 { payload.Categories = app.Categories } + payload.MaintainedApp.PatchQuery = fma.PatchQuery return nil } @@ -2600,6 +2601,7 @@ func (svc *Service) softwareBatchUpload( installer.BundleIdentifier = p.MaintainedApp.BundleIdentifier() installer.StorageID = p.MaintainedApp.SHA256 installer.FleetMaintainedAppID = &p.MaintainedApp.ID + installer.PatchQuery = p.MaintainedApp.PatchQuery } var ext string diff --git a/pkg/patch_policy/patch_policy.go b/pkg/patch_policy/patch_policy.go new file mode 100644 index 0000000000..585c5962d3 --- /dev/null +++ b/pkg/patch_policy/patch_policy.go @@ -0,0 +1,87 @@ +package patch_policy + +import ( + "errors" + "fmt" + "strings" + + "github.com/fleetdm/fleet/v4/server/fleet" +) + +type PolicyData struct { + Name string + Query string + Platform string + Description string + Resolution string + Version string +} + +const versionVariable = "$FMA_VERSION" + +var ( + ErrEmptyQuery = errors.New("query should not be empty") + ErrWrongPlatform = errors.New("platform should be darwin or windows") +) + +// GenerateFromManifest replaces the $FMA_VERSION variable and checks platform +func GenerateFromManifest(p PolicyData) (string, error) { + if p.Query == "" { + return "", ErrEmptyQuery + } + // Version is extracted from the manifest so this should be safe to run as an osquery query + query := strings.ReplaceAll(p.Query, versionVariable, p.Version) + + switch p.Platform { + case "darwin": + case "windows": + default: + return "", ErrWrongPlatform + } + + return query, nil +} + +// 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 = fmt.Sprintf( + "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = '%s' AND version_compare(bundle_short_version, '%s') < 0);", + installer.BundleIdentifier, + installer.Version, + ) + } + case "windows": + if p.Name == "" { + p.Name = fmt.Sprintf("Windows - %s up to date", installer.SoftwareTitle) + } + if installer.PatchQuery == "" { + // TODO: use upgrade code to improve accuracy? + query = fmt.Sprintf( + "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = '%s' AND version_compare(version, '%s') < 0);", + 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 +} diff --git a/server/datastore/mysql/activities_test.go b/server/datastore/mysql/activities_test.go index 51266031d0..2d43fa500c 100644 --- a/server/datastore/mysql/activities_test.go +++ b/server/datastore/mysql/activities_test.go @@ -2001,13 +2001,13 @@ func testActivateScriptPackageInstallWithCorruptPayload(t *testing.T, ds *Datast extension, version, platform, install_script_content_id, pre_install_query, post_install_script_content_id, uninstall_script_content_id, self_service, user_id, user_name, user_email, package_ids, - fleet_maintained_app_id, url, upgrade_code + fleet_maintained_app_id, url, upgrade_code, patch_query ) - VALUES (NULL, 0, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, NULL, ?, ?) + VALUES (NULL, 0, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?) ` res, err = ds.writer(ctx).ExecContext(ctx, installerStmt, titleID, "storage-123", "test-script.sh", "sh", "", "linux", scriptContentID, - "", scriptContentID, 0, u.ID, u.Name, u.Email, "", "", "") + "", scriptContentID, 0, u.ID, u.Name, u.Email, "", "", "", "") require.NoError(t, err) installerID, _ := res.LastInsertId() @@ -2176,13 +2176,13 @@ func testActivateScriptPackageUninstallWithCorruptPayload(t *testing.T, ds *Data extension, version, platform, install_script_content_id, pre_install_query, post_install_script_content_id, uninstall_script_content_id, self_service, user_id, user_name, user_email, package_ids, - fleet_maintained_app_id, url, upgrade_code + fleet_maintained_app_id, url, upgrade_code, patch_query ) - VALUES (NULL, 0, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, NULL, ?, ?) + VALUES (NULL, 0, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?) ` res, err = ds.writer(ctx).ExecContext(ctx, installerStmt, titleID, "storage-id-uninstall", "test-uninstall.sh", "sh", "", "linux", scriptContentID, - "", scriptContentID, 0, u.ID, u.Name, u.Email, "", "", "") + "", scriptContentID, 0, u.ID, u.Name, u.Email, "", "", "", "") require.NoError(t, err) installerID, _ := res.LastInsertId() diff --git a/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn.go b/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn.go new file mode 100644 index 0000000000..9e3c087352 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn.go @@ -0,0 +1,26 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20260324161944, Down_20260324161944) +} + +func Up_20260324161944(tx *sql.Tx) error { + stmt := ` + ALTER TABLE software_installers + ADD COLUMN patch_query TEXT COLLATE utf8mb4_unicode_ci NOT NULL; + ` + if _, err := tx.Exec(stmt); err != nil { + return fmt.Errorf("add patch_query to software_installers: %w", err) + } + + return nil +} + +func Down_20260324161944(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn_test.go b/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn_test.go new file mode 100644 index 0000000000..8b35fe6e66 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20260324161944_AddPatchQueryColumn_test.go @@ -0,0 +1,55 @@ +package tables + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestUp_20260324161944(t *testing.T) { + db := applyUpToPrev(t) + + insertInstallerStmt := ` + INSERT INTO software_installers ( + team_id, + global_or_team_id, + title_id, + storage_id, + filename, + extension, + version, + install_script_content_id, + uninstall_script_content_id, + platform, + package_ids + ) VALUES (NULL, 0, ?, "storage_id", ?, "pkg", "1.0", ?, ?, "darwin", "") +` + + insertTitleStmt := ` + INSERT INTO software_titles (name, source, bundle_identifier) + VALUES (?, 'apps', ?) +` + + scriptID := execNoErrLastID(t, db, `INSERT INTO script_contents (md5_checksum, contents) VALUES (UNHEX(MD5('echo hello')), 'echo hello')`) + + title1 := execNoErrLastID(t, db, insertTitleStmt, "App 1", "com.app1") + title2 := execNoErrLastID(t, db, insertTitleStmt, "App 2", "com.app2") + installer1 := execNoErrLastID(t, db, insertInstallerStmt, title1, "app1.pkg", scriptID, scriptID) + installer2 := execNoErrLastID(t, db, insertInstallerStmt, title2, "app2.pkg", scriptID, scriptID) + + var timestamp, timestamp2 time.Time + require.NoError(t, db.Get(×tamp, `SELECT updated_at FROM software_installers WHERE id = ?`, installer1)) + + // Apply current migration. + applyNext(t, db) + + var patchQuery string + require.NoError(t, db.Get(&patchQuery, `SELECT patch_query FROM software_installers WHERE id = ?`, installer1)) + require.Equal(t, "", patchQuery) + require.NoError(t, db.Get(&patchQuery, `SELECT patch_query FROM software_installers WHERE id = ?`, installer2)) + require.Equal(t, "", patchQuery) + require.NoError(t, db.Get(×tamp2, `SELECT updated_at FROM software_installers WHERE id = ?`, installer1)) + require.Equal(t, timestamp, timestamp2) + +} diff --git a/server/datastore/mysql/policies.go b/server/datastore/mysql/policies.go index af097218f1..bb0295ee94 100644 --- a/server/datastore/mysql/policies.go +++ b/server/datastore/mysql/policies.go @@ -14,6 +14,7 @@ import ( "golang.org/x/text/unicode/norm" + "github.com/fleetdm/fleet/v4/pkg/patch_policy" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" "github.com/fleetdm/fleet/v4/server/fleet" common_mysql "github.com/fleetdm/fleet/v4/server/platform/mysql" @@ -1064,20 +1065,22 @@ func (ds *Datastore) NewTeamPolicy(ctx context.Context, teamID uint, authorID *u args.Type = fleet.PolicyTypeDynamic } if args.Type == fleet.PolicyTypePatch { - generated, err := ds.generatePatchPolicy(ctx, teamID, *args.PatchSoftwareTitleID) + installer, err := ds.getPatchPolicyInstaller(ctx, teamID, *args.PatchSoftwareTitleID) + if err != nil { + return nil, ctxerr.Wrap(ctx, err, "getting patch policy installer") + } + generated, err := patch_policy.GenerateFromInstaller(patch_policy.PolicyData{ + Name: args.Name, + Description: args.Description, + Resolution: args.Resolution, + }, installer) if err != nil { return nil, ctxerr.Wrap(ctx, err, "generating patch policy fields") } - if args.Name == "" { - args.Name = generated.Name - } - if args.Description == "" { - args.Description = generated.Description - } - if args.Resolution == "" { - args.Resolution = generated.Resolution - } + args.Name = generated.Name + args.Description = generated.Description + args.Resolution = generated.Resolution args.Platform = generated.Platform args.Query = generated.Query } @@ -1470,23 +1473,26 @@ func (ds *Datastore) ApplyPolicySpecs(ctx context.Context, authorID uint, specs fmaTitleID := fmaTitleIDs[teamNameToID[spec.Team]][spec.FleetMaintainedAppSlug] - // generate new up-to-date query and other fields for patch policy + // generate new up-to-date patch policy if spec.Type == fleet.PolicyTypePatch { - patch, err := ds.generatePatchPolicy(ctx, ptr.ValOrZero(teamID), *fmaTitleID) + installer, err := ds.getPatchPolicyInstaller(ctx, ptr.ValOrZero(teamID), *fmaTitleID) + if err != nil { + return ctxerr.Wrap(ctx, err, "getting patch policy installer") + } + generated, err := patch_policy.GenerateFromInstaller(patch_policy.PolicyData{ + Name: spec.Name, + Description: spec.Description, + Resolution: spec.Resolution, + }, installer) if err != nil { return ctxerr.Wrap(ctx, err, "generating patch policy fields") } - if spec.Name == "" { - spec.Name = patch.Name - } - if spec.Description == "" { - spec.Description = patch.Description - } - if spec.Resolution == "" { - spec.Resolution = patch.Resolution - } - spec.Platform = patch.Platform - spec.Query = patch.Query + + spec.Name = generated.Name + spec.Description = generated.Description + spec.Resolution = generated.Resolution + spec.Platform = generated.Platform + spec.Query = generated.Query } res, err := tx.ExecContext( @@ -2624,15 +2630,7 @@ func (ds *Datastore) getPoliciesBySoftwareTitleIDs( return policies, nil } -type patchPolicy struct { - Name string - Query string - Platform string - Description string - Resolution string -} - -func (ds *Datastore) generatePatchPolicy(ctx context.Context, teamID uint, titleID uint) (*patchPolicy, error) { +func (ds *Datastore) getPatchPolicyInstaller(ctx context.Context, teamID uint, titleID uint) (*fleet.SoftwareInstaller, error) { installer, err := ds.GetSoftwareInstallerMetadataByTeamAndTitleID(ctx, &teamID, titleID, false) if err != nil { return nil, ctxerr.Wrap(ctx, err, "getting software installer") @@ -2642,32 +2640,7 @@ func (ds *Datastore) generatePatchPolicy(ctx context.Context, teamID uint, title Message: fmt.Sprintf("Software installer for Fleet maintained app with title ID %d does not exist for team ID %d", titleID, teamID), }) } - - var policy patchPolicy - switch { - case installer.Platform == string(fleet.MacOSPlatform): - policy.Query = fmt.Sprintf( - "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = '%s' AND version_compare(bundle_short_version, '%s') < 0);", - installer.BundleIdentifier, - installer.Version, - ) - policy.Platform = string(fleet.MacOSPlatform) - policy.Name = fmt.Sprintf("macOS - %s up to date", installer.SoftwareTitle) - case installer.Platform == "windows": - // TODO: use upgrade code to improve accuracy? - policy.Query = fmt.Sprintf( - "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = '%s' AND version_compare(bundle_short_version, '%s') < 0);", - installer.SoftwareTitle, - installer.Version, - ) - policy.Platform = "windows" - policy.Name = fmt.Sprintf("Windows - %s up to date", installer.SoftwareTitle) - default: - } - policy.Description = "Outdated software might introduce security vulnerabilities or compatibility issues." - policy.Resolution = "Install the latest version from self-service." - - return &policy, nil + return installer, nil } func (ds *Datastore) GetPatchPolicy(ctx context.Context, teamID *uint, titleID uint) (*fleet.PatchPolicyData, error) { diff --git a/server/datastore/mysql/policies_test.go b/server/datastore/mysql/policies_test.go index 7bd17e11d8..0c7bd38260 100644 --- a/server/datastore/mysql/policies_test.go +++ b/server/datastore/mysql/policies_test.go @@ -7006,8 +7006,8 @@ func testPolicyModificationResetsAttemptNumber(t *testing.T, ds *Datastore) { installerID := int64(0) ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { res, err := q.ExecContext(ctx, ` - INSERT INTO software_installers (team_id, global_or_team_id, title_id, storage_id, filename, extension, version, install_script_content_id, uninstall_script_content_id, platform, package_ids) - VALUES (?, ?, ?, 'storage', 'test.pkg', 'pkg', '1.0', ?, ?, 'darwin', '') + INSERT INTO software_installers (team_id, global_or_team_id, title_id, storage_id, filename, extension, version, install_script_content_id, uninstall_script_content_id, platform, package_ids, patch_query) + VALUES (?, ?, ?, 'storage', 'test.pkg', 'pkg', '1.0', ?, ?, 'darwin', '', '') `, team.ID, team.ID, titleID, scriptContentID, scriptContentID) if err != nil { return err @@ -7638,6 +7638,42 @@ func testTeamPatchPolicy(t *testing.T, ds *Datastore) { _, err = ds.GetPatchPolicy(ctx, &team1.ID, titleID2) require.True(t, fleet.IsNotFound(err)) + + maintainedApp2, err := ds.UpsertMaintainedApp(ctx, &fleet.MaintainedApp{ + Name: "Maintained2", + Slug: "maintained2", + Platform: "windows", + UniqueIdentifier: "fleet.maintained2", + }) + require.NoError(t, err) + + payload3 := &fleet.UploadSoftwareInstallerPayload{ + InstallScript: "hello", + PreInstallQuery: "SELECT 1", + PostInstallScript: "world", + StorageID: "storage2", + Filename: "maintained2", + Title: "Maintained2", + Version: "1.0", + Source: "programs", + Platform: "windows", + BundleIdentifier: "fleet.maintained2", + UserID: user1.ID, + TeamID: &team1.ID, + ValidatedLabels: &fleet.LabelIdentsWithScope{}, + FleetMaintainedAppID: &maintainedApp2.ID, + } + _, titleID3, err := ds.MatchOrCreateSoftwareInstaller(context.Background(), payload3) + require.NoError(t, err) + + p5, err := ds.NewTeamPolicy(ctx, team1.ID, &user1.ID, fleet.PolicyPayload{ + Type: fleet.PolicyTypePatch, + PatchSoftwareTitleID: &titleID3, + }) + require.NoError(t, err) + require.Equal(t, "Windows - Maintained2 up to date", p5.Name) + require.Equal(t, "windows", p5.Platform) + require.Equal(t, "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Maintained2' AND version_compare(version, '1.0') < 0);", p5.Query) } func testTeamPolicyAutomationFilter(t *testing.T, ds *Datastore) { diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 3e39f6683c..ad4c9d1395 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -1805,9 +1805,9 @@ CREATE TABLE `migration_status_tables` ( `is_applied` tinyint(1) NOT NULL, `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=500 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=501 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250121094045,1,'2020-01-01 01:01:01'),(347,20250121094500,1,'2020-01-01 01:01:01'),(348,20250121094600,1,'2020-01-01 01:01:01'),(349,20250121094700,1,'2020-01-01 01:01:01'),(350,20250124194347,1,'2020-01-01 01:01:01'),(351,20250127162751,1,'2020-01-01 01:01:01'),(352,20250213104005,1,'2020-01-01 01:01:01'),(353,20250214205657,1,'2020-01-01 01:01:01'),(354,20250217093329,1,'2020-01-01 01:01:01'),(355,20250219090511,1,'2020-01-01 01:01:01'),(356,20250219100000,1,'2020-01-01 01:01:01'),(357,20250219142401,1,'2020-01-01 01:01:01'),(358,20250224184002,1,'2020-01-01 01:01:01'),(359,20250225085436,1,'2020-01-01 01:01:01'),(360,20250226000000,1,'2020-01-01 01:01:01'),(361,20250226153445,1,'2020-01-01 01:01:01'),(362,20250304162702,1,'2020-01-01 01:01:01'),(363,20250306144233,1,'2020-01-01 01:01:01'),(364,20250313163430,1,'2020-01-01 01:01:01'),(365,20250317130944,1,'2020-01-01 01:01:01'),(366,20250318165922,1,'2020-01-01 01:01:01'),(367,20250320132525,1,'2020-01-01 01:01:01'),(368,20250320200000,1,'2020-01-01 01:01:01'),(369,20250326161930,1,'2020-01-01 01:01:01'),(370,20250326161931,1,'2020-01-01 01:01:01'),(371,20250331042354,1,'2020-01-01 01:01:01'),(372,20250331154206,1,'2020-01-01 01:01:01'),(373,20250401155831,1,'2020-01-01 01:01:01'),(374,20250408133233,1,'2020-01-01 01:01:01'),(375,20250410104321,1,'2020-01-01 01:01:01'),(376,20250421085116,1,'2020-01-01 01:01:01'),(377,20250422095806,1,'2020-01-01 01:01:01'),(378,20250424153059,1,'2020-01-01 01:01:01'),(379,20250430103833,1,'2020-01-01 01:01:01'),(380,20250430112622,1,'2020-01-01 01:01:01'),(381,20250501162727,1,'2020-01-01 01:01:01'),(382,20250502154517,1,'2020-01-01 01:01:01'),(383,20250502222222,1,'2020-01-01 01:01:01'),(384,20250507170845,1,'2020-01-01 01:01:01'),(385,20250513162912,1,'2020-01-01 01:01:01'),(386,20250519161614,1,'2020-01-01 01:01:01'),(387,20250519170000,1,'2020-01-01 01:01:01'),(388,20250520153848,1,'2020-01-01 01:01:01'),(389,20250528115932,1,'2020-01-01 01:01:01'),(390,20250529102706,1,'2020-01-01 01:01:01'),(391,20250603105558,1,'2020-01-01 01:01:01'),(392,20250609102714,1,'2020-01-01 01:01:01'),(393,20250609112613,1,'2020-01-01 01:01:01'),(394,20250613103810,1,'2020-01-01 01:01:01'),(395,20250616193950,1,'2020-01-01 01:01:01'),(396,20250624140757,1,'2020-01-01 01:01:01'),(397,20250626130239,1,'2020-01-01 01:01:01'),(398,20250629131032,1,'2020-01-01 01:01:01'),(399,20250701155654,1,'2020-01-01 01:01:01'),(400,20250707095725,1,'2020-01-01 01:01:01'),(401,20250716152435,1,'2020-01-01 01:01:01'),(402,20250718091828,1,'2020-01-01 01:01:01'),(403,20250728122229,1,'2020-01-01 01:01:01'),(404,20250731122715,1,'2020-01-01 01:01:01'),(405,20250731151000,1,'2020-01-01 01:01:01'),(406,20250803000000,1,'2020-01-01 01:01:01'),(407,20250805083116,1,'2020-01-01 01:01:01'),(408,20250807140441,1,'2020-01-01 01:01:01'),(409,20250808000000,1,'2020-01-01 01:01:01'),(410,20250811155036,1,'2020-01-01 01:01:01'),(411,20250813205039,1,'2020-01-01 01:01:01'),(412,20250814123333,1,'2020-01-01 01:01:01'),(413,20250815130115,1,'2020-01-01 01:01:01'),(414,20250816115553,1,'2020-01-01 01:01:01'),(415,20250817154557,1,'2020-01-01 01:01:01'),(416,20250825113751,1,'2020-01-01 01:01:01'),(417,20250827113140,1,'2020-01-01 01:01:01'),(418,20250828120836,1,'2020-01-01 01:01:01'),(419,20250902112642,1,'2020-01-01 01:01:01'),(420,20250904091745,1,'2020-01-01 01:01:01'),(421,20250905090000,1,'2020-01-01 01:01:01'),(422,20250922083056,1,'2020-01-01 01:01:01'),(423,20250923120000,1,'2020-01-01 01:01:01'),(424,20250926123048,1,'2020-01-01 01:01:01'),(425,20251015103505,1,'2020-01-01 01:01:01'),(426,20251015103600,1,'2020-01-01 01:01:01'),(427,20251015103700,1,'2020-01-01 01:01:01'),(428,20251015103800,1,'2020-01-01 01:01:01'),(429,20251015103900,1,'2020-01-01 01:01:01'),(430,20251028140000,1,'2020-01-01 01:01:01'),(431,20251028140100,1,'2020-01-01 01:01:01'),(432,20251028140110,1,'2020-01-01 01:01:01'),(433,20251028140200,1,'2020-01-01 01:01:01'),(434,20251028140300,1,'2020-01-01 01:01:01'),(435,20251028140400,1,'2020-01-01 01:01:01'),(436,20251031154558,1,'2020-01-01 01:01:01'),(437,20251103160848,1,'2020-01-01 01:01:01'),(438,20251104112849,1,'2020-01-01 01:01:01'),(439,20251106000000,1,'2020-01-01 01:01:01'),(440,20251107164629,1,'2020-01-01 01:01:01'),(441,20251107170854,1,'2020-01-01 01:01:01'),(442,20251110172137,1,'2020-01-01 01:01:01'),(443,20251111153133,1,'2020-01-01 01:01:01'),(444,20251117020000,1,'2020-01-01 01:01:01'),(445,20251117020100,1,'2020-01-01 01:01:01'),(446,20251117020200,1,'2020-01-01 01:01:01'),(447,20251121100000,1,'2020-01-01 01:01:01'),(448,20251121124239,1,'2020-01-01 01:01:01'),(449,20251124090450,1,'2020-01-01 01:01:01'),(450,20251124135808,1,'2020-01-01 01:01:01'),(451,20251124140138,1,'2020-01-01 01:01:01'),(452,20251124162948,1,'2020-01-01 01:01:01'),(453,20251127113559,1,'2020-01-01 01:01:01'),(454,20251202162232,1,'2020-01-01 01:01:01'),(455,20251203170808,1,'2020-01-01 01:01:01'),(456,20251207050413,1,'2020-01-01 01:01:01'),(457,20251208215800,1,'2020-01-01 01:01:01'),(458,20251209221730,1,'2020-01-01 01:01:01'),(459,20251209221850,1,'2020-01-01 01:01:01'),(460,20251215163721,1,'2020-01-01 01:01:01'),(461,20251217000000,1,'2020-01-01 01:01:01'),(462,20251217120000,1,'2020-01-01 01:01:01'),(463,20251229000000,1,'2020-01-01 01:01:01'),(464,20251229000010,1,'2020-01-01 01:01:01'),(465,20251229000020,1,'2020-01-01 01:01:01'),(466,20260106000000,1,'2020-01-01 01:01:01'),(467,20260108200708,1,'2020-01-01 01:01:01'),(468,20260108214732,1,'2020-01-01 01:01:01'),(469,20260109231821,1,'2020-01-01 01:01:01'),(470,20260113012054,1,'2020-01-01 01:01:01'),(471,20260124200020,1,'2020-01-01 01:01:01'),(472,20260126150840,1,'2020-01-01 01:01:01'),(473,20260126210724,1,'2020-01-01 01:01:01'),(474,20260202151756,1,'2020-01-01 01:01:01'),(475,20260205184907,1,'2020-01-01 01:01:01'),(476,20260210151544,1,'2020-01-01 01:01:01'),(477,20260210155109,1,'2020-01-01 01:01:01'),(478,20260210181120,1,'2020-01-01 01:01:01'),(479,20260211200153,1,'2020-01-01 01:01:01'),(480,20260217141240,1,'2020-01-01 01:01:01'),(481,20260217200906,1,'2020-01-01 01:01:01'),(482,20260218175704,1,'2020-01-01 01:01:01'),(483,20260314120000,1,'2020-01-01 01:01:01'),(484,20260316120000,1,'2020-01-01 01:01:01'),(485,20260316120001,1,'2020-01-01 01:01:01'),(486,20260316120002,1,'2020-01-01 01:01:01'),(487,20260316120003,1,'2020-01-01 01:01:01'),(488,20260316120004,1,'2020-01-01 01:01:01'),(489,20260316120005,1,'2020-01-01 01:01:01'),(490,20260316120006,1,'2020-01-01 01:01:01'),(491,20260316120007,1,'2020-01-01 01:01:01'),(492,20260316120008,1,'2020-01-01 01:01:01'),(493,20260316120009,1,'2020-01-01 01:01:01'),(494,20260316120010,1,'2020-01-01 01:01:01'),(495,20260316120011,1,'2020-01-01 01:01:01'),(496,20260317120000,1,'2020-01-01 01:01:01'),(497,20260318184559,1,'2020-01-01 01:01:01'),(498,20260319120000,1,'2020-01-01 01:01:01'),(499,20260323144117,1,'2020-01-01 01:01:01'); +INSERT INTO `migration_status_tables` VALUES (1,0,1,'2020-01-01 01:01:01'),(2,20161118193812,1,'2020-01-01 01:01:01'),(3,20161118211713,1,'2020-01-01 01:01:01'),(4,20161118212436,1,'2020-01-01 01:01:01'),(5,20161118212515,1,'2020-01-01 01:01:01'),(6,20161118212528,1,'2020-01-01 01:01:01'),(7,20161118212538,1,'2020-01-01 01:01:01'),(8,20161118212549,1,'2020-01-01 01:01:01'),(9,20161118212557,1,'2020-01-01 01:01:01'),(10,20161118212604,1,'2020-01-01 01:01:01'),(11,20161118212613,1,'2020-01-01 01:01:01'),(12,20161118212621,1,'2020-01-01 01:01:01'),(13,20161118212630,1,'2020-01-01 01:01:01'),(14,20161118212641,1,'2020-01-01 01:01:01'),(15,20161118212649,1,'2020-01-01 01:01:01'),(16,20161118212656,1,'2020-01-01 01:01:01'),(17,20161118212758,1,'2020-01-01 01:01:01'),(18,20161128234849,1,'2020-01-01 01:01:01'),(19,20161230162221,1,'2020-01-01 01:01:01'),(20,20170104113816,1,'2020-01-01 01:01:01'),(21,20170105151732,1,'2020-01-01 01:01:01'),(22,20170108191242,1,'2020-01-01 01:01:01'),(23,20170109094020,1,'2020-01-01 01:01:01'),(24,20170109130438,1,'2020-01-01 01:01:01'),(25,20170110202752,1,'2020-01-01 01:01:01'),(26,20170111133013,1,'2020-01-01 01:01:01'),(27,20170117025759,1,'2020-01-01 01:01:01'),(28,20170118191001,1,'2020-01-01 01:01:01'),(29,20170119234632,1,'2020-01-01 01:01:01'),(30,20170124230432,1,'2020-01-01 01:01:01'),(31,20170127014618,1,'2020-01-01 01:01:01'),(32,20170131232841,1,'2020-01-01 01:01:01'),(33,20170223094154,1,'2020-01-01 01:01:01'),(34,20170306075207,1,'2020-01-01 01:01:01'),(35,20170309100733,1,'2020-01-01 01:01:01'),(36,20170331111922,1,'2020-01-01 01:01:01'),(37,20170502143928,1,'2020-01-01 01:01:01'),(38,20170504130602,1,'2020-01-01 01:01:01'),(39,20170509132100,1,'2020-01-01 01:01:01'),(40,20170519105647,1,'2020-01-01 01:01:01'),(41,20170519105648,1,'2020-01-01 01:01:01'),(42,20170831234300,1,'2020-01-01 01:01:01'),(43,20170831234301,1,'2020-01-01 01:01:01'),(44,20170831234303,1,'2020-01-01 01:01:01'),(45,20171116163618,1,'2020-01-01 01:01:01'),(46,20171219164727,1,'2020-01-01 01:01:01'),(47,20180620164811,1,'2020-01-01 01:01:01'),(48,20180620175054,1,'2020-01-01 01:01:01'),(49,20180620175055,1,'2020-01-01 01:01:01'),(50,20191010101639,1,'2020-01-01 01:01:01'),(51,20191010155147,1,'2020-01-01 01:01:01'),(52,20191220130734,1,'2020-01-01 01:01:01'),(53,20200311140000,1,'2020-01-01 01:01:01'),(54,20200405120000,1,'2020-01-01 01:01:01'),(55,20200407120000,1,'2020-01-01 01:01:01'),(56,20200420120000,1,'2020-01-01 01:01:01'),(57,20200504120000,1,'2020-01-01 01:01:01'),(58,20200512120000,1,'2020-01-01 01:01:01'),(59,20200707120000,1,'2020-01-01 01:01:01'),(60,20201011162341,1,'2020-01-01 01:01:01'),(61,20201021104586,1,'2020-01-01 01:01:01'),(62,20201102112520,1,'2020-01-01 01:01:01'),(63,20201208121729,1,'2020-01-01 01:01:01'),(64,20201215091637,1,'2020-01-01 01:01:01'),(65,20210119174155,1,'2020-01-01 01:01:01'),(66,20210326182902,1,'2020-01-01 01:01:01'),(67,20210421112652,1,'2020-01-01 01:01:01'),(68,20210506095025,1,'2020-01-01 01:01:01'),(69,20210513115729,1,'2020-01-01 01:01:01'),(70,20210526113559,1,'2020-01-01 01:01:01'),(71,20210601000001,1,'2020-01-01 01:01:01'),(72,20210601000002,1,'2020-01-01 01:01:01'),(73,20210601000003,1,'2020-01-01 01:01:01'),(74,20210601000004,1,'2020-01-01 01:01:01'),(75,20210601000005,1,'2020-01-01 01:01:01'),(76,20210601000006,1,'2020-01-01 01:01:01'),(77,20210601000007,1,'2020-01-01 01:01:01'),(78,20210601000008,1,'2020-01-01 01:01:01'),(79,20210606151329,1,'2020-01-01 01:01:01'),(80,20210616163757,1,'2020-01-01 01:01:01'),(81,20210617174723,1,'2020-01-01 01:01:01'),(82,20210622160235,1,'2020-01-01 01:01:01'),(83,20210623100031,1,'2020-01-01 01:01:01'),(84,20210623133615,1,'2020-01-01 01:01:01'),(85,20210708143152,1,'2020-01-01 01:01:01'),(86,20210709124443,1,'2020-01-01 01:01:01'),(87,20210712155608,1,'2020-01-01 01:01:01'),(88,20210714102108,1,'2020-01-01 01:01:01'),(89,20210719153709,1,'2020-01-01 01:01:01'),(90,20210721171531,1,'2020-01-01 01:01:01'),(91,20210723135713,1,'2020-01-01 01:01:01'),(92,20210802135933,1,'2020-01-01 01:01:01'),(93,20210806112844,1,'2020-01-01 01:01:01'),(94,20210810095603,1,'2020-01-01 01:01:01'),(95,20210811150223,1,'2020-01-01 01:01:01'),(96,20210818151827,1,'2020-01-01 01:01:01'),(97,20210818151828,1,'2020-01-01 01:01:01'),(98,20210818182258,1,'2020-01-01 01:01:01'),(99,20210819131107,1,'2020-01-01 01:01:01'),(100,20210819143446,1,'2020-01-01 01:01:01'),(101,20210903132338,1,'2020-01-01 01:01:01'),(102,20210915144307,1,'2020-01-01 01:01:01'),(103,20210920155130,1,'2020-01-01 01:01:01'),(104,20210927143115,1,'2020-01-01 01:01:01'),(105,20210927143116,1,'2020-01-01 01:01:01'),(106,20211013133706,1,'2020-01-01 01:01:01'),(107,20211013133707,1,'2020-01-01 01:01:01'),(108,20211102135149,1,'2020-01-01 01:01:01'),(109,20211109121546,1,'2020-01-01 01:01:01'),(110,20211110163320,1,'2020-01-01 01:01:01'),(111,20211116184029,1,'2020-01-01 01:01:01'),(112,20211116184030,1,'2020-01-01 01:01:01'),(113,20211202092042,1,'2020-01-01 01:01:01'),(114,20211202181033,1,'2020-01-01 01:01:01'),(115,20211207161856,1,'2020-01-01 01:01:01'),(116,20211216131203,1,'2020-01-01 01:01:01'),(117,20211221110132,1,'2020-01-01 01:01:01'),(118,20220107155700,1,'2020-01-01 01:01:01'),(119,20220125105650,1,'2020-01-01 01:01:01'),(120,20220201084510,1,'2020-01-01 01:01:01'),(121,20220208144830,1,'2020-01-01 01:01:01'),(122,20220208144831,1,'2020-01-01 01:01:01'),(123,20220215152203,1,'2020-01-01 01:01:01'),(124,20220223113157,1,'2020-01-01 01:01:01'),(125,20220307104655,1,'2020-01-01 01:01:01'),(126,20220309133956,1,'2020-01-01 01:01:01'),(127,20220316155700,1,'2020-01-01 01:01:01'),(128,20220323152301,1,'2020-01-01 01:01:01'),(129,20220330100659,1,'2020-01-01 01:01:01'),(130,20220404091216,1,'2020-01-01 01:01:01'),(131,20220419140750,1,'2020-01-01 01:01:01'),(132,20220428140039,1,'2020-01-01 01:01:01'),(133,20220503134048,1,'2020-01-01 01:01:01'),(134,20220524102918,1,'2020-01-01 01:01:01'),(135,20220526123327,1,'2020-01-01 01:01:01'),(136,20220526123328,1,'2020-01-01 01:01:01'),(137,20220526123329,1,'2020-01-01 01:01:01'),(138,20220608113128,1,'2020-01-01 01:01:01'),(139,20220627104817,1,'2020-01-01 01:01:01'),(140,20220704101843,1,'2020-01-01 01:01:01'),(141,20220708095046,1,'2020-01-01 01:01:01'),(142,20220713091130,1,'2020-01-01 01:01:01'),(143,20220802135510,1,'2020-01-01 01:01:01'),(144,20220818101352,1,'2020-01-01 01:01:01'),(145,20220822161445,1,'2020-01-01 01:01:01'),(146,20220831100036,1,'2020-01-01 01:01:01'),(147,20220831100151,1,'2020-01-01 01:01:01'),(148,20220908181826,1,'2020-01-01 01:01:01'),(149,20220914154915,1,'2020-01-01 01:01:01'),(150,20220915165115,1,'2020-01-01 01:01:01'),(151,20220915165116,1,'2020-01-01 01:01:01'),(152,20220928100158,1,'2020-01-01 01:01:01'),(153,20221014084130,1,'2020-01-01 01:01:01'),(154,20221027085019,1,'2020-01-01 01:01:01'),(155,20221101103952,1,'2020-01-01 01:01:01'),(156,20221104144401,1,'2020-01-01 01:01:01'),(157,20221109100749,1,'2020-01-01 01:01:01'),(158,20221115104546,1,'2020-01-01 01:01:01'),(159,20221130114928,1,'2020-01-01 01:01:01'),(160,20221205112142,1,'2020-01-01 01:01:01'),(161,20221216115820,1,'2020-01-01 01:01:01'),(162,20221220195934,1,'2020-01-01 01:01:01'),(163,20221220195935,1,'2020-01-01 01:01:01'),(164,20221223174807,1,'2020-01-01 01:01:01'),(165,20221227163855,1,'2020-01-01 01:01:01'),(166,20221227163856,1,'2020-01-01 01:01:01'),(167,20230202224725,1,'2020-01-01 01:01:01'),(168,20230206163608,1,'2020-01-01 01:01:01'),(169,20230214131519,1,'2020-01-01 01:01:01'),(170,20230303135738,1,'2020-01-01 01:01:01'),(171,20230313135301,1,'2020-01-01 01:01:01'),(172,20230313141819,1,'2020-01-01 01:01:01'),(173,20230315104937,1,'2020-01-01 01:01:01'),(174,20230317173844,1,'2020-01-01 01:01:01'),(175,20230320133602,1,'2020-01-01 01:01:01'),(176,20230330100011,1,'2020-01-01 01:01:01'),(177,20230330134823,1,'2020-01-01 01:01:01'),(178,20230405232025,1,'2020-01-01 01:01:01'),(179,20230408084104,1,'2020-01-01 01:01:01'),(180,20230411102858,1,'2020-01-01 01:01:01'),(181,20230421155932,1,'2020-01-01 01:01:01'),(182,20230425082126,1,'2020-01-01 01:01:01'),(183,20230425105727,1,'2020-01-01 01:01:01'),(184,20230501154913,1,'2020-01-01 01:01:01'),(185,20230503101418,1,'2020-01-01 01:01:01'),(186,20230515144206,1,'2020-01-01 01:01:01'),(187,20230517140952,1,'2020-01-01 01:01:01'),(188,20230517152807,1,'2020-01-01 01:01:01'),(189,20230518114155,1,'2020-01-01 01:01:01'),(190,20230520153236,1,'2020-01-01 01:01:01'),(191,20230525151159,1,'2020-01-01 01:01:01'),(192,20230530122103,1,'2020-01-01 01:01:01'),(193,20230602111827,1,'2020-01-01 01:01:01'),(194,20230608103123,1,'2020-01-01 01:01:01'),(195,20230629140529,1,'2020-01-01 01:01:01'),(196,20230629140530,1,'2020-01-01 01:01:01'),(197,20230711144622,1,'2020-01-01 01:01:01'),(198,20230721135421,1,'2020-01-01 01:01:01'),(199,20230721161508,1,'2020-01-01 01:01:01'),(200,20230726115701,1,'2020-01-01 01:01:01'),(201,20230807100822,1,'2020-01-01 01:01:01'),(202,20230814150442,1,'2020-01-01 01:01:01'),(203,20230823122728,1,'2020-01-01 01:01:01'),(204,20230906152143,1,'2020-01-01 01:01:01'),(205,20230911163618,1,'2020-01-01 01:01:01'),(206,20230912101759,1,'2020-01-01 01:01:01'),(207,20230915101341,1,'2020-01-01 01:01:01'),(208,20230918132351,1,'2020-01-01 01:01:01'),(209,20231004144339,1,'2020-01-01 01:01:01'),(210,20231009094541,1,'2020-01-01 01:01:01'),(211,20231009094542,1,'2020-01-01 01:01:01'),(212,20231009094543,1,'2020-01-01 01:01:01'),(213,20231009094544,1,'2020-01-01 01:01:01'),(214,20231016091915,1,'2020-01-01 01:01:01'),(215,20231024174135,1,'2020-01-01 01:01:01'),(216,20231025120016,1,'2020-01-01 01:01:01'),(217,20231025160156,1,'2020-01-01 01:01:01'),(218,20231031165350,1,'2020-01-01 01:01:01'),(219,20231106144110,1,'2020-01-01 01:01:01'),(220,20231107130934,1,'2020-01-01 01:01:01'),(221,20231109115838,1,'2020-01-01 01:01:01'),(222,20231121054530,1,'2020-01-01 01:01:01'),(223,20231122101320,1,'2020-01-01 01:01:01'),(224,20231130132828,1,'2020-01-01 01:01:01'),(225,20231130132931,1,'2020-01-01 01:01:01'),(226,20231204155427,1,'2020-01-01 01:01:01'),(227,20231206142340,1,'2020-01-01 01:01:01'),(228,20231207102320,1,'2020-01-01 01:01:01'),(229,20231207102321,1,'2020-01-01 01:01:01'),(230,20231207133731,1,'2020-01-01 01:01:01'),(231,20231212094238,1,'2020-01-01 01:01:01'),(232,20231212095734,1,'2020-01-01 01:01:01'),(233,20231212161121,1,'2020-01-01 01:01:01'),(234,20231215122713,1,'2020-01-01 01:01:01'),(235,20231219143041,1,'2020-01-01 01:01:01'),(236,20231224070653,1,'2020-01-01 01:01:01'),(237,20240110134315,1,'2020-01-01 01:01:01'),(238,20240119091637,1,'2020-01-01 01:01:01'),(239,20240126020642,1,'2020-01-01 01:01:01'),(240,20240126020643,1,'2020-01-01 01:01:01'),(241,20240129162819,1,'2020-01-01 01:01:01'),(242,20240130115133,1,'2020-01-01 01:01:01'),(243,20240131083822,1,'2020-01-01 01:01:01'),(244,20240205095928,1,'2020-01-01 01:01:01'),(245,20240205121956,1,'2020-01-01 01:01:01'),(246,20240209110212,1,'2020-01-01 01:01:01'),(247,20240212111533,1,'2020-01-01 01:01:01'),(248,20240221112844,1,'2020-01-01 01:01:01'),(249,20240222073518,1,'2020-01-01 01:01:01'),(250,20240222135115,1,'2020-01-01 01:01:01'),(251,20240226082255,1,'2020-01-01 01:01:01'),(252,20240228082706,1,'2020-01-01 01:01:01'),(253,20240301173035,1,'2020-01-01 01:01:01'),(254,20240302111134,1,'2020-01-01 01:01:01'),(255,20240312103753,1,'2020-01-01 01:01:01'),(256,20240313143416,1,'2020-01-01 01:01:01'),(257,20240314085226,1,'2020-01-01 01:01:01'),(258,20240314151747,1,'2020-01-01 01:01:01'),(259,20240320145650,1,'2020-01-01 01:01:01'),(260,20240327115530,1,'2020-01-01 01:01:01'),(261,20240327115617,1,'2020-01-01 01:01:01'),(262,20240408085837,1,'2020-01-01 01:01:01'),(263,20240415104633,1,'2020-01-01 01:01:01'),(264,20240430111727,1,'2020-01-01 01:01:01'),(265,20240515200020,1,'2020-01-01 01:01:01'),(266,20240521143023,1,'2020-01-01 01:01:01'),(267,20240521143024,1,'2020-01-01 01:01:01'),(268,20240601174138,1,'2020-01-01 01:01:01'),(269,20240607133721,1,'2020-01-01 01:01:01'),(270,20240612150059,1,'2020-01-01 01:01:01'),(271,20240613162201,1,'2020-01-01 01:01:01'),(272,20240613172616,1,'2020-01-01 01:01:01'),(273,20240618142419,1,'2020-01-01 01:01:01'),(274,20240625093543,1,'2020-01-01 01:01:01'),(275,20240626195531,1,'2020-01-01 01:01:01'),(276,20240702123921,1,'2020-01-01 01:01:01'),(277,20240703154849,1,'2020-01-01 01:01:01'),(278,20240707134035,1,'2020-01-01 01:01:01'),(279,20240707134036,1,'2020-01-01 01:01:01'),(280,20240709124958,1,'2020-01-01 01:01:01'),(281,20240709132642,1,'2020-01-01 01:01:01'),(282,20240709183940,1,'2020-01-01 01:01:01'),(283,20240710155623,1,'2020-01-01 01:01:01'),(284,20240723102712,1,'2020-01-01 01:01:01'),(285,20240725152735,1,'2020-01-01 01:01:01'),(286,20240725182118,1,'2020-01-01 01:01:01'),(287,20240726100517,1,'2020-01-01 01:01:01'),(288,20240730171504,1,'2020-01-01 01:01:01'),(289,20240730174056,1,'2020-01-01 01:01:01'),(290,20240730215453,1,'2020-01-01 01:01:01'),(291,20240730374423,1,'2020-01-01 01:01:01'),(292,20240801115359,1,'2020-01-01 01:01:01'),(293,20240802101043,1,'2020-01-01 01:01:01'),(294,20240802113716,1,'2020-01-01 01:01:01'),(295,20240814135330,1,'2020-01-01 01:01:01'),(296,20240815000000,1,'2020-01-01 01:01:01'),(297,20240815000001,1,'2020-01-01 01:01:01'),(298,20240816103247,1,'2020-01-01 01:01:01'),(299,20240820091218,1,'2020-01-01 01:01:01'),(300,20240826111228,1,'2020-01-01 01:01:01'),(301,20240826160025,1,'2020-01-01 01:01:01'),(302,20240829165448,1,'2020-01-01 01:01:01'),(303,20240829165605,1,'2020-01-01 01:01:01'),(304,20240829165715,1,'2020-01-01 01:01:01'),(305,20240829165930,1,'2020-01-01 01:01:01'),(306,20240829170023,1,'2020-01-01 01:01:01'),(307,20240829170033,1,'2020-01-01 01:01:01'),(308,20240829170044,1,'2020-01-01 01:01:01'),(309,20240905105135,1,'2020-01-01 01:01:01'),(310,20240905140514,1,'2020-01-01 01:01:01'),(311,20240905200000,1,'2020-01-01 01:01:01'),(312,20240905200001,1,'2020-01-01 01:01:01'),(313,20241002104104,1,'2020-01-01 01:01:01'),(314,20241002104105,1,'2020-01-01 01:01:01'),(315,20241002104106,1,'2020-01-01 01:01:01'),(316,20241002210000,1,'2020-01-01 01:01:01'),(317,20241003145349,1,'2020-01-01 01:01:01'),(318,20241004005000,1,'2020-01-01 01:01:01'),(319,20241008083925,1,'2020-01-01 01:01:01'),(320,20241009090010,1,'2020-01-01 01:01:01'),(321,20241017163402,1,'2020-01-01 01:01:01'),(322,20241021224359,1,'2020-01-01 01:01:01'),(323,20241022140321,1,'2020-01-01 01:01:01'),(324,20241025111236,1,'2020-01-01 01:01:01'),(325,20241025112748,1,'2020-01-01 01:01:01'),(326,20241025141855,1,'2020-01-01 01:01:01'),(327,20241110152839,1,'2020-01-01 01:01:01'),(328,20241110152840,1,'2020-01-01 01:01:01'),(329,20241110152841,1,'2020-01-01 01:01:01'),(330,20241116233322,1,'2020-01-01 01:01:01'),(331,20241122171434,1,'2020-01-01 01:01:01'),(332,20241125150614,1,'2020-01-01 01:01:01'),(333,20241203125346,1,'2020-01-01 01:01:01'),(334,20241203130032,1,'2020-01-01 01:01:01'),(335,20241205122800,1,'2020-01-01 01:01:01'),(336,20241209164540,1,'2020-01-01 01:01:01'),(337,20241210140021,1,'2020-01-01 01:01:01'),(338,20241219180042,1,'2020-01-01 01:01:01'),(339,20241220100000,1,'2020-01-01 01:01:01'),(340,20241220114903,1,'2020-01-01 01:01:01'),(341,20241220114904,1,'2020-01-01 01:01:01'),(342,20241224000000,1,'2020-01-01 01:01:01'),(343,20241230000000,1,'2020-01-01 01:01:01'),(344,20241231112624,1,'2020-01-01 01:01:01'),(345,20250102121439,1,'2020-01-01 01:01:01'),(346,20250121094045,1,'2020-01-01 01:01:01'),(347,20250121094500,1,'2020-01-01 01:01:01'),(348,20250121094600,1,'2020-01-01 01:01:01'),(349,20250121094700,1,'2020-01-01 01:01:01'),(350,20250124194347,1,'2020-01-01 01:01:01'),(351,20250127162751,1,'2020-01-01 01:01:01'),(352,20250213104005,1,'2020-01-01 01:01:01'),(353,20250214205657,1,'2020-01-01 01:01:01'),(354,20250217093329,1,'2020-01-01 01:01:01'),(355,20250219090511,1,'2020-01-01 01:01:01'),(356,20250219100000,1,'2020-01-01 01:01:01'),(357,20250219142401,1,'2020-01-01 01:01:01'),(358,20250224184002,1,'2020-01-01 01:01:01'),(359,20250225085436,1,'2020-01-01 01:01:01'),(360,20250226000000,1,'2020-01-01 01:01:01'),(361,20250226153445,1,'2020-01-01 01:01:01'),(362,20250304162702,1,'2020-01-01 01:01:01'),(363,20250306144233,1,'2020-01-01 01:01:01'),(364,20250313163430,1,'2020-01-01 01:01:01'),(365,20250317130944,1,'2020-01-01 01:01:01'),(366,20250318165922,1,'2020-01-01 01:01:01'),(367,20250320132525,1,'2020-01-01 01:01:01'),(368,20250320200000,1,'2020-01-01 01:01:01'),(369,20250326161930,1,'2020-01-01 01:01:01'),(370,20250326161931,1,'2020-01-01 01:01:01'),(371,20250331042354,1,'2020-01-01 01:01:01'),(372,20250331154206,1,'2020-01-01 01:01:01'),(373,20250401155831,1,'2020-01-01 01:01:01'),(374,20250408133233,1,'2020-01-01 01:01:01'),(375,20250410104321,1,'2020-01-01 01:01:01'),(376,20250421085116,1,'2020-01-01 01:01:01'),(377,20250422095806,1,'2020-01-01 01:01:01'),(378,20250424153059,1,'2020-01-01 01:01:01'),(379,20250430103833,1,'2020-01-01 01:01:01'),(380,20250430112622,1,'2020-01-01 01:01:01'),(381,20250501162727,1,'2020-01-01 01:01:01'),(382,20250502154517,1,'2020-01-01 01:01:01'),(383,20250502222222,1,'2020-01-01 01:01:01'),(384,20250507170845,1,'2020-01-01 01:01:01'),(385,20250513162912,1,'2020-01-01 01:01:01'),(386,20250519161614,1,'2020-01-01 01:01:01'),(387,20250519170000,1,'2020-01-01 01:01:01'),(388,20250520153848,1,'2020-01-01 01:01:01'),(389,20250528115932,1,'2020-01-01 01:01:01'),(390,20250529102706,1,'2020-01-01 01:01:01'),(391,20250603105558,1,'2020-01-01 01:01:01'),(392,20250609102714,1,'2020-01-01 01:01:01'),(393,20250609112613,1,'2020-01-01 01:01:01'),(394,20250613103810,1,'2020-01-01 01:01:01'),(395,20250616193950,1,'2020-01-01 01:01:01'),(396,20250624140757,1,'2020-01-01 01:01:01'),(397,20250626130239,1,'2020-01-01 01:01:01'),(398,20250629131032,1,'2020-01-01 01:01:01'),(399,20250701155654,1,'2020-01-01 01:01:01'),(400,20250707095725,1,'2020-01-01 01:01:01'),(401,20250716152435,1,'2020-01-01 01:01:01'),(402,20250718091828,1,'2020-01-01 01:01:01'),(403,20250728122229,1,'2020-01-01 01:01:01'),(404,20250731122715,1,'2020-01-01 01:01:01'),(405,20250731151000,1,'2020-01-01 01:01:01'),(406,20250803000000,1,'2020-01-01 01:01:01'),(407,20250805083116,1,'2020-01-01 01:01:01'),(408,20250807140441,1,'2020-01-01 01:01:01'),(409,20250808000000,1,'2020-01-01 01:01:01'),(410,20250811155036,1,'2020-01-01 01:01:01'),(411,20250813205039,1,'2020-01-01 01:01:01'),(412,20250814123333,1,'2020-01-01 01:01:01'),(413,20250815130115,1,'2020-01-01 01:01:01'),(414,20250816115553,1,'2020-01-01 01:01:01'),(415,20250817154557,1,'2020-01-01 01:01:01'),(416,20250825113751,1,'2020-01-01 01:01:01'),(417,20250827113140,1,'2020-01-01 01:01:01'),(418,20250828120836,1,'2020-01-01 01:01:01'),(419,20250902112642,1,'2020-01-01 01:01:01'),(420,20250904091745,1,'2020-01-01 01:01:01'),(421,20250905090000,1,'2020-01-01 01:01:01'),(422,20250922083056,1,'2020-01-01 01:01:01'),(423,20250923120000,1,'2020-01-01 01:01:01'),(424,20250926123048,1,'2020-01-01 01:01:01'),(425,20251015103505,1,'2020-01-01 01:01:01'),(426,20251015103600,1,'2020-01-01 01:01:01'),(427,20251015103700,1,'2020-01-01 01:01:01'),(428,20251015103800,1,'2020-01-01 01:01:01'),(429,20251015103900,1,'2020-01-01 01:01:01'),(430,20251028140000,1,'2020-01-01 01:01:01'),(431,20251028140100,1,'2020-01-01 01:01:01'),(432,20251028140110,1,'2020-01-01 01:01:01'),(433,20251028140200,1,'2020-01-01 01:01:01'),(434,20251028140300,1,'2020-01-01 01:01:01'),(435,20251028140400,1,'2020-01-01 01:01:01'),(436,20251031154558,1,'2020-01-01 01:01:01'),(437,20251103160848,1,'2020-01-01 01:01:01'),(438,20251104112849,1,'2020-01-01 01:01:01'),(439,20251106000000,1,'2020-01-01 01:01:01'),(440,20251107164629,1,'2020-01-01 01:01:01'),(441,20251107170854,1,'2020-01-01 01:01:01'),(442,20251110172137,1,'2020-01-01 01:01:01'),(443,20251111153133,1,'2020-01-01 01:01:01'),(444,20251117020000,1,'2020-01-01 01:01:01'),(445,20251117020100,1,'2020-01-01 01:01:01'),(446,20251117020200,1,'2020-01-01 01:01:01'),(447,20251121100000,1,'2020-01-01 01:01:01'),(448,20251121124239,1,'2020-01-01 01:01:01'),(449,20251124090450,1,'2020-01-01 01:01:01'),(450,20251124135808,1,'2020-01-01 01:01:01'),(451,20251124140138,1,'2020-01-01 01:01:01'),(452,20251124162948,1,'2020-01-01 01:01:01'),(453,20251127113559,1,'2020-01-01 01:01:01'),(454,20251202162232,1,'2020-01-01 01:01:01'),(455,20251203170808,1,'2020-01-01 01:01:01'),(456,20251207050413,1,'2020-01-01 01:01:01'),(457,20251208215800,1,'2020-01-01 01:01:01'),(458,20251209221730,1,'2020-01-01 01:01:01'),(459,20251209221850,1,'2020-01-01 01:01:01'),(460,20251215163721,1,'2020-01-01 01:01:01'),(461,20251217000000,1,'2020-01-01 01:01:01'),(462,20251217120000,1,'2020-01-01 01:01:01'),(463,20251229000000,1,'2020-01-01 01:01:01'),(464,20251229000010,1,'2020-01-01 01:01:01'),(465,20251229000020,1,'2020-01-01 01:01:01'),(466,20260106000000,1,'2020-01-01 01:01:01'),(467,20260108200708,1,'2020-01-01 01:01:01'),(468,20260108214732,1,'2020-01-01 01:01:01'),(469,20260109231821,1,'2020-01-01 01:01:01'),(470,20260113012054,1,'2020-01-01 01:01:01'),(471,20260124200020,1,'2020-01-01 01:01:01'),(472,20260126150840,1,'2020-01-01 01:01:01'),(473,20260126210724,1,'2020-01-01 01:01:01'),(474,20260202151756,1,'2020-01-01 01:01:01'),(475,20260205184907,1,'2020-01-01 01:01:01'),(476,20260210151544,1,'2020-01-01 01:01:01'),(477,20260210155109,1,'2020-01-01 01:01:01'),(478,20260210181120,1,'2020-01-01 01:01:01'),(479,20260211200153,1,'2020-01-01 01:01:01'),(480,20260217141240,1,'2020-01-01 01:01:01'),(481,20260217200906,1,'2020-01-01 01:01:01'),(482,20260218175704,1,'2020-01-01 01:01:01'),(483,20260314120000,1,'2020-01-01 01:01:01'),(484,20260316120000,1,'2020-01-01 01:01:01'),(485,20260316120001,1,'2020-01-01 01:01:01'),(486,20260316120002,1,'2020-01-01 01:01:01'),(487,20260316120003,1,'2020-01-01 01:01:01'),(488,20260316120004,1,'2020-01-01 01:01:01'),(489,20260316120005,1,'2020-01-01 01:01:01'),(490,20260316120006,1,'2020-01-01 01:01:01'),(491,20260316120007,1,'2020-01-01 01:01:01'),(492,20260316120008,1,'2020-01-01 01:01:01'),(493,20260316120009,1,'2020-01-01 01:01:01'),(494,20260316120010,1,'2020-01-01 01:01:01'),(495,20260316120011,1,'2020-01-01 01:01:01'),(496,20260317120000,1,'2020-01-01 01:01:01'),(497,20260318184559,1,'2020-01-01 01:01:01'),(498,20260319120000,1,'2020-01-01 01:01:01'),(499,20260323144117,1,'2020-01-01 01:01:01'),(500,20260324161944,1,'2020-01-01 01:01:01'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( @@ -2739,6 +2739,7 @@ CREATE TABLE `software_installers` ( `install_during_setup` tinyint(1) NOT NULL DEFAULT '0', `upgrade_code` varchar(48) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '', `is_active` tinyint(1) NOT NULL DEFAULT '0', + `patch_query` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_software_installers_team_title_version` (`global_or_team_id`,`title_id`,`version`), KEY `fk_software_installers_title` (`title_id`), diff --git a/server/datastore/mysql/software_installers.go b/server/datastore/mysql/software_installers.go index 507f1f153a..b8c5990f99 100644 --- a/server/datastore/mysql/software_installers.go +++ b/server/datastore/mysql/software_installers.go @@ -323,8 +323,9 @@ INSERT INTO software_installers ( fleet_maintained_app_id, url, upgrade_code, - is_active -) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?, ?, ?)` + is_active, + patch_query +) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?, ?, ?, ?)` args := []interface{}{ tid, @@ -348,6 +349,7 @@ INSERT INTO software_installers ( payload.URL, payload.UpgradeCode, true, + payload.PatchQuery, } res, err := tx.ExecContext(ctx, stmt, args...) @@ -1009,7 +1011,8 @@ SELECT si.self_service, si.url, COALESCE(st.name, '') AS software_title, - COALESCE(st.bundle_identifier, '') AS bundle_identifier + COALESCE(st.bundle_identifier, '') AS bundle_identifier, + si.patch_query %s FROM software_installers si @@ -2303,10 +2306,11 @@ INSERT INTO software_installers ( package_ids, install_during_setup, fleet_maintained_app_id, - is_active + is_active, + patch_query ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?, COALESCE(?, false), ?, ? + (SELECT name FROM users WHERE id = ?), (SELECT email FROM users WHERE id = ?), ?, ?, COALESCE(?, false), ?, ?, ? ) ON DUPLICATE KEY UPDATE install_script_content_id = VALUES(install_script_content_id), @@ -2325,7 +2329,8 @@ ON DUPLICATE KEY UPDATE user_email = VALUES(user_email), url = VALUES(url), install_during_setup = COALESCE(?, install_during_setup), - is_active = VALUES(is_active) + is_active = VALUES(is_active), + patch_query = VALUES(patch_query) ` const updateInstaller = ` @@ -2337,7 +2342,8 @@ SET install_script_content_id = ?, uninstall_script_content_id = ?, post_install_script_content_id = ?, - pre_install_query = ? + pre_install_query = ?, + patch_query = ? WHERE id = ? ` @@ -2759,6 +2765,7 @@ WHERE installer.InstallDuringSetup, installer.FleetMaintainedAppID, isActive, + installer.PatchQuery, installer.InstallDuringSetup, } // For FMA installers, skip the insert if this exact version is already cached @@ -2788,6 +2795,7 @@ WHERE uninstallScriptID, postInstallScriptID, installer.PreInstallQuery, + installer.PatchQuery, existingID, } if _, err := tx.ExecContext(ctx, updateInstaller, args...); err != nil { diff --git a/server/datastore/mysql/software_installers_test.go b/server/datastore/mysql/software_installers_test.go index 910f3ceb96..fe72afceec 100644 --- a/server/datastore/mysql/software_installers_test.go +++ b/server/datastore/mysql/software_installers_test.go @@ -3458,9 +3458,9 @@ func testGetTeamsWithInstallerByHash(t *testing.T, ds *Datastore) { _, err := q.ExecContext(ctx, ` INSERT INTO software_installers (team_id, global_or_team_id, storage_id, filename, extension, version, platform, title_id, - install_script_content_id, uninstall_script_content_id, is_active, url, package_ids) + install_script_content_id, uninstall_script_content_id, is_active, url, package_ids, patch_query) SELECT team_id, global_or_team_id, storage_id, filename, extension, 'old_version', platform, title_id, - install_script_content_id, uninstall_script_content_id, 0, url, package_ids + install_script_content_id, uninstall_script_content_id, 0, url, package_ids, patch_query FROM software_installers WHERE id = ? `, installer1NoTeam) return err diff --git a/server/datastore/mysql/software_test.go b/server/datastore/mysql/software_test.go index 8a70c8f338..7806dab3ed 100644 --- a/server/datastore/mysql/software_test.go +++ b/server/datastore/mysql/software_test.go @@ -4436,13 +4436,14 @@ func testListHostSoftware(t *testing.T, ds *Datastore) { platform, self_service, package_ids, - is_active + is_active, + patch_query ) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE)`, + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE, ?)`, teamID, globalOrTeamID, titleID, fmt.Sprintf("installer-%d.pkg", i), "pkg", fmt.Sprintf("v%d.0.0", i), scriptContentID, uninstallScriptContentID, - []byte("test"), "darwin", i < 2, "[]") + []byte("test"), "darwin", i < 2, "[]", "") if err != nil { return err } @@ -5326,13 +5327,14 @@ func testListHostSoftware(t *testing.T, ds *Datastore) { platform, self_service, package_ids, - is_active + is_active, + patch_query ) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE)`, + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE, ?)`, darwinHost.TeamID, 0, softwareAlreadyInstalled.TitleID, "DummyApp.pkg", "pkg", "2.0.0", scriptContentID, uninstallScriptContentID, - []byte("test"), "darwin", true, "[]") + []byte("test"), "darwin", true, "[]", "") if err != nil { return err } @@ -5463,13 +5465,14 @@ func testListLinuxHostSoftware(t *testing.T, ds *Datastore) { platform, self_service, package_ids, - is_active + is_active, + patch_query ) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE)`, + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE, ?)`, nil, 0, titleID, installer.Filename, installer.Extension, "2.0.0", scriptContentID, scriptContentID, - []byte("test"), "linux", true, "[]") + []byte("test"), "linux", true, "[]", "") require.NoError(t, err) } @@ -6367,10 +6370,10 @@ func testSetHostSoftwareInstallResult(t *testing.T, ds *Datastore) { res, err = q.ExecContext(ctx, ` INSERT INTO software_installers - (title_id, filename, extension, version, install_script_content_id, uninstall_script_content_id, storage_id, platform, package_ids) + (title_id, filename, extension, version, install_script_content_id, uninstall_script_content_id, storage_id, platform, package_ids, patch_query) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?)`, - titleID, "installer.pkg", "pkg", "v1.0.0", scriptContentID, uninstallScriptContentID, []byte("test"), "darwin", "[]") + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + titleID, "installer.pkg", "pkg", "v1.0.0", scriptContentID, uninstallScriptContentID, []byte("test"), "darwin", "[]", "") if err != nil { return err } @@ -11806,8 +11809,8 @@ func testListHostSoftwarePaginationWithMultipleInstallers(t *testing.T, ds *Data if _, err := q.ExecContext(ctx, ` INSERT INTO software_installers (team_id, global_or_team_id, title_id, filename, extension, version, - install_script_content_id, uninstall_script_content_id, storage_id, platform, self_service, package_ids) - VALUES (NULL, 0, ?, ?, 'pkg', ?, ?, ?, ?, 'darwin', 0, '[]')`, + install_script_content_id, uninstall_script_content_id, storage_id, platform, self_service, package_ids, patch_query) + VALUES (NULL, 0, ?, ?, 'pkg', ?, ?, ?, ?, 'darwin', 0, '[]', '')`, titleID, fmt.Sprintf("installer-%s.pkg", version), version, installScriptID, uninstallScriptID, fmt.Appendf(nil, "storage-%s", version), ); err != nil { diff --git a/server/datastore/mysql/software_titles.go b/server/datastore/mysql/software_titles.go index 9f88d2fd31..25b4ada8ba 100644 --- a/server/datastore/mysql/software_titles.go +++ b/server/datastore/mysql/software_titles.go @@ -1036,7 +1036,8 @@ func (ds *Datastore) GetCachedFMAInstallerMetadata(ctx context.Context, teamID * COALESCE(isc.contents, '') AS install_script, COALESCE(usc.contents, '') AS uninstall_script, COALESCE(si.pre_install_query, '') AS pre_install_query, - si.upgrade_code + si.upgrade_code, + si.patch_query FROM software_installers si LEFT JOIN script_contents isc ON isc.id = si.install_script_content_id LEFT JOIN script_contents usc ON usc.id = si.uninstall_script_content_id diff --git a/server/datastore/mysql/statistics_test.go b/server/datastore/mysql/statistics_test.go index 6c831def96..c868a4c97b 100644 --- a/server/datastore/mysql/statistics_test.go +++ b/server/datastore/mysql/statistics_test.go @@ -686,9 +686,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "zoom.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage1", "[]", appDarwin1.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "zoom.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage1", "[]", appDarwin1.ID, "") return err }) ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { @@ -696,9 +696,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "slack.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage2", "[]", appDarwin2.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "slack.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage2", "[]", appDarwin2.ID, "") return err }) ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { @@ -706,9 +706,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "teams.exe", "1.0", "windows", installScriptID, uninstallScriptID, "storage3", "[]", appWindows1.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "teams.exe", "1.0", "windows", installScriptID, uninstallScriptID, "storage3", "[]", appWindows1.ID, "") return err }) ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { @@ -716,9 +716,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "zoom.exe", "1.0", "windows", installScriptID, uninstallScriptID, "storage4", "[]", appWindows2.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "zoom.exe", "1.0", "windows", installScriptID, uninstallScriptID, "storage4", "[]", appWindows2.ID, "") return err }) ExecAdhocSQL(t, ds, func(q sqlx.ExtContext) error { @@ -726,9 +726,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "linux.deb", "1.0", "linux", installScriptID, uninstallScriptID, "storage5", "[]", appLinux.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "linux.deb", "1.0", "linux", installScriptID, uninstallScriptID, "storage5", "[]", appLinux.ID, "") return err }) @@ -744,9 +744,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "zoom-v2.pkg", "2.0", "darwin", installScriptID, uninstallScriptID, "storage6", "[]", appDarwin1.ID) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "zoom-v2.pkg", "2.0", "darwin", installScriptID, uninstallScriptID, "storage6", "[]", appDarwin1.ID, "") return err }) macOSApps, windowsApps, err = fleetMaintainedAppsInUseDB(ctx, ds.reader(ctx)) @@ -760,9 +760,9 @@ func testFleetMaintainedAppsInUse(t *testing.T, ds *Datastore) { INSERT INTO software_installers ( team_id, global_or_team_id, filename, version, platform, install_script_content_id, uninstall_script_content_id, - storage_id, package_ids, fleet_maintained_app_id - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `, nil, 0, "custom.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage7", "[]", nil) + storage_id, package_ids, fleet_maintained_app_id, patch_query + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `, nil, 0, "custom.pkg", "1.0", "darwin", installScriptID, uninstallScriptID, "storage7", "[]", nil, "") return err }) diff --git a/server/fleet/maintained_apps.go b/server/fleet/maintained_apps.go index e343513e65..ad9e86842f 100644 --- a/server/fleet/maintained_apps.go +++ b/server/fleet/maintained_apps.go @@ -18,6 +18,7 @@ type MaintainedApp struct { AutomaticInstallQuery string `json:"-" db:"pre_install_query"` Categories []string `json:"categories"` UpgradeCode string `json:"upgrade_code,omitempty" db:"upgrade_code"` + PatchQuery string `json:"-" db:"patch_query"` } func (s *MaintainedApp) Source() string { diff --git a/server/fleet/software_installer.go b/server/fleet/software_installer.go index f222d1e211..4a56d61a78 100644 --- a/server/fleet/software_installer.go +++ b/server/fleet/software_installer.go @@ -138,6 +138,8 @@ type SoftwareInstaller struct { // PatchPolicy is present for Fleet maintained apps with an associated patch policy PatchPolicy *PatchPolicyData `json:"patch_policy"` + // PatchQuery is the query to use for creating a patch policy + PatchQuery string `json:"-" db:"patch_query"` } // SoftwarePackageResponse is the response type used when applying software by batch. @@ -540,6 +542,7 @@ type UploadSoftwareInstallerPayload struct { // automatically created when a software installer is added to Fleet. This field should be set // after software installer creation if AutomaticInstall is true. AddedAutomaticInstallPolicy *Policy + PatchQuery string } func (p UploadSoftwareInstallerPayload) UniqueIdentifier() string { diff --git a/server/mdm/maintainedapps/sync.go b/server/mdm/maintainedapps/sync.go index a54e7b19b0..da3b08ceca 100644 --- a/server/mdm/maintainedapps/sync.go +++ b/server/mdm/maintainedapps/sync.go @@ -159,6 +159,7 @@ func Hydrate(ctx context.Context, app *fleet.MaintainedApp, version string, team app.AutomaticInstallQuery = cached.AutomaticInstallQuery app.Categories = cached.Categories app.UpgradeCode = cached.UpgradeCode + app.PatchQuery = cached.PatchQuery return app, nil } @@ -211,6 +212,7 @@ func Hydrate(ctx context.Context, app *fleet.MaintainedApp, version string, team app.AutomaticInstallQuery = manifest.Versions[0].Queries.Exists app.Categories = manifest.Versions[0].DefaultCategories app.UpgradeCode = manifest.Versions[0].UpgradeCode + app.PatchQuery = manifest.Versions[0].Queries.Patch return app, nil } diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 553508cfbb..e80231abce 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -14557,9 +14557,9 @@ func (s *integrationEnterpriseTestSuite) TestSoftwareInstallerNewInstallRequestP _, err = q.ExecContext(ctx, ` INSERT INTO software_installers - (title_id, filename, extension, version, platform, install_script_content_id, uninstall_script_content_id, storage_id, team_id, global_or_team_id, pre_install_query, package_ids, is_active) + (title_id, filename, extension, version, platform, install_script_content_id, uninstall_script_content_id, storage_id, team_id, global_or_team_id, pre_install_query, package_ids, is_active, patch_query) VALUES - (?, ?, ?, ?, ?, ?, ?, unhex(?), ?, ?, ?, ?, TRUE)`, + (?, ?, ?, ?, ?, ?, ?, unhex(?), ?, ?, ?, ?, TRUE, '')`, titleID, fmt.Sprintf("installer.%s", kind), kind, "v1.0.0", platform, scriptContentID, uninstallScriptContentID, hex.EncodeToString([]byte("test")), tm.ID, tm.ID, "foo", "") return err @@ -27006,6 +27006,8 @@ func (s *integrationEnterpriseTestSuite) TestPatchPolicies() { // team tests and no team tests team, err := s.ds.NewTeam(ctx, &fleet.Team{Name: "Team 1" + t.Name()}) require.NoError(t, err) + team2, err := s.ds.NewTeam(ctx, &fleet.Team{Name: "Team 2" + t.Name()}) + require.NoError(t, err) // mock an installer being uploaded through FMA updateInstallerFMAID := func(fmaID, teamID, titleID uint) { @@ -27017,17 +27019,18 @@ func (s *integrationEnterpriseTestSuite) TestPatchPolicies() { } // resetFMAState resets an fmaTestState to the given version and installer bytes. - resetFMAState := func(state *fmaTestState, version string, installerBytes []byte) { + resetFMAState := func(state *fmaTestState, version string, installerBytes []byte, patchQuery string) { state.version = version state.installerBytes = installerBytes state.ComputeSHA(installerBytes) + state.patchQuery = patchQuery } checkPolicy := func(policy *fleet.Policy, name, version string, titleID uint) { require.NotNil(t, policy.PatchSoftware) require.Equal(t, titleID, policy.PatchSoftware.SoftwareTitleID) require.Equal(t, fleet.PolicyTypePatch, policy.Type) - require.Equal(t, fmt.Sprintf(`SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Zoom Workplace (X64)' AND version_compare(bundle_short_version, '%s') < 0);`, version), policy.Query) + require.Equal(t, fmt.Sprintf(`SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Zoom Workplace (X64)' AND version_compare(version, '%s') < 0);`, version), policy.Query) require.Equal(t, name, policy.Name) } @@ -27372,7 +27375,7 @@ func (s *integrationEnterpriseTestSuite) TestPatchPolicies() { require.Equal(t, uint(1), listPolResp.Policies[0].FailingHostCount) // Test 2: FMA Version is updated (query should use new version) - resetFMAState(states["/zoom/windows.json"], "1.2", []byte("abc")) + resetFMAState(states["/zoom/windows.json"], "1.2", []byte("abc"), "") s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{{Slug: ptr.String("zoom/windows")}}, TeamName: team.Name}, @@ -27398,7 +27401,7 @@ func (s *integrationEnterpriseTestSuite) TestPatchPolicies() { require.NoError(t, err) require.Equal(t, uint(0), listPolResp.Policies[0].FailingHostCount) - // Test 3: FMA Version is pinned back after update? (query should use old version) + // Test 3: FMA Version is pinned back after update to a newer version (query should use old version) s.DoJSON("POST", "/api/latest/fleet/software/batch", batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{ {Slug: ptr.String("zoom/windows"), SelfService: true, RollbackVersion: "1.0"}, @@ -27420,6 +27423,108 @@ func (s *integrationEnterpriseTestSuite) TestPatchPolicies() { require.Len(t, listPolResp.Policies, 1) checkPolicy(listPolResp.Policies[0], spec.Name, "1.0", title.ID) }) + + t.Run("override and empty queries behave the same", func(t *testing.T) { + // initialize FMA server + states := make(map[string]*fmaTestState, 2) + states["/zoom/windows.json"] = &fmaTestState{ + version: "1.0", + installerBytes: []byte("xyz"), + installerPath: "/zoom.msi", + } + states["/1password/darwin.json"] = &fmaTestState{ + version: "1.0", + installerBytes: []byte("xyz"), + installerPath: "/1password.pkg", + patchQuery: "SELECT 1; --custom query 1.0", + } + startFMAServers(t, s.ds, states) + + // Add installers at version 1.0 + var resp batchSetSoftwareInstallersResponse + s.DoJSON("POST", "/api/latest/fleet/software/batch", + batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{{Slug: ptr.String("zoom/windows")}, {Slug: ptr.String("1password/darwin")}}, TeamName: team2.Name}, + http.StatusAccepted, &resp, + "team_name", team2.Name, "team_id", fmt.Sprint(team2.ID), + ) + waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team2.Name, resp.RequestUUID) + + checkPolicies := func(policies []*fleet.Policy, version string) { + require.Len(t, policies, 2) + require.Equal(t, fmt.Sprintf(`SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM programs WHERE name = 'Zoom Workplace (X64)' AND version_compare(version, '%s') < 0);`, version), policies[0].Query) + require.Equal(t, fmt.Sprintf(`SELECT 1; --custom query %s`, version), policies[1].Query) + } + + specs := []*fleet.PolicySpec{ + { + Name: "team patch policy", + Query: "SELECT 1", + Team: team2.Name, + Type: fleet.PolicyTypePatch, + FleetMaintainedAppSlug: "zoom/windows", + }, + { + Name: "team patch policy 2", + Team: team2.Name, + Type: fleet.PolicyTypePatch, + FleetMaintainedAppSlug: "1password/darwin", + }, + } + + // Apply patch policies + applyResp := applyPolicySpecsResponse{} + s.DoJSON("POST", "/api/latest/fleet/spec/policies", + applyPolicySpecsRequest{Specs: specs}, + http.StatusOK, &applyResp, + ) + + listPolResp := listTeamPoliciesResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/fleets/%d/policies", team2.ID), listTeamPoliciesRequest{}, http.StatusOK, &listPolResp, "page", "0") + checkPolicies(listPolResp.Policies, "1.0") + + // Pin FMA versions to 1.2, queries should update + resetFMAState(states["/zoom/windows.json"], "1.2", []byte("abc"), "") + resetFMAState(states["/1password/darwin.json"], "1.2", []byte("abc"), "SELECT 1; --custom query 1.2") + + s.DoJSON("POST", "/api/latest/fleet/software/batch", + batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{{Slug: ptr.String("zoom/windows")}, {Slug: ptr.String("1password/darwin")}}, TeamName: team2.Name}, + http.StatusAccepted, &resp, + "team_name", team2.Name, "team_id", fmt.Sprint(team2.ID), + ) + waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team2.Name, resp.RequestUUID) + + applyResp = applyPolicySpecsResponse{} + s.DoJSON("POST", "/api/latest/fleet/spec/policies", + applyPolicySpecsRequest{Specs: specs}, + http.StatusOK, &applyResp, + ) + + listPolResp = listTeamPoliciesResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/fleets/%d/policies", team2.ID), listTeamPoliciesRequest{}, http.StatusOK, &listPolResp, "page", "0") + checkPolicies(listPolResp.Policies, "1.2") + + // Rollback FMA versions to 1.0, queries should use older versions + s.DoJSON("POST", "/api/latest/fleet/software/batch", + batchSetSoftwareInstallersRequest{Software: []*fleet.SoftwareInstallerPayload{ + {Slug: ptr.String("zoom/windows"), SelfService: true, RollbackVersion: "1.0"}, + {Slug: ptr.String("1password/darwin"), SelfService: true, RollbackVersion: "1.0"}, + }, TeamName: team2.Name}, + http.StatusAccepted, &resp, + "team_name", team2.Name, "team_id", fmt.Sprint(team2.ID), + ) + waitBatchSetSoftwareInstallersCompleted(t, &s.withServer, team2.Name, resp.RequestUUID) + + applyResp = applyPolicySpecsResponse{} + s.DoJSON("POST", "/api/latest/fleet/spec/policies", + applyPolicySpecsRequest{Specs: specs}, + http.StatusOK, &applyResp, + ) + + listPolResp = listTeamPoliciesResponse{} + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/fleets/%d/policies", team2.ID), listTeamPoliciesRequest{}, http.StatusOK, &listPolResp, "page", "0") + checkPolicies(listPolResp.Policies, "1.0") + }) + } func (s *integrationEnterpriseTestSuite) TestConditionalAccessPlatformValidation() { diff --git a/server/service/integration_mdm_test.go b/server/service/integration_mdm_test.go index f8391ab5cb..65ce68ce5d 100644 --- a/server/service/integration_mdm_test.go +++ b/server/service/integration_mdm_test.go @@ -12272,6 +12272,7 @@ type appStoreApp interface { func (s *integrationMDMTestSuite) TestBatchAssociateAppStoreApps() { t := s.T() + s.setSkipWorkerJobs(t) batchURL := "/api/latest/fleet/software/app_store_apps/batch" // non-existent team diff --git a/server/service/testing_utils.go b/server/service/testing_utils.go index 8fd0fdc928..0603154280 100644 --- a/server/service/testing_utils.go +++ b/server/service/testing_utils.go @@ -1427,6 +1427,7 @@ type fmaTestState struct { installerBytes []byte sha256 string installerPath string + patchQuery string } func (s *fmaTestState) ComputeSHA(b []byte) { @@ -1480,8 +1481,11 @@ func startFMAServers(t *testing.T, ds fleet.Datastore, states map[string]*fmaTes versions := []*ma.FMAManifestApp{ { - Version: state.version, - Queries: ma.FMAQueries{Exists: "SELECT 1 FROM osquery_info;"}, + Version: state.version, + Queries: ma.FMAQueries{ + Exists: "SELECT 1 FROM osquery_info;", + Patch: state.patchQuery, + }, InstallerURL: installerServer.URL + state.installerPath, InstallScriptRef: "foobaz", UninstallScriptRef: "foobaz",