From d5e82fda30d7d0b05acccae7c187ce2ddb44b331 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:49 +0000 Subject: [PATCH 01/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- ...20240222073518_AddCertInfoToNanoCertAssociations.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/datastore/mysql/migrations/tables/20240222073518_AddCertInfoToNanoCertAssociations.go b/server/datastore/mysql/migrations/tables/20240222073518_AddCertInfoToNanoCertAssociations.go index 12ae487694f..873cc0b063f 100644 --- a/server/datastore/mysql/migrations/tables/20240222073518_AddCertInfoToNanoCertAssociations.go +++ b/server/datastore/mysql/migrations/tables/20240222073518_AddCertInfoToNanoCertAssociations.go @@ -94,9 +94,13 @@ func updateCertAssociationTimestamps(txx *sqlx.Tx, limit, offset int) error { expiries := make(map[string]time.Time, len(scepCerts)) for i, rawCert := range scepCerts { block, _ := pem.Decode(rawCert.CertificatePEM) + if block == nil { + log.Printf("failed to decode PEM for certificate with serial %s", rawCert.Serial) + continue + } cert, err := x509.ParseCertificate(block.Bytes) if err != nil { - log.Printf("failed to parse certificate with serial %s", rawCert.Serial) + log.Printf("failed to parse certificate with serial %s: %v", rawCert.Serial, err) continue } @@ -123,6 +127,10 @@ func updateCertAssociationTimestamps(txx *sqlx.Tx, limit, offset int) error { return fmt.Errorf("failed to retrieve cert associations: %w", err) } + if len(assocs) == 0 { + return nil + } + var sb strings.Builder updateAssocArgs := make([]any, len(assocs)*3) for i, assoc := range assocs { From 414a3a115d5b4e84adb2b28520ec3f18cddcc08a Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:50 +0000 Subject: [PATCH 02/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- tools/mdm/assets/main.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/mdm/assets/main.go b/tools/mdm/assets/main.go index 2fd34c2870a..b4c53e5623f 100644 --- a/tools/mdm/assets/main.go +++ b/tools/mdm/assets/main.go @@ -74,7 +74,7 @@ func setupSharedFlags() { func setupDS(privateKey, userName, password, address, name string) *mysql.Datastore { db, err := sql.Open( "mysql", - fmt.Sprintf("%s:%s@tcp(%s)/?multiStatements=true&tls=skip-verify", testUsername, testPassword, testAddress), + fmt.Sprintf("%s:%s@tcp(%s)/?multiStatements=true&tls=skip-verify", userName, password, address), ) if err != nil { log.Fatal("opening MySQL connection:", err) @@ -324,9 +324,20 @@ export FLEET_MDM_APPLE_BM_KEY=%[1]s/abm_key.key // SCEPDepot.Serial() from ever handing it out to a future client cert. // We deliberately do not insert into identity_certificates — the CA // cert itself lives in mdm_config_assets, not the depot's cert table. + // + // NOTE: this uses a separate raw *sql.DB connection (rather than the + // `ds` datastore) because reserving the serial and writing the + // rolled-over CA cert via ReplaceMDMConfigAssets are not wrapped in a + // single cross-datastore transaction. If ReplaceMDMConfigAssets fails + // after the serial below has been allocated, this tool exits via + // log.Fatal without releasing/rolling back the reserved serial. This + // is an accepted trade-off: the only consequence is a permanent gap + // in the identity_serials auto-increment sequence (no cert is ever + // issued using the leaked serial, so there is no risk of collision + // or of a dangling/invalid certificate). rawDB, err := sql.Open( "mysql", - fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=skip-verify", flagDBUser, flagDBPass, flagDBAddress, flagDBName), + fmt.Sprintf("%s:%s@tcp(%s)/%s?tls=%s", flagDBUser, flagDBPass, flagDBAddress, flagDBName, "skip-verify"), ) if err != nil { log.Fatal("opening MySQL connection to reserve CA serial: ", err) From aa408be0b88b6a71314555c8545199a805c93f18 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:51 +0000 Subject: [PATCH 03/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/reconcile/reconcile_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/mdm/reconcile/reconcile_test.go b/server/mdm/reconcile/reconcile_test.go index 2bf2366a1f5..a4673c695c2 100644 --- a/server/mdm/reconcile/reconcile_test.go +++ b/server/mdm/reconcile/reconcile_test.go @@ -101,7 +101,7 @@ func TestHandlerExcludeAny(t *testing.T) { t.Run("dynamic label created after host's last scan -> true (exclude)", func(t *testing.T) { labels := []fleet.MDMProfileLabelRef{ { - LabelID: new(uint(1)), + LabelID: func() *uint { v := uint(1); return &v }(), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeDynamic), }, @@ -111,7 +111,7 @@ func TestHandlerExcludeAny(t *testing.T) { t.Run("host vital label created after host's last scan -> false (include)", func(t *testing.T) { labels := []fleet.MDMProfileLabelRef{ { - LabelID: new(uint(1)), + LabelID: func() *uint { v := uint(1); return &v }(), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeHostVitals), }, @@ -121,7 +121,7 @@ func TestHandlerExcludeAny(t *testing.T) { t.Run("manual label created after host's last scan -> still false (include)", func(t *testing.T) { labels := []fleet.MDMProfileLabelRef{ { - LabelID: new(uint(1)), + LabelID: func() *uint { v := uint(1); return &v }(), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeManual), }, From bacf53af162d9277d3e11ed2183789112f04c65a Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:52 +0000 Subject: [PATCH 04/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/redis/keyprefix.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/server/datastore/redis/keyprefix.go b/server/datastore/redis/keyprefix.go index c606506d150..5002e7124ad 100644 --- a/server/datastore/redis/keyprefix.go +++ b/server/datastore/redis/keyprefix.go @@ -137,7 +137,11 @@ func twoKeys(args []interface{}, prefix string) { prefixOne(args, 1, prefix) } -// evalArgs: args = [script, numKeys, k1, ..., kN, arg1, ...]. +// evalArgs: args = [script, numKeys, k1, ..., kN, arg1, ...]. If numKeys is +// malformed (larger than the number of args actually supplied), that's a +// caller bug: fail closed and skip prefixing entirely rather than silently +// prefixing only some of the intended keys, which could leave later key args +// operating on another tenant's keyspace. func evalArgs(args []interface{}, prefix string) { if len(args) < 2 { return @@ -146,6 +150,9 @@ func evalArgs(args []interface{}, prefix string) { if n <= 0 { return } + if 2+n > len(args) { + return + } for i := 0; i < n; i++ { prefixOne(args, 2+i, prefix) } @@ -220,6 +227,21 @@ func sortArgs(args []interface{}, prefix string) { } } +// geoRadiusArgs: GEORADIUS/GEORADIUSBYMEMBER key ... [STORE dst] [STOREDIST dst]. +// Prefix the source key and any STORE/STOREDIST destination key(s) so results +// aren't written into another tenant's keyspace. +func geoRadiusArgs(args []interface{}, prefix string) { + if len(args) == 0 { + return + } + prefixOne(args, 0, prefix) + for i := 1; i < len(args)-1; i++ { + if strings.EqualFold(toString(args[i]), "STORE") || strings.EqualFold(toString(args[i]), "STOREDIST") { + prefixOne(args, i+1, prefix) + } + } +} + func toInt(v interface{}) int { switch x := v.(type) { case int: @@ -343,6 +365,10 @@ var specialCmds = map[string]prefixRule{ // SORT key ... [STORE dst] "SORT": sortArgs, + // GEORADIUS/GEORADIUSBYMEMBER key ... [STORE dst] [STOREDIST dst] + "GEORADIUS": geoRadiusArgs, + "GEORADIUSBYMEMBER": geoRadiusArgs, + // pub/sub channel commands — every arg is a channel "SUBSCRIBE": allArgs, "UNSUBSCRIBE": allArgs, From 286bde029a3db4774eec368dca9f558920e3c4f0 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:53 +0000 Subject: [PATCH 05/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/mysqlredis/host_cache_writes_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/datastore/mysqlredis/host_cache_writes_test.go b/server/datastore/mysqlredis/host_cache_writes_test.go index cc0046f0b7f..4e92502b6e3 100644 --- a/server/datastore/mysqlredis/host_cache_writes_test.go +++ b/server/datastore/mysqlredis/host_cache_writes_test.go @@ -95,7 +95,8 @@ func TestWritePathInvalidation(t *testing.T) { ds.UpdateHostRefetchCriticalQueriesUntilFunc = func(_ context.Context, _ uint, _ *time.Time) error { return nil } }, invoke: func(ctx context.Context, d *Datastore, id uint, _ string) error { - return d.UpdateHostRefetchCriticalQueriesUntil(ctx, id, new(time.Unix(1, 0))) + until := time.Unix(1, 0) + return d.UpdateHostRefetchCriticalQueriesUntil(ctx, id, &until) }, invoked: func(ds *mock.Store) bool { return ds.UpdateHostRefetchCriticalQueriesUntilFuncInvoked }, }, From 94a2212d549cf63b53469c4507ad13fb536d30ac Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:54 +0000 Subject: [PATCH 06/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/nanomdm/storage/mysql/pushcert.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/mdm/nanomdm/storage/mysql/pushcert.go b/server/mdm/nanomdm/storage/mysql/pushcert.go index 1a7bd63b58d..b154e8d6a20 100644 --- a/server/mdm/nanomdm/storage/mysql/pushcert.go +++ b/server/mdm/nanomdm/storage/mysql/pushcert.go @@ -55,8 +55,9 @@ ON DUPLICATE KEY UPDATE cert_pem = VALUES(cert_pem), key_pem = VALUES(key_pem), - push_certs.stale_token = push_certs.stale_token + 1;`, + stale_token = stale_token + 1;`, topic, pemCert, pemKey, ) return err } + From 31faecd164311bbfcd32412920d34040045abf2d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:55 +0000 Subject: [PATCH 07/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/profiles/android_appconfig_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/mdm/profiles/android_appconfig_test.go b/server/mdm/profiles/android_appconfig_test.go index d10ea4a739a..da1514fab34 100644 --- a/server/mdm/profiles/android_appconfig_test.go +++ b/server/mdm/profiles/android_appconfig_test.go @@ -7,6 +7,7 @@ import ( "github.com/fleetdm/fleet/v4/server/fleet" "github.com/fleetdm/fleet/v4/server/mock" + "github.com/fleetdm/fleet/v4/server/ptr" "github.com/stretchr/testify/require" ) @@ -139,7 +140,7 @@ func TestSubstituteFleetVarsInAndroidAppConfig(t *testing.T) { return []uint{42}, nil } ds.ScimUserByHostIDFunc = func(ctx context.Context, hostID uint) (*fleet.ScimUser, error) { - return &fleet.ScimUser{UserName: "jdoe@example.com", GivenName: new("John"), FamilyName: new("Doe")}, nil + return &fleet.ScimUser{UserName: "jdoe@example.com", GivenName: ptr.String("John"), FamilyName: ptr.String("Doe")}, nil } ds.ListHostDeviceMappingFunc = func(ctx context.Context, hostID uint) ([]*fleet.HostDeviceMapping, error) { return nil, nil @@ -157,7 +158,7 @@ func TestSubstituteFleetVarsInAndroidAppConfig(t *testing.T) { return []uint{42}, nil } ds.ScimUserByHostIDFunc = func(ctx context.Context, hostID uint) (*fleet.ScimUser, error) { - return &fleet.ScimUser{UserName: "jdoe@example.com", GivenName: new("John"), FamilyName: new("Doe")}, nil + return &fleet.ScimUser{UserName: "jdoe@example.com", GivenName: ptr.String("John"), FamilyName: ptr.String("Doe")}, nil } ds.ListHostDeviceMappingFunc = func(ctx context.Context, hostID uint) ([]*fleet.HostDeviceMapping, error) { return nil, nil From 8d9d98178d59ef0724b4be3f8eb98258eed4ac23 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:56 +0000 Subject: [PATCH 08/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/nanomdm/storage/mysql/certauth.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/mdm/nanomdm/storage/mysql/certauth.go b/server/mdm/nanomdm/storage/mysql/certauth.go index c64cb1bb696..8bb904f5e30 100644 --- a/server/mdm/nanomdm/storage/mysql/certauth.go +++ b/server/mdm/nanomdm/storage/mysql/certauth.go @@ -60,7 +60,7 @@ func (s *MySQLStorage) EnrollmentFromHash(ctx context.Context, hash string) (str var id string err := s.db.QueryRowContext( ctx, - `SELECT id FROM cert_auth_associations WHERE sha256 = ? LIMIT 1;`, + `SELECT id FROM nano_cert_auth_associations WHERE sha256 = ? LIMIT 1;`, hash, ).Scan(&id) if errors.Is(err, sql.ErrNoRows) { @@ -68,3 +68,4 @@ func (s *MySQLStorage) EnrollmentFromHash(ctx context.Context, hash string) (str } return id, err } + From 4495899db448bf9774afdee4532d173e88fd929d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:57 +0000 Subject: [PATCH 09/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/filesystem/software_installer.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/datastore/filesystem/software_installer.go b/server/datastore/filesystem/software_installer.go index 26c7a6fc514..7e22584a3c4 100644 --- a/server/datastore/filesystem/software_installer.go +++ b/server/datastore/filesystem/software_installer.go @@ -119,7 +119,8 @@ func (i *SoftwareInstallerStore) Cleanup(ctx context.Context, usedInstallerIDs [ info, err := de.Info() if err != nil { - return 0, ctxerr.Wrap(ctx, err, "get software installer modtime in filesystem store") + errs = append(errs, err) + continue } if info.ModTime().After(removeCreatedBefore) { continue From 31d568f7323f86fd41711cee3099742d69ec52ef Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:58 +0000 Subject: [PATCH 10/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../20241125150614_AddAppConfigWindowsMigrationEnabledField.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/datastore/mysql/migrations/tables/20241125150614_AddAppConfigWindowsMigrationEnabledField.go b/server/datastore/mysql/migrations/tables/20241125150614_AddAppConfigWindowsMigrationEnabledField.go index f069d072aae..204b42468e2 100644 --- a/server/datastore/mysql/migrations/tables/20241125150614_AddAppConfigWindowsMigrationEnabledField.go +++ b/server/datastore/mysql/migrations/tables/20241125150614_AddAppConfigWindowsMigrationEnabledField.go @@ -31,7 +31,8 @@ func Up_20241125150614(tx *sql.Tx) error { mdm, ok := config["mdm"] if !ok { - return errors.New("missing mdm section") + mdm = map[string]interface{}{} + config["mdm"] = mdm } mdmMap, ok := mdm.(map[string]interface{}) if !ok { From 60a18e52d74db78bfc3c03a89d73a1feddcb6cc1 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:44:59 +0000 Subject: [PATCH 11/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- infrastructure/loadtesting/terraform/infra/secrets.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/loadtesting/terraform/infra/secrets.tf b/infrastructure/loadtesting/terraform/infra/secrets.tf index 1171ff0156f..71ebe496e44 100644 --- a/infrastructure/loadtesting/terraform/infra/secrets.tf +++ b/infrastructure/loadtesting/terraform/infra/secrets.tf @@ -4,4 +4,4 @@ data "aws_secretsmanager_secret" "license" { data "aws_secretsmanager_secret_version" "enroll_secret" { secret_id = data.terraform_remote_state.shared.outputs.enroll_secret.id -} \ No newline at end of file +} From 6a9255ee7258c541d840f86829c591fb36524545 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:00 +0000 Subject: [PATCH 12/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../conditional_access_microsoft_proxy.go | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/server/service/conditional_access_microsoft_proxy/conditional_access_microsoft_proxy.go b/server/service/conditional_access_microsoft_proxy/conditional_access_microsoft_proxy.go index 0a64c90349c..53136e3a258 100644 --- a/server/service/conditional_access_microsoft_proxy/conditional_access_microsoft_proxy.go +++ b/server/service/conditional_access_microsoft_proxy/conditional_access_microsoft_proxy.go @@ -53,6 +53,7 @@ type CreateResponse struct { func (p *Proxy) Create(ctx context.Context, tenantID string) (*CreateResponse, error) { var createResponse CreateResponse if err := p.post( + ctx, "/api/v1/microsoft-compliance-partner", createRequest{TenantID: tenantID}, &createResponse, @@ -73,9 +74,13 @@ type GetResponse struct { // Get returns the integration settings. func (p *Proxy) Get(ctx context.Context, tenantID string, secret string) (*GetResponse, error) { var getResponse GetResponse + query := url.Values{} + query.Set("entraTenantId", tenantID) + query.Set("fleetServerSecret", secret) if err := p.get( + ctx, "/api/v1/microsoft-compliance-partner/settings", - fmt.Sprintf("entraTenantId=%s&fleetServerSecret=%s", tenantID, secret), + query.Encode(), &getResponse, ); err != nil { return nil, fmt.Errorf("get integration settings failed: %w", err) @@ -92,9 +97,13 @@ type DeleteResponse struct { // Returns a fleet.IsNotFound error if the integration doesn't exist. func (p *Proxy) Delete(ctx context.Context, tenantID string, secret string) (*DeleteResponse, error) { var deleteResponse DeleteResponse + query := url.Values{} + query.Set("entraTenantId", tenantID) + query.Set("fleetServerSecret", secret) if err := p.delete( + ctx, "/api/v1/microsoft-compliance-partner", - fmt.Sprintf("entraTenantId=%s&fleetServerSecret=%s", tenantID, secret), + query.Encode(), &deleteResponse, ); err != nil { return nil, fmt.Errorf("delete integration failed: %w", err) @@ -137,6 +146,7 @@ func (p *Proxy) SetComplianceStatus( ) (*SetComplianceStatusResponse, error) { var setComplianceStatusResponse SetComplianceStatusResponse if err := p.post( + ctx, "/api/v1/microsoft-compliance-partner/device", setComplianceStatusRequest{ TenantID: tenantID, @@ -179,9 +189,14 @@ func (p *Proxy) GetMessageStatus( messageID string, ) (*GetMessageStatusResponse, error) { var getMessageStatusResponse GetMessageStatusResponse + query := url.Values{} + query.Set("entraTenantId", tenantID) + query.Set("fleetServerSecret", secret) + query.Set("messageId", messageID) if err := p.get( + ctx, "/api/v1/microsoft-compliance-partner/device/message", - fmt.Sprintf("entraTenantId=%s&fleetServerSecret=%s&messageId=%s", tenantID, secret, messageID), + query.Encode(), &getMessageStatusResponse, ); err != nil { return nil, fmt.Errorf("get message status response failed: %w", err) @@ -189,12 +204,12 @@ func (p *Proxy) GetMessageStatus( return &getMessageStatusResponse, nil } -func (p *Proxy) post(path string, request interface{}, response interface{}) error { +func (p *Proxy) post(ctx context.Context, path string, request interface{}, response interface{}) error { b, err := json.Marshal(request) if err != nil { return fmt.Errorf("marshal request: %w", err) } - postRequest, err := http.NewRequest("POST", p.uri+path, nil) + postRequest, err := http.NewRequestWithContext(ctx, "POST", p.uri+path, bytes.NewBuffer(b)) if err != nil { return fmt.Errorf("post create request: %w", err) } @@ -202,7 +217,6 @@ func (p *Proxy) post(path string, request interface{}, response interface{}) err return fmt.Errorf("post set headers: %w", err) } postRequest.Header.Add("Content-Type", "application/json") - postRequest.Body = io.NopCloser(bytes.NewBuffer(b)) resp, err := p.c.Do(postRequest) if err != nil { return fmt.Errorf("post request: %w", err) @@ -221,12 +235,12 @@ func (p *Proxy) post(path string, request interface{}, response interface{}) err return nil } -func (p *Proxy) get(path string, query string, response interface{}) error { +func (p *Proxy) get(ctx context.Context, path string, query string, response interface{}) error { getURL := p.uri + path if query != "" { - getURL += "?" + url.PathEscape(query) + getURL += "?" + query } - getRequest, err := http.NewRequest("GET", getURL, nil) + getRequest, err := http.NewRequestWithContext(ctx, "GET", getURL, nil) if err != nil { return fmt.Errorf("get create request: %w", err) } @@ -251,12 +265,12 @@ func (p *Proxy) get(path string, query string, response interface{}) error { return nil } -func (p *Proxy) delete(path string, query string, response interface{}) error { +func (p *Proxy) delete(ctx context.Context, path string, query string, response interface{}) error { deleteURL := p.uri + path if query != "" { - deleteURL += "?" + url.PathEscape(query) + deleteURL += "?" + query } - deleteRequest, err := http.NewRequest("DELETE", deleteURL, nil) + deleteRequest, err := http.NewRequestWithContext(ctx, "DELETE", deleteURL, nil) if err != nil { return fmt.Errorf("delete create request: %w", err) } From eb9e71542cf08c79b659aaeeb141466233fa24c5 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:02 +0000 Subject: [PATCH 13/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../tables/20240314085226_AddCalendarEventTables.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/20240314085226_AddCalendarEventTables.go b/server/datastore/mysql/migrations/tables/20240314085226_AddCalendarEventTables.go index 8f8871d1054..ac0a136562b 100644 --- a/server/datastore/mysql/migrations/tables/20240314085226_AddCalendarEventTables.go +++ b/server/datastore/mysql/migrations/tables/20240314085226_AddCalendarEventTables.go @@ -22,7 +22,7 @@ func Up_20240314085226(tx *sql.Tx) error { event JSON NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY idx_one_calendar_event_per_email (email) ) DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; @@ -30,6 +30,11 @@ func Up_20240314085226(tx *sql.Tx) error { return fmt.Errorf("create calendar_events table: %w", err) } + // Note: host_id intentionally does not have a foreign key constraint to the + // hosts table. Hosts can be deleted and re-enrolled (churn/replacement), and + // host_calendar_events rows referencing a deleted host_id are cleaned up by + // application logic rather than a DB-level cascade, to avoid tying calendar + // event bookkeeping lifecycle directly to host deletion. if _, err := tx.Exec(` CREATE TABLE IF NOT EXISTS host_calendar_events ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, @@ -38,7 +43,7 @@ func Up_20240314085226(tx *sql.Tx) error { webhook_status TINYINT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP NOT NULL NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY idx_one_calendar_event_per_host (host_id), FOREIGN KEY (calendar_event_id) REFERENCES calendar_events(id) ON DELETE CASCADE From c35d992ea244b1a2e7fa452ddac0b56415128dee Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:03 +0000 Subject: [PATCH 14/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- ee/tools/puppet/fleetdm/lib/puppet/util/fleet_client.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ee/tools/puppet/fleetdm/lib/puppet/util/fleet_client.rb b/ee/tools/puppet/fleetdm/lib/puppet/util/fleet_client.rb index 1eb10295433..542cb80003c 100644 --- a/ee/tools/puppet/fleetdm/lib/puppet/util/fleet_client.rb +++ b/ee/tools/puppet/fleetdm/lib/puppet/util/fleet_client.rb @@ -175,7 +175,7 @@ def req(method: :get, path: '', body: nil, headers: {}, cached: false, environme end end rescue => e - out['error'] = e + out['error'] = e.message end out @@ -196,7 +196,7 @@ def parse_response(response) message = 'server returned a non-ok status code without an error' if response.body - body = JSON.parse(response.body) + body = out['body'] message = body['message'] unless body['errors'].nil? From cec4ffc6c597ff21e90726d3872ccf03e9c21c00 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:04 +0000 Subject: [PATCH 15/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- tools/tuf/test/create_repository.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/tuf/test/create_repository.sh b/tools/tuf/test/create_repository.sh index 8aa777eb6f2..e07aae58f60 100755 --- a/tools/tuf/test/create_repository.sh +++ b/tools/tuf/test/create_repository.sh @@ -317,7 +317,6 @@ for system in $SYSTEMS; do do extensionName=$(basename $extension) extensionName=$(echo "$extensionName" | cut -d'.' -f1) - echo "$FILE" | cut -d'.' -f2 ./build/fleetctl updates add \ --path $TUF_PATH \ --target $extension \ @@ -333,7 +332,6 @@ for system in $SYSTEMS; do do extensionName=$(basename $extension) extensionName=$(echo "$extensionName" | cut -d'.' -f1) - echo "$FILE" | cut -d'.' -f2 ./build/fleetctl updates add \ --path $TUF_PATH \ --target $extension \ @@ -343,3 +341,4 @@ for system in $SYSTEMS; do done fi done + From dba4e2441c22e64f22f31d02b9cdfd671842c704 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:05 +0000 Subject: [PATCH 16/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../SoftwareVulnerabilities/SoftwareVulnerabilities.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/pages/SoftwarePage/SoftwareVulnerabilities/SoftwareVulnerabilities.tsx b/frontend/pages/SoftwarePage/SoftwareVulnerabilities/SoftwareVulnerabilities.tsx index b4974e3672e..e7fbf21d20e 100644 --- a/frontend/pages/SoftwarePage/SoftwareVulnerabilities/SoftwareVulnerabilities.tsx +++ b/frontend/pages/SoftwarePage/SoftwareVulnerabilities/SoftwareVulnerabilities.tsx @@ -178,7 +178,7 @@ const SoftwareVulnerabilities = ({ }; // Handle 400 response which is an invalid CVE format - if (error.status === 400) { + if (error.response?.status === 400) { if ( error?.data?.errors && error.data.errors[0].reason.includes( @@ -198,7 +198,7 @@ const SoftwareVulnerabilities = ({ } // Handle 404 response which is BE validated CVE string but not a known CVE - } else if (error.status === 404) { + } else if (error.response?.status === 404) { if ( error?.data?.errors && (error.data.errors[0].reason.includes("This is not a known CVE.") || @@ -234,7 +234,7 @@ const SoftwareVulnerabilities = ({ if (isExactMatchQuery) { refetchExactMatch(); } - }, [queryParams.exploit, isExactMatchQuery]); + }, [queryParams.exploit, isExactMatchQuery, refetchExactMatch]); // !tableData is used to show the Spinner only on the first render. // This prevents the Spinner from flashing on every data refresh, noticable From 421b682d74c24be09d582d909018cb4d6fdfe8ac Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:06 +0000 Subject: [PATCH 17/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../migrations/tables/20240826160025_AddRemovedToInstalls.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/20240826160025_AddRemovedToInstalls.go b/server/datastore/mysql/migrations/tables/20240826160025_AddRemovedToInstalls.go index 6314d654854..37e8d27c511 100644 --- a/server/datastore/mysql/migrations/tables/20240826160025_AddRemovedToInstalls.go +++ b/server/datastore/mysql/migrations/tables/20240826160025_AddRemovedToInstalls.go @@ -56,7 +56,7 @@ func Up_20240826160025(tx *sql.Tx) error { return fmt.Errorf("failed to find host_software_installs to remove: %w", err) } if len(ids) > 0 { - stmt, args, err := sqlx.In("UPDATE host_software_installs SET removed = 1 WHERE id IN (?)", ids) + stmt, args, err := sqlx.In("UPDATE host_software_installs SET removed = 1 WHERE id IN (?)", ids) if err != nil { return fmt.Errorf("failed to expand slice value for host_software_installs: %w", err) } @@ -87,7 +87,7 @@ func Up_20240826160025(tx *sql.Tx) error { return fmt.Errorf("failed to find host_vpp_software_installs to remove: %w", err) } if len(vppIDs) > 0 { - stmt, args, err := sqlx.In("UPDATE host_vpp_software_installs SET removed = 1 WHERE id IN (?)", vppIDs) + stmt, args, err := sqlx.In("UPDATE host_vpp_software_installs SET removed = 1 WHERE id IN (?)", vppIDs) if err != nil { return fmt.Errorf("failed to expand slice value for host_vpp_software_installs: %w", err) } From 5f02562cf1d1316ad721d3ee2e4f53706a48288a Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:07 +0000 Subject: [PATCH 18/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/errorstore/errors.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/errorstore/errors.go b/server/errorstore/errors.go index a730de6583f..1d617775636 100644 --- a/server/errorstore/errors.go +++ b/server/errorstore/errors.go @@ -317,19 +317,22 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { flush, err = strconv.ParseBool(opts.Get("flush")) if err != nil { + h.logger.ErrorContext(r.Context(), "failed to parse flush query param", "err", err) w.WriteHeader(http.StatusBadRequest) return } } - errors, err := h.Retrieve(flush) + storedErrors, err := h.Retrieve(flush) if err != nil { + h.logger.ErrorContext(r.Context(), "failed to retrieve stored errors", "err", err) w.WriteHeader(http.StatusInternalServerError) return } - bytes, err := json.Marshal(errors) + bytes, err := json.Marshal(storedErrors) if err != nil { + h.logger.ErrorContext(r.Context(), "failed to marshal stored errors", "err", err) w.WriteHeader(http.StatusInternalServerError) return } From 8da2947a05a59bdcb927cc7f68c943478eb363ec Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:08 +0000 Subject: [PATCH 19/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/scep/depot/file/depot.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/mdm/scep/depot/file/depot.go b/server/mdm/scep/depot/file/depot.go index a4a9874f18f..55b980956bc 100644 --- a/server/mdm/scep/depot/file/depot.go +++ b/server/mdm/scep/depot/file/depot.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "io/ioutil" + "log" "math/big" "os" "path/filepath" @@ -230,13 +231,12 @@ func (d *fileDepot) HasCN(_ string, allowTime int, cert *x509.Certificate, revok addDB.WriteString(line + "\n") } } - file.Close() for key, value := range candidates { if value == "no" { return false, errors.New("DN " + dn + " already exists") } if revokeOldCertificate { - fmt.Println("Revoking certificate with serial " + key + " from DB. Recreation of CRL needed.") + log.Printf("Revoking certificate with serial %s from DB. Recreation of CRL needed.", key) entries := strings.Split(value, "\t") addDB.WriteString("R\t" + entries[1] + "\t" + makeOpenSSLTime(time.Now()) + "\t" + strings.ToUpper(entries[3]) + "\t" + entries[4] + "\t" + entries[5] + "\n") } @@ -249,6 +249,7 @@ func (d *fileDepot) HasCN(_ string, allowTime int, cert *x509.Certificate, revok if err != nil { return false, err } + defer file.Close() if _, err := file.Write(addDB.Bytes()); err != nil { return false, err } From f56465ff29de48b130d75b661d7cc5097e414c73 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:09 +0000 Subject: [PATCH 20/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../datastore/mysql/migrations/tables/migration.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/migration.go b/server/datastore/mysql/migrations/tables/migration.go index b8724086bdf..2b96a366720 100644 --- a/server/datastore/mysql/migrations/tables/migration.go +++ b/server/datastore/mysql/migrations/tables/migration.go @@ -60,7 +60,7 @@ func incrementalMigrationStep(count getTotalCountFn, execute executeWithProgress // Every five seconds, echo the % progress of the executor // Since we output once the migration step is complete, we need an extra channel to indicate when both the step - // and the "step complete" output are com0plete + // and the "step complete" output are complete stepComplete := make(chan struct{}) outputComplete := make(chan struct{}) go func() { @@ -83,10 +83,12 @@ func incrementalMigrationStep(count getTotalCountFn, execute executeWithProgress } }() - err = execute(tx, func() { - atomicCurrent.Add(1) - }) - close(stepComplete) + func() { + defer close(stepComplete) + err = execute(tx, func() { + atomicCurrent.Add(1) + }) + }() <-outputComplete // Wait for the goroutine to complete return err } @@ -258,3 +260,4 @@ func updateAppConfigJSON(tx *sql.Tx, fn func(config *fleet.AppConfig) error) err return nil } + From b75a9b8c9b0743a7c1983b2f5a761cdefc9e40b6 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:10 +0000 Subject: [PATCH 21/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/service/endpoint_setup.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/service/endpoint_setup.go b/server/service/endpoint_setup.go index 75927928f20..95d722521a6 100644 --- a/server/service/endpoint_setup.go +++ b/server/service/endpoint_setup.go @@ -157,9 +157,15 @@ func ApplyStarterLibrary( if err != nil { return fmt.Errorf("failed to create fleetctl config: %w", err) } + if err := os.Chmod(configFile.Name(), 0o600); err != nil { + configFile.Close() + return fmt.Errorf("failed to set permissions on fleetctl config: %w", err) + } fmt.Fprintf(configFile, "contexts:\n default:\n address: %s\n tls-skip-verify: true\n token: %s\n", serverURL, token) - configFile.Close() + if err := configFile.Close(); err != nil { + return fmt.Errorf("failed to close fleetctl config: %w", err) + } // Build the gitops args: global config first, then team configs (premium only). args := []string{"gitops", "--config", configFile.Name(), "-f", filepath.Join(outDir, "default.yml")} From fe88ceb135fd53e63afbe52d44046cc9fd6de2e6 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:11 +0000 Subject: [PATCH 22/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- frontend/components/LiveQuery/SelectTargets.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/components/LiveQuery/SelectTargets.tsx b/frontend/components/LiveQuery/SelectTargets.tsx index c6e547b936c..1ee075f0692 100644 --- a/frontend/components/LiveQuery/SelectTargets.tsx +++ b/frontend/components/LiveQuery/SelectTargets.tsx @@ -290,6 +290,8 @@ const SelectTargets = ({ // this effect every render, which dispatches a context update and causes an // infinite render loop ("Maximum update depth exceeded"), freezing the // Select targets UI (e.g. the X to remove a host stops responding). + // TODO: memoize `setSelectedTargets` at its source in QueryContext so this + // effect can safely include it in its dependency array. // eslint-disable-next-line react-hooks/exhaustive-deps }, [targetedHosts, targetedLabels, targetedTeams]); @@ -318,7 +320,9 @@ const SelectTargets = ({ // if the target was previously selected, we want to remove it now let newTargets = prevTargets.filter((t) => t.id !== selectedEntity.id); // if the length remains the same, the target was not previously selected so we want to add it now - prevTargets.length === newTargets.length && newTargets.push(selectedEntity); + if (prevTargets.length === newTargets.length) { + newTargets.push(selectedEntity); + } // Logic when to deselect/select "all hosts" when using more granulated filters // If "all hosts" is selected From 41ba39680888e19ec85ad717d7dc630619b42f0c Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:12 +0000 Subject: [PATCH 23/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/mysql/software_title_icons.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/server/datastore/mysql/software_title_icons.go b/server/datastore/mysql/software_title_icons.go index 2f96e7086c7..aa53dbf2888 100644 --- a/server/datastore/mysql/software_title_icons.go +++ b/server/datastore/mysql/software_title_icons.go @@ -183,8 +183,9 @@ func (ds *Datastore) ActivityDetailsForSoftwareTitleIcon(ctx context.Context, te Exclude bool `db:"exclude"` RequireAll bool `db:"require_all"` } - var labels []ActivitySoftwareLabel + var allLabels []ActivitySoftwareLabel if details.SoftwareInstallerID != nil { + var labels []ActivitySoftwareLabel labelQuery := ` SELECT labels.id AS id, @@ -198,8 +199,10 @@ func (ds *Datastore) ActivityDetailsForSoftwareTitleIcon(ctx context.Context, te if err := sqlx.SelectContext(ctx, ds.reader(ctx), &labels, labelQuery, details.SoftwareInstallerID); err != nil { return fleet.DetailsForSoftwareIconActivity{}, ctxerr.Wrap(ctx, err, "getting labels for software title icon") } + allLabels = append(allLabels, labels...) } - if details.AdamID != nil { + if details.AdamID != nil && details.VPPAppTeamID != nil { + var labels []ActivitySoftwareLabel labelQuery := ` SELECT labels.id AS id, @@ -213,8 +216,10 @@ func (ds *Datastore) ActivityDetailsForSoftwareTitleIcon(ctx context.Context, te if err := sqlx.SelectContext(ctx, ds.reader(ctx), &labels, labelQuery, details.VPPAppTeamID); err != nil { return fleet.DetailsForSoftwareIconActivity{}, ctxerr.Wrap(ctx, err, "getting labels for software title icon") } + allLabels = append(allLabels, labels...) } if details.InHouseAppID != nil { + var labels []ActivitySoftwareLabel labelQuery := ` SELECT labels.id AS id, @@ -228,9 +233,10 @@ func (ds *Datastore) ActivityDetailsForSoftwareTitleIcon(ctx context.Context, te if err := sqlx.SelectContext(ctx, ds.reader(ctx), &labels, labelQuery, details.InHouseAppID); err != nil { return fleet.DetailsForSoftwareIconActivity{}, ctxerr.Wrap(ctx, err, "getting labels for software title icon") } + allLabels = append(allLabels, labels...) } - for _, l := range labels { + for _, l := range allLabels { switch { case l.Exclude && !l.RequireAll: details.LabelsExcludeAny = append(details.LabelsExcludeAny, fleet.ActivitySoftwareLabel{ From f22fa048098604ff4ded3bf2d26c5a90271e8ea8 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:13 +0000 Subject: [PATCH 24/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../tables/20260518194422_AddEncodingTypeToHostSCDData.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/datastore/mysql/migrations/tables/20260518194422_AddEncodingTypeToHostSCDData.go b/server/datastore/mysql/migrations/tables/20260518194422_AddEncodingTypeToHostSCDData.go index 12f6df131ae..dded977df57 100644 --- a/server/datastore/mysql/migrations/tables/20260518194422_AddEncodingTypeToHostSCDData.go +++ b/server/datastore/mysql/migrations/tables/20260518194422_AddEncodingTypeToHostSCDData.go @@ -9,7 +9,7 @@ func init() { MigrationClient.AddMigration(Up_20260518194422, Down_20260518194422) } -// Up_20260514220719 adds the encoding_type column that discriminates between +// Up_20260518194422 adds the encoding_type column that discriminates between // the legacy dense bitmap format (encoding_type = 0) and the new roaring // bitmap format (encoding_type = 1). ALGORITHM=INSTANT is a metadata-only // change on MySQL 8.0+; existing rows are not rewritten and read back with From 11a4a23e9fa6c03f6d18efc1827a034f67608f3d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:14 +0000 Subject: [PATCH 25/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- website/api/helpers/strings/to-html-email.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/api/helpers/strings/to-html-email.js b/website/api/helpers/strings/to-html-email.js index 59aa7e999fb..dc5e4f514b6 100644 --- a/website/api/helpers/strings/to-html-email.js +++ b/website/api/helpers/strings/to-html-email.js @@ -112,7 +112,7 @@ module.exports = { // For emphasized text customRenderer.em = function(textHTML) { - return `${textHTML}`; }; // For inline codespans @@ -162,3 +162,4 @@ module.exports = { }; + From 270eb8a0ba8122b911dd7941990807ea9bb73c82 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:15 +0000 Subject: [PATCH 26/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- frontend/pages/DashboardPage/cards/Software/Software.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/DashboardPage/cards/Software/Software.tsx b/frontend/pages/DashboardPage/cards/Software/Software.tsx index 82660ca26c8..bf238b59c27 100644 --- a/frontend/pages/DashboardPage/cards/Software/Software.tsx +++ b/frontend/pages/DashboardPage/cards/Software/Software.tsx @@ -97,7 +97,7 @@ const Software = ({ data={(isSoftwareEnabled && software?.software) || []} isLoading={isSoftwareFetching} pageIndex={softwarePageIndex} - defaultSortHeader={SOFTWARE_DEFAULT_SORT_DIRECTION} + defaultSortHeader={SOFTWARE_DEFAULT_SORT_HEADER} defaultSortDirection={SOFTWARE_DEFAULT_SORT_DIRECTION} resultsTitle="software" emptyComponent={() => } From 3375f7761ef13a9e24bbfbfdc5841600fa2392af Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:16 +0000 Subject: [PATCH 27/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- orbit/pkg/table/app_sso_platform/app_sso_platform_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orbit/pkg/table/app_sso_platform/app_sso_platform_darwin.go b/orbit/pkg/table/app_sso_platform/app_sso_platform_darwin.go index ad079368287..d6d97c6bc82 100644 --- a/orbit/pkg/table/app_sso_platform/app_sso_platform_darwin.go +++ b/orbit/pkg/table/app_sso_platform/app_sso_platform_darwin.go @@ -232,7 +232,7 @@ func parseAppSSOPlatformCommandOutput(output []byte, expectedExtensionIdentifier } realm, ok := realm_.(string) if !ok { - return nil, fmt.Errorf("unexpected type for \"realm\" key in \"kerberosStatus\": %T", err) + return nil, fmt.Errorf("unexpected type for \"realm\" key in \"kerberosStatus\": %T", realm_) } upn_, ok := userConfig.KerberosStatus[0]["upn"] if !ok { From c3fcb765c0865be7d2bae0cb019fefad105acd99 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:18 +0000 Subject: [PATCH 28/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/mysql/conditional_access_microsoft.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/datastore/mysql/conditional_access_microsoft.go b/server/datastore/mysql/conditional_access_microsoft.go index a4367f62d95..ee3d5dff743 100644 --- a/server/datastore/mysql/conditional_access_microsoft.go +++ b/server/datastore/mysql/conditional_access_microsoft.go @@ -93,6 +93,7 @@ func (ds *Datastore) LoadHostConditionalAccessStatus(ctx context.Context, hostID if errors.Is(err, sql.ErrNoRows) { return nil, ctxerr.Wrap(ctx, notFound("HostConditionalAccessStatus").WithID(hostID)) } + return nil, ctxerr.Wrap(ctx, err, "getting host conditional access status") } hostConditionalAccessStatus.OSVersion = strings.TrimPrefix(hostConditionalAccessStatus.OSVersion, "macOS ") if strings.HasPrefix(hostConditionalAccessStatus.OSVersion, "Windows") { From 1f79c0ff4265e037c9b83b3b1437c5de395c7872 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:19 +0000 Subject: [PATCH 29/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/nanodep/cmd/deptokens/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/mdm/nanodep/cmd/deptokens/main.go b/server/mdm/nanodep/cmd/deptokens/main.go index a7775f2ecd5..ef4a8c91668 100644 --- a/server/mdm/nanodep/cmd/deptokens/main.go +++ b/server/mdm/nanodep/cmd/deptokens/main.go @@ -83,6 +83,9 @@ func encodeEncryptedKeyPEM(key *rsa.PrivateKey, password string) ([]byte, error) // decrypting it with password. func decodeEncryptedKeyPEM(pemBytes []byte, password string) (*rsa.PrivateKey, error) { block, _ := pem.Decode(pemBytes) + if block == nil { + return nil, errors.New("failed to decode PEM block") + } if block.Type != "RSA PRIVATE KEY" { return nil, errors.New("PEM type is not RSA PRIVATE KEY") } From 0541ce407b2c496d0485b0ab5ddbe9a744d648ea Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:20 +0000 Subject: [PATCH 30/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../external_refs/cisco_jabber_version_transformer.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ee/maintained-apps/ingesters/homebrew/external_refs/cisco_jabber_version_transformer.go b/ee/maintained-apps/ingesters/homebrew/external_refs/cisco_jabber_version_transformer.go index be81febcc2b..355600d065c 100644 --- a/ee/maintained-apps/ingesters/homebrew/external_refs/cisco_jabber_version_transformer.go +++ b/ee/maintained-apps/ingesters/homebrew/external_refs/cisco_jabber_version_transformer.go @@ -4,9 +4,10 @@ import ( maintained_apps "github.com/fleetdm/fleet/v4/ee/maintained-apps" ) -// CiscoJabberVersionTransformer sets the version to "15.2.0" which matches what osquery reports. -// Homebrew reports a build number (e.g., "20251027035315") instead of the app version (e.g., "15.2.0"). +// CiscoJabberVersionTransformer sets the version to "15.2.1" which matches what osquery reports. +// Homebrew reports a build number (e.g., "20251027035315") instead of the app version (e.g., "15.2.1"). func CiscoJabberVersionTransformer(app *maintained_apps.FMAManifestApp) (*maintained_apps.FMAManifestApp, error) { app.Version = "15.2.1" return app, nil } + From dfeff80be8f9d65139b94723674ffcb2e083f761 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:21 +0000 Subject: [PATCH 31/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/datastore/mysql/host_identity_scep.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/datastore/mysql/host_identity_scep.go b/server/datastore/mysql/host_identity_scep.go index b261a8581cd..813376d70f2 100644 --- a/server/datastore/mysql/host_identity_scep.go +++ b/server/datastore/mysql/host_identity_scep.go @@ -19,12 +19,12 @@ import ( func (ds *Datastore) GetHostIdentityCertBySerialNumber(ctx context.Context, serialNumber uint64) (*types.HostIdentityCertificate, error) { var hostIdentityCert types.HostIdentityCertificate - err := sqlx.GetContext(ctx, ds.reader(ctx), &hostIdentityCert, fmt.Sprintf(` + err := sqlx.GetContext(ctx, ds.reader(ctx), &hostIdentityCert, ` SELECT serial, host_id, name, not_valid_after, public_key_raw FROM host_identity_scep_certificates - WHERE serial = %d + WHERE serial = ? AND not_valid_after > NOW() - AND revoked = 0`, serialNumber)) + AND revoked = 0`, serialNumber) switch { case errors.Is(err, sql.ErrNoRows): return nil, notFound("host identity certificate") From 8222f0a5ff108e8c798590ef2bfb4f423385b040 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:22 +0000 Subject: [PATCH 32/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- frontend/pages/hosts/details/cards/User/helpers.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/pages/hosts/details/cards/User/helpers.tsx b/frontend/pages/hosts/details/cards/User/helpers.tsx index 9f5a6c6c7c8..3a073dc359a 100644 --- a/frontend/pages/hosts/details/cards/User/helpers.tsx +++ b/frontend/pages/hosts/details/cards/User/helpers.tsx @@ -38,7 +38,7 @@ export const generateGroupsValues = (endUsers: IHostEndUser[]) => { return []; } - return endUser.idp_groups.sort((a, b) => { + return [...endUser.idp_groups].sort((a, b) => { return a.localeCompare(b); }); }; @@ -106,3 +106,4 @@ export const generateGroupsTipContent = (endUsers: IHostEndUser[]) => { return null; }; + From e82bd17e65e2feb14010c690e262897aa59aba4d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:23 +0000 Subject: [PATCH 33/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../components/ProfileUploader/helpers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/ManageControlsPage/OSSettings/cards/ConfigurationProfiles/components/ProfileUploader/helpers.tsx b/frontend/pages/ManageControlsPage/OSSettings/cards/ConfigurationProfiles/components/ProfileUploader/helpers.tsx index 6a28479cf4c..6ddd7150346 100644 --- a/frontend/pages/ManageControlsPage/OSSettings/cards/ConfigurationProfiles/components/ProfileUploader/helpers.tsx +++ b/frontend/pages/ManageControlsPage/OSSettings/cards/ConfigurationProfiles/components/ProfileUploader/helpers.tsx @@ -94,7 +94,7 @@ const generateGenericLearnMoreErrMsg = (errMsg: string) => { */ // eslint-disable-next-line import/prefer-default-export export const getErrorMessage = (err: AxiosResponse) => { - const apiReason = err?.data?.errors?.[0]?.reason; + const apiReason = err?.data?.errors?.[0]?.reason ?? ""; if (apiReason.includes("should include valid JSON")) { return "Couldn't add. The profile should include valid JSON."; From 70fce183d0f8cd8179a1e87fe9dec418bf271ed3 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:24 +0000 Subject: [PATCH 34/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- ...103700_AddAndroidApplicationIDToSoftware_test.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/20251015103700_AddAndroidApplicationIDToSoftware_test.go b/server/datastore/mysql/migrations/tables/20251015103700_AddAndroidApplicationIDToSoftware_test.go index 27f15dc01da..0f300d5261c 100644 --- a/server/datastore/mysql/migrations/tables/20251015103700_AddAndroidApplicationIDToSoftware_test.go +++ b/server/datastore/mysql/migrations/tables/20251015103700_AddAndroidApplicationIDToSoftware_test.go @@ -54,21 +54,18 @@ func TestUp_20251015103700(t *testing.T) { var title fleet.SoftwareTitle err := db.Get(&title, "SELECT id, name, source, extension_for, application_id, bundle_identifier FROM software_titles WHERE id = ?", tt.titleID) require.NoError(t, err) - switch { - case tt.expectedBundleID == nil: + if tt.expectedBundleID == nil { require.Nil(t, title.BundleIdentifier) - - case tt.expectedBundleID != nil: + } else { require.NotNil(t, tt.expectedBundleID) assert.Equal(t, *tt.expectedBundleID, *title.BundleIdentifier) + } - case tt.expectedApplicationID == nil: + if tt.expectedApplicationID == nil { require.Nil(t, title.ApplicationID) - - case tt.expectedApplicationID != nil: + } else { require.NotNil(t, title.ApplicationID) assert.Equal(t, tt.expectedApplicationID, title.ApplicationID) - } var gotUniqueID string From 688ac1fb993e09b29768b9a4fc18ce16ff5d14a1 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:25 +0000 Subject: [PATCH 35/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/nanomdm/http/api/api.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/mdm/nanomdm/http/api/api.go b/server/mdm/nanomdm/http/api/api.go index 7954331dbbc..0f66fead18c 100644 --- a/server/mdm/nanomdm/http/api/api.go +++ b/server/mdm/nanomdm/http/api/api.go @@ -280,6 +280,7 @@ func readPEMCertAndKey(input []byte) (cert []byte, key []byte, err error) { // if the PEM blocks are mushed together with no newline then add one input = bytes.ReplaceAll(input, []byte("----------"), []byte("-----\n-----")) var block *pem.Block +loop: for { block, input = pem.Decode(input) if block == nil { @@ -291,7 +292,7 @@ func readPEMCertAndKey(input []byte) (cert []byte, key []byte, err error) { case block.Type == "PRIVATE KEY" || strings.HasSuffix(block.Type, " PRIVATE KEY"): if x509.IsEncryptedPEMBlock(block) { err = errors.New("private key PEM appears to be encrypted") - break + break loop } key = pem.EncodeToMemory(block) default: From 2025adbcb7e63c4ec0a1c520ceaa036307a9a790 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:27 +0000 Subject: [PATCH 36/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/nanomdm/storage/allmulti/certauth.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/server/mdm/nanomdm/storage/allmulti/certauth.go b/server/mdm/nanomdm/storage/allmulti/certauth.go index 3fcec098b27..23b716d3125 100644 --- a/server/mdm/nanomdm/storage/allmulti/certauth.go +++ b/server/mdm/nanomdm/storage/allmulti/certauth.go @@ -12,21 +12,24 @@ func (ms *MultiAllStorage) HasCertHash(r *mdm.Request, hash string) (bool, error val, err := ms.execStores(r.Context, func(s storage.AllStorage) (interface{}, error) { return s.HasCertHash(r, hash) }) - return val.(bool), err + b, _ := val.(bool) + return b, err } func (ms *MultiAllStorage) EnrollmentHasCertHash(r *mdm.Request, hash string) (bool, error) { val, err := ms.execStores(r.Context, func(s storage.AllStorage) (interface{}, error) { return s.EnrollmentHasCertHash(r, hash) }) - return val.(bool), err + b, _ := val.(bool) + return b, err } func (ms *MultiAllStorage) IsCertHashAssociated(r *mdm.Request, hash string) (bool, error) { val, err := ms.execStores(r.Context, func(s storage.AllStorage) (interface{}, error) { return s.IsCertHashAssociated(r, hash) }) - return val.(bool), err + b, _ := val.(bool) + return b, err } func (ms *MultiAllStorage) AssociateCertHash(r *mdm.Request, hash string, certNotValidAfter time.Time) error { @@ -40,5 +43,7 @@ func (ms *MultiAllStorage) EnrollmentFromHash(ctx context.Context, hash string) val, err := ms.execStores(ctx, func(s storage.AllStorage) (interface{}, error) { return s.EnrollmentFromHash(ctx, hash) }) - return val.(string), err + str, _ := val.(string) + return str, err } + From 68555caffd5cbbefc961ce3bad9c45bf1fcd0eea Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:28 +0000 Subject: [PATCH 37/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- .../tables/20241210140021_AddErrorsToCronStatsTable.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/20241210140021_AddErrorsToCronStatsTable.go b/server/datastore/mysql/migrations/tables/20241210140021_AddErrorsToCronStatsTable.go index ee699ebe18f..c73b0659bba 100644 --- a/server/datastore/mysql/migrations/tables/20241210140021_AddErrorsToCronStatsTable.go +++ b/server/datastore/mysql/migrations/tables/20241210140021_AddErrorsToCronStatsTable.go @@ -6,10 +6,10 @@ import ( ) func init() { - MigrationClient.AddMigration(Up_20241126140021, Down_20241126140021) + MigrationClient.AddMigration(Up_20241210140021, Down_20241210140021) } -func Up_20241126140021(tx *sql.Tx) error { +func Up_20241210140021(tx *sql.Tx) error { // Idempotent migration. // Add columns if !columnExists(tx, "cron_stats", "errors") { @@ -21,6 +21,7 @@ func Up_20241126140021(tx *sql.Tx) error { return nil } -func Down_20241126140021(tx *sql.Tx) error { +func Down_20241210140021(tx *sql.Tx) error { return nil } + From 29650b1e52426655de5542832e88a0a4189e7035 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:29 +0000 Subject: [PATCH 38/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- server/mdm/apple/mobileconfig/mobileconfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mdm/apple/mobileconfig/mobileconfig.go b/server/mdm/apple/mobileconfig/mobileconfig.go index 2dab9cf1e90..e4832a9b225 100644 --- a/server/mdm/apple/mobileconfig/mobileconfig.go +++ b/server/mdm/apple/mobileconfig/mobileconfig.go @@ -269,7 +269,7 @@ func (mc *Mobileconfig) ScreenPayloads(allowCustomFileVault bool) error { case FleetCustomSettingsPayloadType: contains, err := ContainsFDEFileVaultOptionsPayload(*mc) if err != nil { - return fmt.Errorf("checking for FDEVileVaultOptions payload: %w", err) + return fmt.Errorf("checking for FDEFileVaultOptions payload: %w", err) } if contains && !allowCustomFileVault { return errors.New(DiskEncryptionProfileRestrictionErrMsg) From 6d3e3da46c5fe7de0bbf4652c9cf51afc6eb3118 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:30 +0000 Subject: [PATCH 39/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- tools/android/android.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/android/android.go b/tools/android/android.go index c4e5c54743f..95fb6c6ea5c 100644 --- a/tools/android/android.go +++ b/tools/android/android.go @@ -223,7 +223,7 @@ func devicesDelete(mgmt *androidmanagement.Service, enterpriseID string, deviceI } _, err := mgmt.Enterprises.Devices.Delete("enterprises/" + enterpriseID + "/devices/" + deviceID).Do() if err != nil { - log.Fatalf("Error listing devices: %v", err) + log.Fatalf("Error deleting device: %v", err) } log.Printf("Device %s deleted", deviceID) } From 51dd6a06796ac8527fe6ad47bd30fbaf0e8c8432 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 05:45:31 +0000 Subject: [PATCH 40/40] fix(adhoc-sweep-fixes): 60 review findings across 40 files --- website/assets/js/components/multifield.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/assets/js/components/multifield.component.js b/website/assets/js/components/multifield.component.js index bf386ef14a6..8a3e2aeb34e 100644 --- a/website/assets/js/components/multifield.component.js +++ b/website/assets/js/components/multifield.component.js @@ -170,7 +170,7 @@ parasails.registerComponent('multifield', { } } this.optionsForSelect = _.clone(this.selectOptions); - if(this.currentFieldValues === [null]){ + if(_.isEqual(this.currentFieldValues, [null]) || _.isEqual(this.currentFieldValues, [undefined])){ this.currentFieldValues = []; }