diff --git a/changes/18079-query-results-bug-transfer-hosts b/changes/18079-query-results-bug-transfer-hosts new file mode 100644 index 000000000000..1e114771da78 --- /dev/null +++ b/changes/18079-query-results-bug-transfer-hosts @@ -0,0 +1 @@ +* Fixed bug where hosts query results were not cleared after transferring the host to other teams. diff --git a/server/datastore/mysql/hosts.go b/server/datastore/mysql/hosts.go index 56474197b4d0..70cd85028910 100644 --- a/server/datastore/mysql/hosts.go +++ b/server/datastore/mysql/hosts.go @@ -2717,6 +2717,9 @@ func (ds *Datastore) AddHostsToTeam(ctx context.Context, teamID *uint, hostIDs [ if err := cleanupPolicyMembershipOnTeamChange(ctx, tx, hostIDs); err != nil { return ctxerr.Wrap(ctx, err, "AddHostsToTeam delete policy membership") } + if err := cleanupQueryResultsOnTeamChange(ctx, tx, hostIDs); err != nil { + return ctxerr.Wrap(ctx, err, "AddHostsToTeam delete query results") + } query, args, err := sqlx.In(`UPDATE hosts SET team_id = ? WHERE id IN (?)`, teamID, hostIDs) if err != nil { diff --git a/server/datastore/mysql/hosts_test.go b/server/datastore/mysql/hosts_test.go index 453a69e1d6ef..f6e5613b86fd 100644 --- a/server/datastore/mysql/hosts_test.go +++ b/server/datastore/mysql/hosts_test.go @@ -165,6 +165,7 @@ func TestHosts(t *testing.T) { {"HostHealth", testHostHealth}, {"GetHostOrbitInfo", testGetHostOrbitInfo}, {"HostnamesByIdentifiers", testHostnamesByIdentifiers}, + {"HostsAddToTeamCleansUpTeamQueryResults", testHostsAddToTeamCleansUpTeamQueryResults}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { @@ -8860,3 +8861,200 @@ func testHostnamesByIdentifiers(t *testing.T, ds *Datastore) { }) } } + +func testHostsAddToTeamCleansUpTeamQueryResults(t *testing.T, ds *Datastore) { + ctx := context.Background() + + team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1"}) + require.NoError(t, err) + team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team2"}) + require.NoError(t, err) + + hostCount := 1 + newHost := func(teamID *uint) *fleet.Host { + h, err := ds.NewHost(ctx, &fleet.Host{ + OsqueryHostID: ptr.String(fmt.Sprintf("foobar%d", hostCount)), + NodeKey: ptr.String(fmt.Sprintf("nodekey%d", hostCount)), + TeamID: teamID, + }) + require.NoError(t, err) + hostCount++ + return h + } + newQuery := func(name string, teamID *uint) *fleet.Query { + q, err := ds.NewQuery(ctx, &fleet.Query{ + Name: name, + Query: "SELECT 1:", + TeamID: teamID, + Logging: fleet.LoggingSnapshot, + }) + require.NoError(t, err) + return q + } + + h0 := newHost(nil) + h1 := newHost(&team1.ID) + h2 := newHost(&team2.ID) + h3 := newHost(&team2.ID) + + hostStaticOnTeam1 := newHost(&team1.ID) // host that we won't move + + query0Global := newQuery("query0Global", nil) + query1Team1 := newQuery("query1Team1", &team1.ID) + query2Team2 := newQuery("query2Team2", &team2.ID) + + // Transfer h2 from team2 to team1 and back without any query results yet. + err = ds.AddHostsToTeam(ctx, &team1.ID, []uint{h2.ID}) + require.NoError(t, err) + err = ds.AddHostsToTeam(ctx, &team2.ID, []uint{h2.ID}) + require.NoError(t, err) + + data := ptr.RawMessage(json.RawMessage(`{"foo": "bar"}`)) + h0Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h0.ID, + QueryID: query0Global.ID, + Data: data, + }, + } + h1Global0Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h1.ID, + QueryID: query0Global.ID, + Data: data, + }, + } + h1Query1Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h1.ID, + QueryID: query1Team1.ID, + Data: data, + }, + } + h2Global0Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h2.ID, + QueryID: query0Global.ID, + Data: data, + }, + } + h2Query2Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h2.ID, + QueryID: query2Team2.ID, + Data: data, + }, + } + h3Global0Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h3.ID, + QueryID: query0Global.ID, + Data: data, + }, + } + h3Query2Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: h3.ID, + QueryID: query2Team2.ID, + Data: data, + }, + } + h4Global0Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: hostStaticOnTeam1.ID, + QueryID: query0Global.ID, + Data: data, + }, + } + h4Query1Results := []*fleet.ScheduledQueryResultRow{ + { + HostID: hostStaticOnTeam1.ID, + QueryID: query1Team1.ID, + Data: data, + }, + } + for _, results := range [][]*fleet.ScheduledQueryResultRow{ + h0Results, + h1Global0Results, + h1Query1Results, + h2Global0Results, + h2Query2Results, + h3Global0Results, + h3Query2Results, + h4Global0Results, + h4Query1Results, + } { + err = ds.OverwriteQueryResultRows(ctx, results) + require.NoError(t, err) + } + + tf := fleet.TeamFilter{ + User: &fleet.User{ + GlobalRole: ptr.String(fleet.RoleAdmin), + }, + } + + rows, err := ds.QueryResultRows(ctx, query0Global.ID, tf) + require.NoError(t, err) + require.Len(t, rows, 5) + rows, err = ds.QueryResultRows(ctx, query1Team1.ID, tf) + require.NoError(t, err) + require.Len(t, rows, 2) + rows, err = ds.QueryResultRows(ctx, query2Team2.ID, tf) + require.NoError(t, err) + require.Len(t, rows, 2) + + // Transfer h2 from team2 to team1. + err = ds.AddHostsToTeam(ctx, &team1.ID, []uint{h2.ID}) + require.NoError(t, err) + // Transfer h1 from team1 to team2. + err = ds.AddHostsToTeam(ctx, &team2.ID, []uint{h1.ID}) + require.NoError(t, err) + // Transfer h3 from team2 to global. + err = ds.AddHostsToTeam(ctx, nil, []uint{h3.ID}) + require.NoError(t, err) + + // No global query results should be deleted + rows, err = ds.QueryResultRows(ctx, query0Global.ID, tf) + require.NoError(t, err) + require.Len(t, rows, 5) + // Results for h1 should be gone, and results for hostStaticOnTeam1 should be here. + rows, err = ds.QueryResultRows(ctx, query1Team1.ID, tf) + require.NoError(t, err) + require.Len(t, rows, 1) + require.Equal(t, hostStaticOnTeam1.ID, rows[0].HostID) + // Results for h2 and h3 should be gone. + rows, err = ds.QueryResultRows(ctx, query2Team2.ID, tf) + require.NoError(t, err) + require.Empty(t, rows) + + // h1 should have only the global result. + h1, err = ds.Host(ctx, h1.ID) + require.NoError(t, err) + require.Len(t, h1.PackStats, 1) + require.Len(t, h1.PackStats[0].QueryStats, 1) + require.Equal(t, query0Global.ID, h1.PackStats[0].QueryStats[0].ScheduledQueryID) + + // h2 should have only the global result. + h2, err = ds.Host(ctx, h2.ID) + require.NoError(t, err) + require.Len(t, h2.PackStats, 1) + require.Len(t, h2.PackStats[0].QueryStats, 1) + require.Equal(t, query0Global.ID, h2.PackStats[0].QueryStats[0].ScheduledQueryID) + + // h3 should have only the global result. + h3, err = ds.Host(ctx, h3.ID) + require.NoError(t, err) + require.Len(t, h3.PackStats, 1) + require.Len(t, h3.PackStats[0].QueryStats, 1) + require.Equal(t, query0Global.ID, h3.PackStats[0].QueryStats[0].ScheduledQueryID) + + // hostStaticOnTeam1 should have the global result and the team1 result. + hostStaticOnTeam1, err = ds.Host(ctx, hostStaticOnTeam1.ID) + require.NoError(t, err) + require.Len(t, hostStaticOnTeam1.PackStats, 2) + require.Len(t, hostStaticOnTeam1.PackStats[0].QueryStats, 1) + require.Equal(t, query0Global.ID, hostStaticOnTeam1.PackStats[0].QueryStats[0].ScheduledQueryID) + require.Len(t, hostStaticOnTeam1.PackStats[1].QueryStats, 1) + require.Equal(t, query1Team1.ID, hostStaticOnTeam1.PackStats[1].QueryStats[0].ScheduledQueryID) +} diff --git a/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults.go b/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults.go new file mode 100644 index 000000000000..063a15061738 --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults.go @@ -0,0 +1,30 @@ +package tables + +import ( + "database/sql" + "fmt" +) + +func init() { + MigrationClient.AddMigration(Up_20240430111727, Down_20240430111727) +} + +func Up_20240430111727(tx *sql.Tx) error { + // This cleanup correspond to the following bug: https://github.com/fleetdm/fleet/issues/18079. + // The following deletes "team query results" that do not match the host's team. + _, err := tx.Exec(` + DELETE qr + FROM query_results qr + JOIN queries q ON (q.id=qr.query_id) + JOIN hosts h ON (h.id=qr.host_id) + WHERE q.team_id IS NOT NULL AND q.team_id != COALESCE(h.team_id, 0); + `) + if err != nil { + return fmt.Errorf("failed to delete query_results %w", err) + } + return nil +} + +func Down_20240430111727(tx *sql.Tx) error { + return nil +} diff --git a/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults_test.go b/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults_test.go new file mode 100644 index 000000000000..a2f7df2bf83c --- /dev/null +++ b/server/datastore/mysql/migrations/tables/20240430111727_CleanupQueryResults_test.go @@ -0,0 +1,98 @@ +package tables + +import ( + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUp_20240430111727(t *testing.T) { + db := applyUpToPrev(t) + + hostID := 1 + newTeam := func(name string) uint { + return uint(execNoErrLastID(t, db, + `INSERT INTO teams (name) VALUES (?);`, + name, + )) + } + newHost := func(teamID *uint) uint { + id := fmt.Sprintf("%d", hostID) + hostID++ + return uint(execNoErrLastID(t, db, + `INSERT INTO hosts (osquery_host_id, node_key, team_id) VALUES (?, ?, ?);`, + id, id, teamID, + )) + } + newQuery := func(name string, teamID *uint) uint { + return uint(execNoErrLastID(t, db, + `INSERT INTO queries (name, description, logging_type, team_id, query, saved) VALUES (?, '', 'snapshot', ?, 'SELECT 1;', 1);`, + name, teamID, + )) + } + newQueryResults := func(queryID, hostID uint, resultCount int) { + var args []interface{} + for i := 0; i < resultCount; i++ { + args = append(args, queryID, hostID, fmt.Sprintf(`{"foo": "bar%d"}`, i)) + } + values := strings.TrimSuffix(strings.Repeat("(?, ?, ?, NOW()),", resultCount), ",") + _, err := db.Exec(fmt.Sprintf(`INSERT INTO query_results (query_id, host_id, data, last_fetched) VALUES %s;`, values), + args..., + ) + require.NoError(t, err) + } + + team1ID := newTeam("team1") + team2ID := newTeam("team2") + host1GlobalID := newHost(nil) + host2Team1ID := newHost(&team1ID) + host3Team2ID := newHost(&team2ID) + query1GlobalID := newQuery("query1Global", nil) + query2Team1ID := newQuery("query2Team1", &team1ID) + query3Team2ID := newQuery("query3Team2", &team2ID) + + newQueryResults(query1GlobalID, host1GlobalID, 1) + newQueryResults(query1GlobalID, host2Team1ID, 2) + newQueryResults(query1GlobalID, host3Team2ID, 3) + + newQueryResults(query2Team1ID, host1GlobalID, 4) + newQueryResults(query2Team1ID, host2Team1ID, 5) + newQueryResults(query2Team1ID, host3Team2ID, 6) + + newQueryResults(query3Team2ID, host1GlobalID, 7) + newQueryResults(query3Team2ID, host2Team1ID, 8) + newQueryResults(query3Team2ID, host3Team2ID, 9) + + // Apply current migration. + applyNext(t, db) + + getQueryResultsCount := func(queryID, hostID uint) int { + var count int + err := db.Get(&count, `SELECT COUNT(*) FROM query_results WHERE query_id = ? AND host_id = ?`, queryID, hostID) + require.NoError(t, err) + return count + } + + count := getQueryResultsCount(query1GlobalID, host1GlobalID) + require.Equal(t, 1, count) // result for global queries are not deleted. + count = getQueryResultsCount(query1GlobalID, host2Team1ID) + require.Equal(t, 2, count) // result for global queries are not deleted. + count = getQueryResultsCount(query1GlobalID, host3Team2ID) + require.Equal(t, 3, count) // result for global queries are not deleted. + + count = getQueryResultsCount(query2Team1ID, host1GlobalID) + require.Equal(t, 0, count) // query results of a team query different than the host's team are deleted. + count = getQueryResultsCount(query2Team1ID, host2Team1ID) + require.Equal(t, 5, count) // team query results of the host's team are not deleted. + count = getQueryResultsCount(query2Team1ID, host3Team2ID) + require.Equal(t, 0, count) // query results of a team query different than the host's team are deleted. + + count = getQueryResultsCount(query3Team2ID, host1GlobalID) + require.Equal(t, 0, count) // query results of a team query different than the host's team are deleted. + count = getQueryResultsCount(query3Team2ID, host2Team1ID) + require.Equal(t, 0, count) // query results of a team query different than the host's team are deleted. + count = getQueryResultsCount(query3Team2ID, host3Team2ID) + require.Equal(t, 9, count) // team query results of the host's team are not deleted. +} diff --git a/server/datastore/mysql/policies.go b/server/datastore/mysql/policies.go index 0952250198da..0f07441110b6 100644 --- a/server/datastore/mysql/policies.go +++ b/server/datastore/mysql/policies.go @@ -841,6 +841,22 @@ func cleanupPolicyMembershipOnTeamChange(ctx context.Context, tx sqlx.ExtContext return nil } +func cleanupQueryResultsOnTeamChange(ctx context.Context, tx sqlx.ExtContext, hostIDs []uint) error { + // Similar to cleanupPolicyMembershipOnTeamChange, hosts can belong to one team only, so we just delete all + // the query results of the hosts that belong to queries that are not global. + const cleanupQuery = ` + DELETE FROM query_results + WHERE query_id IN (SELECT id FROM queries WHERE team_id IS NOT NULL) AND host_id IN (?)` + query, args, err := sqlx.In(cleanupQuery, hostIDs) + if err != nil { + return ctxerr.Wrap(ctx, err, "build cleanup query results query") + } + if _, err := tx.ExecContext(ctx, query, args...); err != nil { + return ctxerr.Wrap(ctx, err, "exec cleanup query results query") + } + return nil +} + func cleanupPolicyMembershipOnPolicyUpdate(ctx context.Context, db sqlx.ExecerContext, policyID uint, platforms string) error { if platforms == "" { // all platforms allowed, nothing to clean up diff --git a/server/datastore/mysql/schema.sql b/server/datastore/mysql/schema.sql index 741a8a78b36a..56ae918699a2 100644 --- a/server/datastore/mysql/schema.sql +++ b/server/datastore/mysql/schema.sql @@ -886,9 +886,9 @@ CREATE TABLE `migration_status_tables` ( `tstamp` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=264 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +) ENGINE=InnoDB AUTO_INCREMENT=265 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'); +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'); /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `mobile_device_management_solutions` ( diff --git a/server/service/osquery.go b/server/service/osquery.go index 67dfc36133b5..bf72824aaeff 100644 --- a/server/service/osquery.go +++ b/server/service/osquery.go @@ -1865,6 +1865,16 @@ func (svc *Service) saveResultLogsToQueryReports(ctx context.Context, unmarshale continue } + hostTeamID := uint(0) + if host.TeamID != nil { + hostTeamID = *host.TeamID + } + if dbQuery.TeamID != nil && *dbQuery.TeamID != hostTeamID { + // The host was transferred to another team/global so we ignore the incoming results + // of this query that belong to a different team. + continue + } + // We first check the current query results count using the DB reader (also cached) // to reduce the DB writer load of osquery/log requests when the host count is high. count, err := svc.ds.ResultCountForQuery(ctx, dbQuery.ID) diff --git a/server/service/osquery_test.go b/server/service/osquery_test.go index 7f3fb1142ab5..35d2d5962710 100644 --- a/server/service/osquery_test.go +++ b/server/service/osquery_test.go @@ -546,6 +546,22 @@ func TestSubmitResultLogsToLogDestination(t *testing.T) { } ds.QueryByNameFunc = func(ctx context.Context, teamID *uint, name string) (*fleet.Query, error) { switch { + case teamID != nil && *teamID == 1: + return &fleet.Query{ + ID: 4242, + Name: name, + AutomationsEnabled: true, + TeamID: ptr.Uint(1), + Logging: fleet.LoggingSnapshot, + }, nil + case teamID != nil && *teamID == 2: + return &fleet.Query{ + ID: 4343, + Name: name, + AutomationsEnabled: true, + TeamID: ptr.Uint(2), + Logging: fleet.LoggingSnapshot, + }, nil case teamID == nil && (name == "time" || name == "system_info" || name == "encrypted" || name == "hosts"): return &fleet.Query{ Name: name, @@ -597,11 +613,16 @@ func TestSubmitResultLogsToLogDestination(t *testing.T) { ds.ResultCountForQueryFunc = func(ctx context.Context, queryID uint) (int, error) { return 0, nil } + teamQueryResultsStored := false ds.OverwriteQueryResultRowsFunc = func(ctx context.Context, rows []*fleet.ScheduledQueryResultRow) error { if len(rows) == 0 { return nil } switch { + case rows[0].QueryID == 4242: + t.Fatal("should not happen, as query 4242 is a team query and host is global") + case rows[0].QueryID == 4343: + teamQueryResultsStored = true case rows[0].QueryID == 123: require.Len(t, rows, 1) require.Equal(t, uint(999), rows[0].HostID) @@ -654,6 +675,9 @@ func TestSubmitResultLogsToLogDestination(t *testing.T) { `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-foo/bar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`, `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`, `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/PackName","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`, + + // Query results of a query that belongs to a different team than the host's team (can happen when host is transferred from one team to another or no team). + `{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-1/Foobar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`, } logJSON := fmt.Sprintf("[%s]", strings.Join(validLogResults, ",")) @@ -671,7 +695,8 @@ func TestSubmitResultLogsToLogDestination(t *testing.T) { require.NoError(t, err) host := fleet.Host{ - ID: 999, + ID: 999, + TeamID: nil, // Global host. } ctx = hostctx.NewContext(ctx, &host) @@ -691,6 +716,25 @@ func TestSubmitResultLogsToLogDestination(t *testing.T) { require.NoError(t, err) assert.Equal(t, validResults, testLogger.logs) + + // + // Run a similar test but now with a team host. + // + host = fleet.Host{ + ID: 999, + TeamID: ptr.Uint(2), + } + ctx = hostctx.NewContext(ctx, &host) + results := []json.RawMessage{ + // This query should be ignored. + json.RawMessage(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-1/Foobar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`), + // This query should be stored. + json.RawMessage(`{"snapshot":[{"hour":"20","minutes":"8"}],"action":"snapshot","name":"pack/team-2/Zoobar","hostIdentifier":"1379f59d98f4","calendarTime":"Tue Jan 10 20:08:51 2017 UTC","unixTime":1484078931,"decorations":{"host_uuid":"EB714C9D-C1F8-A436-B6DA-3F853C5502EA"}}`), + } + err = serv.SubmitResultLogs(ctx, results) + require.NoError(t, err) + + require.True(t, teamQueryResultsStored) } func TestSaveResultLogsToQueryReports(t *testing.T) {