From 5339794f9705a740cf372023cdce2cd3fca37a17 Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Tue, 8 Oct 2024 15:45:31 -0500 Subject: [PATCH] Include the policy ID and name in the "script ran" activity of a script run queued by a policy failure (#22690) #22692 # 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/Committing-Changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [x] Added/updated tests - [x] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [x] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [x] 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`). - [x] Manual QA for all new/changed functionality --- changes/22692-script-policy-id | 1 + docs/Contributing/Audit-logs.md | 6 ++- server/datastore/mysql/activities.go | 14 +++++- ...004005000_AddPolicyIdToScriptExecutions.go | 26 +++++++++++ ...5000_AddPolicyIdToScriptExecutions_test.go | 41 +++++++++++++++++ server/datastore/mysql/policies.go | 2 +- server/datastore/mysql/schema.sql | 9 ++-- server/datastore/mysql/scripts.go | 6 ++- server/fleet/activities.go | 20 ++++++--- server/fleet/policies.go | 2 + server/fleet/scripts.go | 4 ++ server/service/activities.go | 10 ++++- server/service/integration_enterprise_test.go | 45 ++++++++++++++++--- server/service/orbit.go | 9 ++++ server/service/osquery.go | 5 ++- 15 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 changes/22692-script-policy-id create mode 100644 server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions.go create mode 100644 server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions_test.go diff --git a/changes/22692-script-policy-id b/changes/22692-script-policy-id new file mode 100644 index 0000000000..dc7de0c56a --- /dev/null +++ b/changes/22692-script-policy-id @@ -0,0 +1 @@ +* Record which policy automation triggered a script run in the activity feed diff --git a/docs/Contributing/Audit-logs.md b/docs/Contributing/Audit-logs.md index 25062d463f..0b25b68d11 100644 --- a/docs/Contributing/Audit-logs.md +++ b/docs/Contributing/Audit-logs.md @@ -887,6 +887,8 @@ This activity contains the following fields: - "script_execution_id": Execution ID of the script run. - "script_name": Name of the script (empty if it was an anonymous script). - "async": Whether the script was executed asynchronously. +- "policy_id": ID of the policy whose failure triggered the script run. Null if no associated policy. +- "policy_name": Name of the policy whose failure triggered the script run. Null if no associated policy. #### Example @@ -896,7 +898,9 @@ This activity contains the following fields: "host_display_name": "Anna's MacBook Pro", "script_name": "set-timezones.sh", "script_execution_id": "d6cffa75-b5b5-41ef-9230-15073c8a88cf", - "async": false + "async": false, + "policy_id": 123, + "policy_name": "Ensure photon torpedoes are primed" } ``` diff --git a/server/datastore/mysql/activities.go b/server/datastore/mysql/activities.go index ccaeb4f16a..8daef164b7 100644 --- a/server/datastore/mysql/activities.go +++ b/server/datastore/mysql/activities.go @@ -15,6 +15,8 @@ import ( "github.com/jmoiron/sqlx" ) +var automationActivityAuthor = "Fleet" + // NewActivity stores an activity item that the user performed func (ds *Datastore) NewActivity( ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, details []byte, createdAt time.Time, @@ -39,6 +41,10 @@ func (ds *Datastore) NewActivity( } userName = &user.Name userEmail = &user.Email + } else if ranScriptActivity, ok := activity.(fleet.ActivityTypeRanScript); ok { + if ranScriptActivity.PolicyID != nil { + userName = &automationActivityAuthor + } } cols := []string{"user_id", "user_name", "activity_type", "details", "created_at"} @@ -293,7 +299,7 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint // list pending scripts `SELECT hsr.execution_id as uuid, - u.name as name, + IF(hsr.policy_id IS NOT NULL, 'Fleet', u.name) as name, u.id as user_id, u.gravatar_url as gravatar_url, u.email as user_email, @@ -304,12 +310,16 @@ func (ds *Datastore) ListHostUpcomingActivities(ctx context.Context, hostID uint 'host_display_name', COALESCE(hdn.display_name, ''), 'script_name', COALESCE(scr.name, ''), 'script_execution_id', hsr.execution_id, - 'async', NOT hsr.sync_request + 'async', NOT hsr.sync_request, + 'policy_id', hsr.policy_id, + 'policy_name', p.name ) as details FROM host_script_results hsr LEFT OUTER JOIN users u ON u.id = hsr.user_id + LEFT OUTER JOIN + policies p ON p.id = hsr.policy_id LEFT OUTER JOIN host_display_names hdn ON hdn.host_id = hsr.host_id LEFT OUTER JOIN diff --git a/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions.go b/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions.go new file mode 100644 index 0000000000..bc16175b5f --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions.go @@ -0,0 +1,26 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20241004005000, Down_20241004005000) +} + +func Up_20241004005000(tx *sql.Tx) error { + if _, err := tx.Exec(` + ALTER TABLE host_script_results + ADD COLUMN policy_id INT UNSIGNED DEFAULT NULL, + ADD FOREIGN KEY fk_script_result_policy_id (policy_id) REFERENCES policies (id) ON DELETE SET NULL + `); err != nil { + return fmt.Errorf("failed to add policy_id to host script results: %w", err) + } + + return nil +} + +func Down_20241004005000(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions_test.go b/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions_test.go new file mode 100644 index 0000000000..71e0d62a2c --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20241004005000_AddPolicyIdToScriptExecutions_test.go @@ -0,0 +1,41 @@ +package tables + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUp_20241004005000(t *testing.T) { + db := applyUpToPrev(t) + + // insert a team + teamID := execNoErrLastID(t, db, `INSERT INTO teams (name) VALUES ("Foo")`) + + // insert a policy + policyID := execNoErrLastID(t, db, `INSERT INTO policies (name, query, description, team_id, checksum) + VALUES ('test_policy', "SELECT 1", "", ?, "a123b123")`, teamID) + + // insert a script + scriptContentID := execNoErrLastID(t, db, `INSERT INTO script_contents (md5_checksum, contents) VALUES ("md5", "echo 'Hello World'")`) + scriptID := execNoErrLastID(t, db, `INSERT INTO scripts ( + team_id, global_or_team_id, name, script_content_id + ) VALUES (?, ?, "hello-world.sh", ?)`, teamID, teamID, scriptContentID) + + // Apply current migration. + applyNext(t, db) + + // insert a script result + hostScriptResultID := execNoErrLastID(t, db, `INSERT INTO host_script_results + (host_id, execution_id, script_content_id, output, script_id, policy_id, user_id, sync_request) + VALUES (1, 'a123b123', ?, '', ?, ?, NULL, FALSE)`, scriptContentID, scriptID, policyID) + + // delete the associated policy + execNoErr(t, db, `DELETE FROM policies WHERE id = ?`, policyID) + + // policy ID should be null but script result should still exist + var count int + err := db.Get(&count, "SELECT COUNT(*) FROM host_script_results WHERE policy_id IS NULL AND id = ?", hostScriptResultID) + require.NoError(t, err) + require.Equal(t, 1, count) +} diff --git a/server/datastore/mysql/policies.go b/server/datastore/mysql/policies.go index 7ff7695414..1f3f2492e3 100644 --- a/server/datastore/mysql/policies.go +++ b/server/datastore/mysql/policies.go @@ -123,7 +123,7 @@ func (ds *Datastore) PolicyLite(ctx context.Context, id uint) (*fleet.PolicyLite var policy fleet.PolicyLite err := sqlx.GetContext( ctx, ds.reader(ctx), &policy, - `SELECT id, description, resolution FROM policies WHERE id=?`, id, + `SELECT id, name, description, resolution FROM policies WHERE id=?`, id, ) if err != nil { if errors.Is(err, sql.ErrNoRows) { diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 9dd2b88123..fa60b29cd6 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -521,6 +521,7 @@ CREATE TABLE `host_script_results` ( `script_content_id` int unsigned DEFAULT NULL, `host_deleted_at` timestamp NULL DEFAULT NULL, `timeout` int DEFAULT NULL, + `policy_id` int unsigned DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_host_script_results_execution_id` (`execution_id`), KEY `idx_host_script_results_host_exit_created` (`host_id`,`exit_code`,`created_at`), @@ -528,9 +529,11 @@ CREATE TABLE `host_script_results` ( KEY `idx_host_script_created_at` (`host_id`,`script_id`,`created_at`), KEY `fk_host_script_results_user_id` (`user_id`), KEY `script_content_id` (`script_content_id`), + KEY `fk_script_result_policy_id` (`policy_id`), CONSTRAINT `fk_host_script_results_script_id` FOREIGN KEY (`script_id`) REFERENCES `scripts` (`id`) ON DELETE SET NULL, CONSTRAINT `fk_host_script_results_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL, - CONSTRAINT `host_script_results_ibfk_1` FOREIGN KEY (`script_content_id`) REFERENCES `script_contents` (`id`) ON DELETE CASCADE + CONSTRAINT `host_script_results_ibfk_1` FOREIGN KEY (`script_content_id`) REFERENCES `script_contents` (`id`) ON DELETE CASCADE, + CONSTRAINT `host_script_results_ibfk_2` FOREIGN KEY (`policy_id`) REFERENCES `policies` (`id`) ON DELETE SET NULL ) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; /*!40101 SET @saved_cs_client = @@character_set_client */; @@ -1061,9 +1064,9 @@ CREATE TABLE `migration_status_tables` ( `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`) -) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=318 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) /*!50100 TABLESPACE `innodb_system` */ ENGINE=InnoDB AUTO_INCREMENT=319 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'); +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'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!50503 SET character_set_client = utf8mb4 */; CREATE TABLE `mobile_device_management_solutions` ( diff --git a/server/datastore/mysql/scripts.go b/server/datastore/mysql/scripts.go index 4332a4f15f..bc23f3b13b 100644 --- a/server/datastore/mysql/scripts.go +++ b/server/datastore/mysql/scripts.go @@ -39,8 +39,8 @@ func (ds *Datastore) NewHostScriptExecutionRequest(ctx context.Context, request func newHostScriptExecutionRequest(ctx context.Context, tx sqlx.ExtContext, request *fleet.HostScriptRequestPayload) (*fleet.HostScriptResult, error) { const ( - insStmt = `INSERT INTO host_script_results (host_id, execution_id, script_content_id, output, script_id, user_id, sync_request) VALUES (?, ?, ?, '', ?, ?, ?)` - getStmt = `SELECT hsr.id, hsr.host_id, hsr.execution_id, hsr.created_at, hsr.script_id, hsr.user_id, hsr.sync_request, sc.contents as script_contents FROM host_script_results hsr JOIN script_contents sc WHERE sc.id = hsr.script_content_id AND hsr.id = ?` + insStmt = `INSERT INTO host_script_results (host_id, execution_id, script_content_id, output, script_id, policy_id, user_id, sync_request) VALUES (?, ?, ?, '', ?, ?, ?, ?)` + getStmt = `SELECT hsr.id, hsr.host_id, hsr.execution_id, hsr.created_at, hsr.script_id, hsr.policy_id, hsr.user_id, hsr.sync_request, sc.contents as script_contents FROM host_script_results hsr JOIN script_contents sc WHERE sc.id = hsr.script_content_id AND hsr.id = ?` ) execID := uuid.New().String() @@ -49,6 +49,7 @@ func newHostScriptExecutionRequest(ctx context.Context, tx sqlx.ExtContext, requ execID, request.ScriptContentID, request.ScriptID, + request.PolicyID, request.UserID, request.SyncRequest, ) @@ -260,6 +261,7 @@ func (ds *Datastore) getHostScriptExecutionResultDB(ctx context.Context, q sqlx. hsr.execution_id, sc.contents as script_contents, hsr.script_id, + hsr.policy_id, hsr.output, hsr.runtime, hsr.exit_code, diff --git a/server/fleet/activities.go b/server/fleet/activities.go index 373aa5b720..dd7231b62f 100644 --- a/server/fleet/activities.go +++ b/server/fleet/activities.go @@ -1166,11 +1166,13 @@ func (a ActivityTypeDisabledWindowsMDM) Documentation() (activity, details, deta } type ActivityTypeRanScript struct { - HostID uint `json:"host_id"` - HostDisplayName string `json:"host_display_name"` - ScriptExecutionID string `json:"script_execution_id"` - ScriptName string `json:"script_name"` - Async bool `json:"async"` + HostID uint `json:"host_id"` + HostDisplayName string `json:"host_display_name"` + ScriptExecutionID string `json:"script_execution_id"` + ScriptName string `json:"script_name"` + Async bool `json:"async"` + PolicyID *uint `json:"policy_id"` + PolicyName *string `json:"policy_name"` } func (a ActivityTypeRanScript) ActivityName() string { @@ -1188,12 +1190,16 @@ func (a ActivityTypeRanScript) Documentation() (activity, details, detailsExampl - "host_display_name": Display name of the host. - "script_execution_id": Execution ID of the script run. - "script_name": Name of the script (empty if it was an anonymous script). -- "async": Whether the script was executed asynchronously.`, `{ +- "async": Whether the script was executed asynchronously. +- "policy_id": ID of the policy whose failure triggered the script run. Null if no associated policy. +- "policy_name": Name of the policy whose failure triggered the script run. Null if no associated policy.`, `{ "host_id": 1, "host_display_name": "Anna's MacBook Pro", "script_name": "set-timezones.sh", "script_execution_id": "d6cffa75-b5b5-41ef-9230-15073c8a88cf", - "async": false + "async": false, + "policy_id": 123, + "policy_name": "Ensure photon torpedoes are primed" }` } diff --git a/server/fleet/policies.go b/server/fleet/policies.go index 9a27f39937..10c3c907d4 100644 --- a/server/fleet/policies.go +++ b/server/fleet/policies.go @@ -271,6 +271,8 @@ type PolicyScriptData struct { // PolicyLite is a stripped down version of the policy. type PolicyLite struct { ID uint `db:"id"` + // Name is the name of the policy. + Name string `db:"name"` // Description describes the policy. Description string `db:"description"` // Resolution describes how to solve a failing policy. diff --git a/server/fleet/scripts.go b/server/fleet/scripts.go index 0b51a5a887..1dcbb343e9 100644 --- a/server/fleet/scripts.go +++ b/server/fleet/scripts.go @@ -138,6 +138,7 @@ func (hs *HostScriptDetail) setLastExecution(executionID *string, executedAt *ti type HostScriptRequestPayload struct { HostID uint `json:"host_id"` ScriptID *uint `json:"script_id"` + PolicyID *uint `json:"policy_id"` ScriptContents string `json:"script_contents"` ScriptContentID uint `json:"-"` ScriptName string `json:"script_name"` @@ -217,6 +218,9 @@ type HostScriptResult struct { // ScriptID is the id of the saved script to execute, or nil if this was an // anonymous script execution. ScriptID *uint `json:"script_id" db:"script_id"` + // PolicyID is the id of the policy that triggered the script execution, or + // nil if the execution was not triggered by a policy failure + PolicyID *uint `json:"policy_id" db:"policy_id"` // UserID is the id of the user that requested execution. It is not part of // the rendered JSON as it is only returned by the // /hosts/:id/activities/upcoming endpoint which doesn't use this struct as diff --git a/server/service/activities.go b/server/service/activities.go index 861c873095..a38881f8a4 100644 --- a/server/service/activities.go +++ b/server/service/activities.go @@ -67,6 +67,8 @@ func (svc *Service) NewActivity(ctx context.Context, user *fleet.User, activity return newActivity(ctx, user, activity, svc.ds, svc.logger) } +var automationActivityAuthor = "Fleet" + func newActivity(ctx context.Context, user *fleet.User, activity fleet.ActivityDetails, ds fleet.Datastore, logger kitlog.Logger) error { appConfig, err := ds.AppConfig(ctx) if err != nil { @@ -84,6 +86,8 @@ func newActivity(ctx context.Context, user *fleet.User, activity fleet.ActivityD var userID *uint var userName *string var userEmail *string + activityType := activity.ActivityName() + if user != nil { // To support creating activities with users that were deleted. This can happen // for automatically installed software which uses the author of the upload as the author of @@ -93,8 +97,12 @@ func newActivity(ctx context.Context, user *fleet.User, activity fleet.ActivityD } userName = &user.Name userEmail = &user.Email + } else if ranScriptActivity, ok := activity.(fleet.ActivityTypeRanScript); ok { + if ranScriptActivity.PolicyID != nil { + userName = &automationActivityAuthor + } } - activityType := activity.ActivityName() + go func() { retryStrategy := backoff.NewExponentialBackOff() retryStrategy.MaxElapsedTime = 30 * time.Minute diff --git a/server/service/integration_enterprise_test.go b/server/service/integration_enterprise_test.go index 0dc8e8b697..eed8ebef68 100644 --- a/server/service/integration_enterprise_test.go +++ b/server/service/integration_enterprise_test.go @@ -6284,7 +6284,7 @@ func (s *integrationEnterpriseTestSuite) TestRunHostSavedScript() { s.lastActivityMatches( fleet.ActivityTypeRanScript{}.ActivityName(), fmt.Sprintf( - `{"host_id": %d, "host_display_name": %q, "script_name": %q, "script_execution_id": %q, "async": true}`, + `{"host_id": %d, "host_display_name": %q, "script_name": %q, "script_execution_id": %q, "async": true, "policy_id": null, "policy_name": null}`, host.ID, host.DisplayName(), savedNoTmScript.Name, scriptResultResp.ExecutionID, ), 0, @@ -12196,7 +12196,7 @@ func (s *integrationEnterpriseTestSuite) TestHostScriptSoftDelete() { s.lastActivityOfTypeMatches( fleet.ActivityTypeRanScript{}.ActivityName(), fmt.Sprintf( - `{"host_id": %d, "host_display_name": %q, "script_name": "", "script_execution_id": %q, "async": true}`, + `{"host_id": %d, "host_display_name": %q, "script_name": "", "script_execution_id": %q, "async": true, "policy_id": null, "policy_name": null}`, host.ID, host.DisplayName(), scriptExecID), 0) // create a saved script execution request @@ -12218,7 +12218,7 @@ func (s *integrationEnterpriseTestSuite) TestHostScriptSoftDelete() { http.StatusOK) s.lastActivityOfTypeMatches( fleet.ActivityTypeRanScript{}.ActivityName(), - fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "script_name": "script1.sh", "script_execution_id": %q, "async": true}`, + fmt.Sprintf(`{"host_id": %d, "host_display_name": %q, "script_name": "script1.sh", "script_execution_id": %q, "async": true, "policy_id": null, "policy_name": null}`, host.ID, host.DisplayName(), savedScriptExecID), 0) // get the anoymous script result details @@ -14717,11 +14717,12 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { }, ), http.StatusOK, &distributedResp) - hostPendingScript, err = s.ds.IsExecutionPendingForHost(ctx, host3Team2.ID, psScript.ID) + host3PendingScripts, err := s.ds.ListPendingHostScriptExecutions(ctx, host3Team2.ID) require.NoError(t, err) - require.True(t, hostPendingScript) + require.Len(t, host3PendingScripts, 1) + host3executionID := host3PendingScripts[0].ExecutionID - // Unassociate policy4Team2 from script. + // Dissociate policy4Team2 from script. mtplr = modifyTeamPolicyResponse{} s.DoJSON("PATCH", fmt.Sprintf("/api/latest/fleet/teams/%d/policies/%d", team2.ID, policy4Team2.ID), modifyTeamPolicyRequest{ ModifyPolicyPayload: fleet.ModifyPolicyPayload{ @@ -14750,6 +14751,38 @@ func (s *integrationEnterpriseTestSuite) TestPolicyAutomationsScripts() { hostPendingScripts, err := s.ds.ListPendingHostScriptExecutions(ctx, hostVanillaOsquery5Team1.ID) require.NoError(t, err) require.Len(t, hostPendingScripts, 0) + + // activity feed should show script run as pending, with "Fleet" as author, policy ID and name set in body + var listResp listActivitiesResponse + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities/upcoming", host3Team2.ID), nil, http.StatusOK, &listResp) + require.Len(t, listResp.Activities, 1) + require.Nil(t, listResp.Activities[0].ActorEmail) + require.Equal(t, "Fleet", *listResp.Activities[0].ActorFullName) + require.Nil(t, listResp.Activities[0].ActorGravatar) + require.Equal(t, "ran_script", listResp.Activities[0].Type) + var activityJson map[string]interface{} + err = json.Unmarshal(*listResp.Activities[0].Details, &activityJson) + require.NoError(t, err) + require.Equal(t, float64(policy4Team2.ID), activityJson["policy_id"]) + require.Equal(t, "policy4Team2", activityJson["policy_name"]) + + // post script result response + var orbitPostScriptResp orbitPostScriptResultResponse + s.DoJSON("POST", "/api/fleet/orbit/scripts/result", + json.RawMessage(fmt.Sprintf(`{"orbit_node_key": %q, "execution_id": %q, "exit_code": 0, "output": "ok"}`, *host3Team2.OrbitNodeKey, host3executionID)), + http.StatusOK, &orbitPostScriptResp) + + // activity feed should show script run as completed + s.DoJSON("GET", fmt.Sprintf("/api/latest/fleet/hosts/%d/activities", host3Team2.ID), nil, http.StatusOK, &listResp) + require.Len(t, listResp.Activities, 1) + require.Equal(t, "", *listResp.Activities[0].ActorEmail) // actor email is blank rather than nil here 👀 + require.Equal(t, "Fleet", *listResp.Activities[0].ActorFullName) + require.Nil(t, listResp.Activities[0].ActorGravatar) + require.Equal(t, "ran_script", listResp.Activities[0].Type) + err = json.Unmarshal(*listResp.Activities[0].Details, &activityJson) + require.NoError(t, err) + require.Equal(t, float64(policy4Team2.ID), activityJson["policy_id"]) + require.Equal(t, "policy4Team2", activityJson["policy_name"]) } func (s *integrationEnterpriseTestSuite) TestSoftwareInstallersWithoutBundleIdentifier() { diff --git a/server/service/orbit.go b/server/service/orbit.go index bbc6c437e0..55885200df 100644 --- a/server/service/orbit.go +++ b/server/service/orbit.go @@ -733,6 +733,13 @@ func (svc *Service) SaveHostScriptResult(ctx context.Context, result *fleet.Host } default: // TODO(sarah): We may need to special case lock/unlock script results here? + var policyName *string + if hsr.PolicyID != nil { + if policy, err := svc.ds.PolicyLite(ctx, *hsr.PolicyID); err == nil { + policyName = &policy.Name // fall back to blank policy name if we can't retrieve the policy + } + } + if err := svc.NewActivity( ctx, user, @@ -742,6 +749,8 @@ func (svc *Service) SaveHostScriptResult(ctx context.Context, result *fleet.Host ScriptExecutionID: hsr.ExecutionID, ScriptName: scriptName, Async: !hsr.SyncRequest, + PolicyID: hsr.PolicyID, + PolicyName: policyName, }, ); err != nil { return ctxerr.Wrap(ctx, err, "create activity for script execution request") diff --git a/server/service/osquery.go b/server/service/osquery.go index 8c2a630941..9dd19141e5 100644 --- a/server/service/osquery.go +++ b/server/service/osquery.go @@ -1931,6 +1931,8 @@ func (svc *Service) processScriptsForNewlyFailingPolicies( } for _, failingPolicyWithScript := range failingPoliciesWithScript { + policyID := failingPolicyWithScript.ID + scriptMetadata, err := svc.ds.Script(ctx, failingPolicyWithScript.ScriptID) if err != nil { return ctxerr.Wrap(ctx, err, "get script metadata by id") @@ -1938,7 +1940,7 @@ func (svc *Service) processScriptsForNewlyFailingPolicies( logger := log.With(svc.logger, "host_id", hostID, "host_platform", hostPlatform, - "policy_id", failingPolicyWithScript.ID, + "policy_id", policyID, "script_id", failingPolicyWithScript.ScriptID, "script_name", scriptMetadata.Name, ) @@ -1989,6 +1991,7 @@ func (svc *Service) processScriptsForNewlyFailingPolicies( ScriptContentID: scriptMetadata.ScriptContentID, ScriptID: &scriptMetadata.ID, TeamID: policyTeamID, + PolicyID: &policyID, // no user ID as scripts are executed by Fleet }