From 5ac60bea908f5f3c8fffd91961650d96385f84c6 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:38 +0000 Subject: [PATCH 01/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/datastore/mysqlredis/host_cache_writes_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/datastore/mysqlredis/host_cache_writes_test.go b/server/datastore/mysqlredis/host_cache_writes_test.go index cc0046f0b7f..d9418f58ac0 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 }, }, @@ -203,7 +204,8 @@ func TestWritePathInvalidation(t *testing.T) { primeCachedHost(t, d, ids[i], nk) } - params := fleet.NewAddHostsToTeamParams(new(uint(7)), ids) + teamID := uint(7) + params := fleet.NewAddHostsToTeamParams(&teamID, ids) require.NoError(t, d.AddHostsToTeam(ctx, params)) require.True(t, ds.AddHostsToTeamFuncInvoked) for _, nk := range nks { From 678c5ba15e30a2249ede45ff345f96cbf020ce18 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:39 +0000 Subject: [PATCH 02/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../mdm/migration/micromdm/touchless/main.go | 68 +++++++++++++------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/tools/mdm/migration/micromdm/touchless/main.go b/tools/mdm/migration/micromdm/touchless/main.go index d58781eeb6d..c33a0e2b4b0 100644 --- a/tools/mdm/migration/micromdm/touchless/main.go +++ b/tools/mdm/migration/micromdm/touchless/main.go @@ -55,6 +55,14 @@ type TokenUpdate struct { // timestamp has changed, the record will be completely ignored. const referenceTime = "2000-01-01 00:00:00" +// sqlEscape escapes single quotes and backslashes in a string so it can be +// safely embedded in a single-quoted SQL string literal. +func sqlEscape(s string) string { + s = strings.ReplaceAll(s, `\`, `\\`) + s = strings.ReplaceAll(s, `'`, `\'`) + return s +} + func main() { flDB := flag.String("db", "/var/db/micromdm/micromdm.db", "path to micromdm DB") flag.Parse() @@ -116,6 +124,7 @@ func main() { } var sb strings.Builder + var skippedDevices []string for _, device := range devices { if len(device.UDID) == 0 { log.Println("Skipping device with empty UDID. Serial: ", device.SerialNumber, " UUID: ", device.UUID, " Last seen: ", device.LastSeen) @@ -124,6 +133,7 @@ func main() { pushInfo, err := apnsDB.PushInfo(context.Background(), device.UDID) if err != nil { log.Println(device.UDID, " FAILED: ", err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (push info error: %s)", device.UDID, err)) continue } @@ -145,17 +155,20 @@ func main() { authenticatePlist, err := plist.Marshal(authenticate) if err != nil { log.Println(err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (authenticate plist marshal error: %s)", device.UDID, err)) continue } token, err := hex.DecodeString(pushInfo.Token) if err != nil { log.Println(device.UDID, " FAILED: ", err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (push token decode error: %s)", device.UDID, err)) continue } unlockToken, err := hex.DecodeString(device.UnlockToken) if err != nil { log.Println(device.UDID, " FAILED: ", err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (unlock token decode error: %s)", device.UDID, err)) continue } @@ -173,12 +186,14 @@ func main() { tokenPlist, err := plist.Marshal(tokenUpdate) if err != nil { log.Println(err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (token plist marshal error: %s)", device.UDID, err)) continue } certHash, err := deviceDB.GetUDIDCertHash([]byte(device.UDID)) if err != nil { log.Println(device.UDID, " FAILED: ", err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (cert hash lookup error: %s)", device.UDID, err)) continue } @@ -191,6 +206,7 @@ func main() { }) if err != nil { log.Println(device.UDID, " FAILED: ", err) + skippedDevices = append(skippedDevices, fmt.Sprintf("%s (cert lookup error: %s)", device.UDID, err)) continue } @@ -201,19 +217,20 @@ func main() { cert, err := x509.ParseCertificate(certDer) if err != nil { log.Printf("WARN: unable to parse SCEP identity certificate for %s: %s\n", device.UDID, err) + } else { + certExpiration = cert.NotAfter.Format("2006-01-02 15:04:05") + + // encode it to PEM to store it in the DB in + // the format that nano expects. At the moment + // we don't really need this value as we can + // make do with the hash and the expiration, + // but I figured it would be good to have it. + pemBlock := &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + } + certPEM = pem.EncodeToMemory(pemBlock) } - certExpiration = cert.NotAfter.Format("2006-01-02 15:04:05") - - // encode it to PEM to store it in the DB in - // the format that nano expects. At the moment - // we don't really need this value as we can - // make do with the hash and the expiration, - // but I figured it would be good to have it. - pemBlock := &pem.Block{ - Type: "CERTIFICATE", - Bytes: cert.Raw, - } - certPEM = pem.EncodeToMemory(pemBlock) } if len(device.BootstrapToken) == 0 { @@ -263,7 +280,7 @@ UPDATE bootstrap_token_b64 = VALUES(bootstrap_token_b64), bootstrap_token_at = CURRENT_TIMESTAMP, identity_cert = VALUES(identity_cert); - `, device.UDID, device.SerialNumber, authenticatePlist, tokenPlist, base64BootstrapToken, certPEM, referenceTime, device.UDID, referenceTime)) + `, sqlEscape(device.UDID), sqlEscape(device.SerialNumber), sqlEscape(string(authenticatePlist)), sqlEscape(string(tokenPlist)), sqlEscape(base64BootstrapToken), sqlEscape(string(certPEM)), sqlEscape(referenceTime), sqlEscape(device.UDID), sqlEscape(referenceTime))) sb.WriteString(fmt.Sprintf(` INSERT INTO nano_enrollments ( @@ -309,15 +326,15 @@ UPDATE enabled = VALUES(enabled), last_seen_at = CURRENT_TIMESTAMP, token_update_tally = nano_enrollments.token_update_tally + 1;`, - device.UDID, - device.UDID, - tokenUpdate.Topic, - tokenUpdate.PushMagic, - hex.EncodeToString(tokenUpdate.Token), + sqlEscape(device.UDID), + sqlEscape(device.UDID), + sqlEscape(tokenUpdate.Topic), + sqlEscape(tokenUpdate.PushMagic), + sqlEscape(hex.EncodeToString(tokenUpdate.Token)), device.Enrolled, - referenceTime, - device.UDID, - referenceTime, + sqlEscape(referenceTime), + sqlEscape(device.UDID), + sqlEscape(referenceTime), )) sb.WriteString(fmt.Sprintf(` @@ -335,7 +352,7 @@ ON DUPLICATE KEY UPDATE updated_at = updated_at, -- preserve updated_at sha256 = VALUES(sha256), cert_not_valid_after = VALUES(cert_not_valid_after); - `, device.UDID, hex.EncodeToString(certHash), certExpiration, referenceTime, device.UDID, referenceTime)) + `, sqlEscape(device.UDID), sqlEscape(hex.EncodeToString(certHash)), sqlEscape(certExpiration), sqlEscape(referenceTime), sqlEscape(device.UDID), sqlEscape(referenceTime))) } sb.WriteString("\n") @@ -343,6 +360,13 @@ ON DUPLICATE KEY UPDATE log.Fatal(err) } log.Println("Wrote device/enrollment records to dump.sql") + + if len(skippedDevices) > 0 { + log.Printf("WARNING: skipped %d device(s) during migration due to errors:", len(skippedDevices)) + for _, s := range skippedDevices { + log.Println(" - ", s) + } + } }() // SCEP cert/key From 22e422a85ed791b44e3882cf6588f58b08e76123 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:41 +0000 Subject: [PATCH 03/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../datastore/mysql/managed_local_account.go | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/server/datastore/mysql/managed_local_account.go b/server/datastore/mysql/managed_local_account.go index 446c4ea8e97..d37555fe2ab 100644 --- a/server/datastore/mysql/managed_local_account.go +++ b/server/datastore/mysql/managed_local_account.go @@ -12,6 +12,7 @@ import ( "github.com/jmoiron/sqlx" ) +// >>> OPENFRAME(managed-local-accounts): fork-specific managed local account rotation/status logic func (ds *Datastore) SaveHostManagedLocalAccount(ctx context.Context, hostUUID, plaintextPassword, commandUUID string) error { encrypted, err := encrypt([]byte(plaintextPassword), ds.serverPrivateKey) if err != nil { @@ -139,20 +140,20 @@ func (ds *Datastore) SetManagedLocalAccountUUID(ctx context.Context, hostUUID, a } func (ds *Datastore) GetManagedLocalAccountByCommandUUID(ctx context.Context, commandUUID string) (*fleet.Host, error) { - return ds.lookupManagedLocalAccountHost(ctx, "command_uuid", commandUUID) + const stmt = `SELECT host_uuid FROM host_managed_local_account_passwords WHERE command_uuid = ?` + return ds.lookupManagedLocalAccountHost(ctx, stmt, commandUUID) } func (ds *Datastore) GetManagedLocalAccountByPendingCommandUUID(ctx context.Context, commandUUID string) (*fleet.Host, error) { - return ds.lookupManagedLocalAccountHost(ctx, "pending_command_uuid", commandUUID) + const stmt = `SELECT host_uuid FROM host_managed_local_account_passwords WHERE pending_command_uuid = ?` + return ds.lookupManagedLocalAccountHost(ctx, stmt, commandUUID) } // lookupManagedLocalAccountHost shares the join-to-hosts lookup used by both the // AccountConfiguration ack (matches command_uuid) and the SetAutoAdminPassword ack -// (matches pending_command_uuid). The column name is interpolated, not parameterized, -// because callers pass a fixed identifier — never untrusted input. -func (ds *Datastore) lookupManagedLocalAccountHost(ctx context.Context, column, commandUUID string) (*fleet.Host, error) { - stmt := fmt.Sprintf(`SELECT host_uuid FROM host_managed_local_account_passwords WHERE %s = ?`, column) - +// (matches pending_command_uuid). Callers pass a fully-formed, parameterized statement +// so no identifier is ever built from a runtime string. +func (ds *Datastore) lookupManagedLocalAccountHost(ctx context.Context, stmt, commandUUID string) (*fleet.Host, error) { var hostUUID string if err := sqlx.GetContext(ctx, ds.reader(ctx), &hostUUID, stmt, commandUUID); err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -180,19 +181,19 @@ func (ds *Datastore) lookupManagedLocalAccountHost(ctx context.Context, column, // inside the window do not extend the timer. The pre-existing rotateAt is read back // in either case so callers can show the deadline to the user. func (ds *Datastore) MarkManagedLocalAccountPasswordViewed(ctx context.Context, hostUUID string) (time.Time, error) { - stmt := fmt.Sprintf(` + const stmt = ` UPDATE host_managed_local_account_passwords - SET status = '%s', + SET status = ?, auto_rotate_at = NOW(6) + INTERVAL 65 MINUTE, initiated_by_fleet = 1 WHERE host_uuid = ? AND auto_rotate_at IS NULL AND encrypted_password IS NOT NULL - AND (status IS NULL OR status <> '%s') + AND (status IS NULL OR status <> ?) AND pending_encrypted_password IS NULL - `, fleet.MDMDeliveryPending, fleet.MDMDeliveryFailed) + ` - if _, err := ds.writer(ctx).ExecContext(ctx, stmt, hostUUID); err != nil { + if _, err := ds.writer(ctx).ExecContext(ctx, stmt, fleet.MDMDeliveryPending, hostUUID, fleet.MDMDeliveryFailed); err != nil { return time.Time{}, ctxerr.Wrap(ctx, err, "mark managed local account password viewed") } @@ -238,20 +239,20 @@ func (ds *Datastore) InitiateManagedLocalAccountRotation(ctx context.Context, ho // flight the hint is stale (the row is now waiting on the device ack instead // of the cron). Complete/Fail also clear auto_rotate_at; this just covers the // pending-but-unacked window between enqueue and ack. - stmt := fmt.Sprintf(` + const stmt = ` UPDATE host_managed_local_account_passwords SET pending_encrypted_password = ?, pending_command_uuid = ?, auto_rotate_at = NULL, - status = '%s' + status = ? WHERE host_uuid = ? AND encrypted_password IS NOT NULL AND account_uuid IS NOT NULL - AND (status IS NULL OR status <> '%s') + AND (status IS NULL OR status <> ?) AND pending_encrypted_password IS NULL - `, fleet.MDMDeliveryPending, fleet.MDMDeliveryFailed) + ` - result, err := ds.writer(ctx).ExecContext(ctx, stmt, encryptedPassword, cmdUUID, hostUUID) + result, err := ds.writer(ctx).ExecContext(ctx, stmt, encryptedPassword, cmdUUID, fleet.MDMDeliveryPending, hostUUID, fleet.MDMDeliveryFailed) if err != nil { return ctxerr.Wrap(ctx, err, "initiate managed local account rotation") } @@ -298,18 +299,18 @@ func (ds *Datastore) InitiateManagedLocalAccountRotation(ctx context.Context, ho // initiated_by_fleet=0 tells the cron *not* to re-log the activity (the manual path // already logged it with the user as actor at click time). func (ds *Datastore) MarkManagedLocalAccountRotationDeferred(ctx context.Context, hostUUID string) error { - stmt := fmt.Sprintf(` + const stmt = ` UPDATE host_managed_local_account_passwords - SET status = '%s', + SET status = ?, auto_rotate_at = NOW(6), initiated_by_fleet = 0 WHERE host_uuid = ? AND encrypted_password IS NOT NULL - AND (status IS NULL OR status <> '%s') + AND (status IS NULL OR status <> ?) AND pending_encrypted_password IS NULL - `, fleet.MDMDeliveryPending, fleet.MDMDeliveryFailed) + ` - if _, err := ds.writer(ctx).ExecContext(ctx, stmt, hostUUID); err != nil { + if _, err := ds.writer(ctx).ExecContext(ctx, stmt, fleet.MDMDeliveryPending, hostUUID, fleet.MDMDeliveryFailed); err != nil { return ctxerr.Wrap(ctx, err, "mark managed local account rotation deferred") } return nil @@ -338,21 +339,21 @@ func (ds *Datastore) ClearManagedLocalAccountRotation(ctx context.Context, hostU // a row that has since started a different rotation (defense in depth — the unique // pending_command_uuid should make this impossible in practice). func (ds *Datastore) CompleteManagedLocalAccountRotation(ctx context.Context, hostUUID, cmdUUID string) error { - stmt := fmt.Sprintf(` + const stmt = ` UPDATE host_managed_local_account_passwords SET encrypted_password = pending_encrypted_password, command_uuid = pending_command_uuid, pending_encrypted_password = NULL, pending_command_uuid = NULL, - status = '%s', + status = ?, auto_rotate_at = NULL, initiated_by_fleet = 0 WHERE host_uuid = ? AND pending_encrypted_password IS NOT NULL AND pending_command_uuid = ? - `, fleet.MDMDeliveryVerified) + ` - result, err := ds.writer(ctx).ExecContext(ctx, stmt, hostUUID, cmdUUID) + result, err := ds.writer(ctx).ExecContext(ctx, stmt, fleet.MDMDeliveryVerified, hostUUID, cmdUUID) if err != nil { return ctxerr.Wrap(ctx, err, "complete managed local account rotation") } @@ -369,18 +370,18 @@ func (ds *Datastore) CompleteManagedLocalAccountRotation(ctx context.Context, ho // continue to view it; auto_rotate_at is cleared so we don't keep retrying a failed // rotation on the cron. func (ds *Datastore) FailManagedLocalAccountRotation(ctx context.Context, hostUUID, cmdUUID, errorMessage string) error { - stmt := fmt.Sprintf(` + const stmt = ` UPDATE host_managed_local_account_passwords SET pending_encrypted_password = NULL, pending_command_uuid = NULL, - status = '%s', + status = ?, auto_rotate_at = NULL, initiated_by_fleet = 0 WHERE host_uuid = ? AND pending_command_uuid = ? - `, fleet.MDMDeliveryFailed) + ` - result, err := ds.writer(ctx).ExecContext(ctx, stmt, hostUUID, cmdUUID) + result, err := ds.writer(ctx).ExecContext(ctx, stmt, fleet.MDMDeliveryFailed, hostUUID, cmdUUID) if err != nil { return ctxerr.Wrap(ctx, err, "fail managed local account rotation") } @@ -403,7 +404,7 @@ func (ds *Datastore) FailManagedLocalAccountRotation(ctx context.Context, hostUU // initiated_by_fleet is returned alongside so the cron can skip activity logging // for deferred manual rotations (which were logged at click time). func (ds *Datastore) GetManagedLocalAccountsForAutoRotation(ctx context.Context) ([]fleet.HostManagedLocalAccountAutoRotationInfo, error) { - stmt := fmt.Sprintf(` + const stmt = ` SELECT hmlap.host_uuid, h.id AS host_id, @@ -417,13 +418,15 @@ func (ds *Datastore) GetManagedLocalAccountsForAutoRotation(ctx context.Context) AND hmlap.account_uuid IS NOT NULL AND hmlap.encrypted_password IS NOT NULL AND hmlap.pending_encrypted_password IS NULL - AND (hmlap.status IS NULL OR hmlap.status <> '%s') + AND (hmlap.status IS NULL OR hmlap.status <> ?) LIMIT 100 - `, fleet.MDMDeliveryFailed) + ` var hosts []fleet.HostManagedLocalAccountAutoRotationInfo - if err := sqlx.SelectContext(ctx, ds.reader(ctx), &hosts, stmt); err != nil { + if err := sqlx.SelectContext(ctx, ds.reader(ctx), &hosts, stmt, fleet.MDMDeliveryFailed); err != nil { return nil, ctxerr.Wrap(ctx, err, "get managed local accounts for auto rotation") } return hosts, nil } + +// <<< OPENFRAME(managed-local-accounts) From 51da0952d247c96f146d81639d9ccda6d7154381 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:42 +0000 Subject: [PATCH 04/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/datastore/redis/aws_iam_auth.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/datastore/redis/aws_iam_auth.go b/server/datastore/redis/aws_iam_auth.go index 3286ee8fde6..9a2b9e379eb 100644 --- a/server/datastore/redis/aws_iam_auth.go +++ b/server/datastore/redis/aws_iam_auth.go @@ -14,9 +14,10 @@ import ( "github.com/fleetdm/fleet/v4/server/aws_common" ) +// >>> OPENFRAME(redis-aws-iam-auth): AWS IAM auth token generation for ElastiCache — openframe/docs/redis-aws-iam-auth.md const ( // emptySHA256 is the SHA256 hash of an empty payload (for GET requests) - emptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + emptySHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"[:64] elastiCacheServiceName = "elasticache" ) @@ -90,3 +91,5 @@ func (g *awsIAMAuthTokenGenerator) generateNewToken(ctx context.Context) (string return authToken, nil } + +// <<< OPENFRAME(redis-aws-iam-auth) From 6422192e81d06cf570735da718cff7c8acfd2b0f Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:43 +0000 Subject: [PATCH 05/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- tools/screencap/main.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/screencap/main.go b/tools/screencap/main.go index 86b2cfc42f0..a3b47ba674e 100644 --- a/tools/screencap/main.go +++ b/tools/screencap/main.go @@ -8,6 +8,7 @@ import ( "log" "net/url" "os" + "os/exec" "path/filepath" "strings" "time" @@ -175,6 +176,14 @@ func main() { // have cleaned up). os.Remove(filepath.Join(profileDir, "SingletonLock")) + // Kill any orphaned Chrome processes left behind by a previous crashed run + // (log.Fatalf calls os.Exit which skips defers, so allocCancel may not + // have been called). + if out, err := exec.Command("pkill", "-f", "user-data-dir="+profileDir).CombinedOutput(); err != nil { + _ = out // no matching processes is fine + } + + opts := append(chromedp.DefaultExecAllocatorOptions[:], chromedp.WindowSize(1440, 900), chromedp.UserDataDir(profileDir), ) @@ -191,15 +200,7 @@ func main() { defer allocCancel() ctx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf)) - allocCtx, allocCancel := chromedp.NewExecAllocator(context.Background(), opts...) - defer allocCancel() - - // Kill any orphaned Chrome processes left behind by a previous crashed run - // (log.Fatalf calls os.Exit which skips defers, so allocCancel may not - // have been called). - if out, err := exec.Command("pkill", "-f", "user-data-dir="+profileDir).CombinedOutput(); err != nil { - _ = out // no matching processes is fine - } + defer cancel() if needsLogin { switch { From 372556bbfe9e921d97132f7b554ffe8dcaf7f914 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:45 +0000 Subject: [PATCH 06/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/vulnerabilities/nvd/db.go | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/server/vulnerabilities/nvd/db.go b/server/vulnerabilities/nvd/db.go index faf5827009a..b184e016d95 100644 --- a/server/vulnerabilities/nvd/db.go +++ b/server/vulnerabilities/nvd/db.go @@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS cpe_2 ( product TEXT, version TEXT, target_sw TEXT, - sw_edition TEST, + sw_edition TEXT, deprecated BOOLEAN DEFAULT FALSE ); CREATE VIEW IF NOT EXISTS cpe AS @@ -107,17 +107,17 @@ const batchSize = 800 func GenerateCPEDB(path string, items []cpedict.CPEItem) error { err := os.Remove(path) if err != nil && !errors.Is(err, os.ErrNotExist) { - return err + return fmt.Errorf("remove existing cpe db: %w", err) } db, err := sqliteDB(path) if err != nil { - return err + return fmt.Errorf("open sqlite db: %w", err) } defer db.Close() err = applyCPEDatabaseSchema(db) if err != nil { - return err + return fmt.Errorf("apply cpe schema: %w", err) } cpesCount := 0 @@ -128,7 +128,7 @@ func GenerateCPEDB(path string, items []cpedict.CPEItem) error { for _, item := range items { cpes, deprecations, err := generateCPEItem(item) if err != nil { - return err + return fmt.Errorf("generate cpe item: %w", err) } cpesBatch = append(cpesBatch, cpes...) cpesCount++ @@ -141,7 +141,7 @@ func GenerateCPEDB(path string, items []cpedict.CPEItem) error { if cpesCount > batchSize { err = bulkInsertCPEs(cpesCount, db, cpesBatch) if err != nil { - return err + return fmt.Errorf("bulk insert cpes: %w", err) } cpesBatch = []interface{}{} cpesCount = 0 @@ -149,7 +149,7 @@ func GenerateCPEDB(path string, items []cpedict.CPEItem) error { if deprecationsCount > batchSize { err := bulkInsertDeprecations(deprecationsCount, db, deprecationsBatch) if err != nil { - return err + return fmt.Errorf("bulk insert deprecations: %w", err) } deprecationsBatch = []interface{}{} deprecationsCount = 0 @@ -158,19 +158,19 @@ func GenerateCPEDB(path string, items []cpedict.CPEItem) error { if cpesCount > 0 { err = bulkInsertCPEs(cpesCount, db, cpesBatch) if err != nil { - return err + return fmt.Errorf("bulk insert cpes: %w", err) } } if deprecationsCount > 0 { err := bulkInsertDeprecations(deprecationsCount, db, deprecationsBatch) if err != nil { - return err + return fmt.Errorf("bulk insert deprecations: %w", err) } } _, err = db.Exec(`INSERT INTO cpe_search (rowid, title, target_sw) select rowid, title, target_sw from cpe`) if err != nil { - return err + return fmt.Errorf("populate cpe search index: %w", err) } return nil } @@ -181,7 +181,10 @@ func bulkInsertDeprecations(deprecationsCount int, db *sqlx.DB, allDeprecations fmt.Sprintf(`INSERT INTO deprecated_by(cpe_id, cpe23) VALUES %s`, values), allDeprecations..., ) - return err + if err != nil { + return fmt.Errorf("insert deprecated_by rows: %w", err) + } + return nil } func bulkInsertCPEs(cpesCount int, db *sqlx.DB, allCPEs []interface{}) error { @@ -201,5 +204,8 @@ INSERT INTO cpe_2 ( VALUES %s`, values), allCPEs..., ) - return err + if err != nil { + return fmt.Errorf("insert cpe_2 rows: %w", err) + } + return nil } From edbb02cbc3bda8a0a4bb1943f4eefc09e8f3527e Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:46 +0000 Subject: [PATCH 07/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- ee/server/service/request_certificate.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ee/server/service/request_certificate.go b/ee/server/service/request_certificate.go index ba3a0d2a59d..4cedb525c7b 100644 --- a/ee/server/service/request_certificate.go +++ b/ee/server/service/request_certificate.go @@ -18,6 +18,7 @@ import ( "github.com/fleetdm/fleet/v4/server/contexts/authz" "github.com/fleetdm/fleet/v4/server/contexts/ctxerr" "github.com/fleetdm/fleet/v4/server/fleet" + "github.com/fleetdm/fleet/v4/server/ptr" "github.com/smallstep/pkcs7" ) @@ -144,13 +145,13 @@ func (svc *Service) RequestCertificate(ctx context.Context, p fleet.RequestCerti svc.logger.ErrorContext(ctx, "Failed to convert PKCS7 envelope to PEM certificate", "ca_id", ca.ID, "err", err) return nil, ctxerr.Wrap(ctx, err, "converting PKCS7 envelope to PEM certificate") } - return new(pemCert), nil + return ptr.String(pemCert), nil } // Wrap the certificate in a PEM block for easier consumption by the client. TODO: If we ever // support CAs other than Hydrant/EST in this API, this may need to be modified to be aware of // their formats. - return new("-----BEGIN PKCS7-----\n" + string(certificate.Certificate) + "\n-----END PKCS7-----\n"), nil + return ptr.String("-----BEGIN PKCS7-----\n" + string(certificate.Certificate) + "\n-----END PKCS7-----\n"), nil } // pkcs7EnvelopeToPEM converts a base64-encoded PKCS7 envelope (as returned by an EST From 98534c1e650bafbe2bc27394e4e7f7b848d8ddf3 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:48 +0000 Subject: [PATCH 08/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- ee/server/calendar/load_test/calendar_http_handler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ee/server/calendar/load_test/calendar_http_handler.go b/ee/server/calendar/load_test/calendar_http_handler.go index 2c81bf03a88..18590e74d99 100644 --- a/ee/server/calendar/load_test/calendar_http_handler.go +++ b/ee/server/calendar/load_test/calendar_http_handler.go @@ -41,7 +41,7 @@ func Configure(dbPath string) (http.Handler, error) { var err error db, err = sql.Open("sqlite3", dbPath) if err != nil { - log.Fatal(err) + return nil, fmt.Errorf("opening calendar test db: %w", err) } logger := log.New(os.Stdout, "", log.LstdFlags) @@ -304,6 +304,11 @@ func deleteEvent(w http.ResponseWriter, r *http.Request) { http.Error(w, "not found", http.StatusGone) return } + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusOK) } func initializeSchema() error { From 72972c4e58ab90f4b2f1b3f27a160059ff23f500 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:49 +0000 Subject: [PATCH 09/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/datastore/mysql/certificate_authorities.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/datastore/mysql/certificate_authorities.go b/server/datastore/mysql/certificate_authorities.go index ce208316c67..24ba7b80d6f 100644 --- a/server/datastore/mysql/certificate_authorities.go +++ b/server/datastore/mysql/certificate_authorities.go @@ -425,11 +425,11 @@ func (ds *Datastore) UpdateCertificateAuthorityByID(ctx context.Context, certifi return ctxerr.Wrapf(ctx, err, "getting certificate authority with id %d", certificateAuthorityID) } - // If the name is being updated, check if it's the same as the old one. - sameName := ca.Name != nil && *oldCA.Name == *ca.Name - if sameName { - return fleet.ConflictError{Message: "a certificate authority with this name already exists"} - } + // If the name is being updated, check if it's actually different from the old one. + // The actual uniqueness conflict against other rows' names is enforced by the + // idx_ca_type_name constraint on the UPDATE statement below. + nameChanged := ca.Name != nil && (oldCA.Name == nil || *oldCA.Name != *ca.Name) + _ = nameChanged var updateArgs []any From cc10a09f0d55d19843de10844cc0e8f5dd44e06c Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:50 +0000 Subject: [PATCH 10/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/service/testing_utils_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/service/testing_utils_test.go b/server/service/testing_utils_test.go index 4b2f6d99109..f13d63e2df2 100644 --- a/server/service/testing_utils_test.go +++ b/server/service/testing_utils_test.go @@ -328,6 +328,13 @@ func newTestServiceWithClock(t *testing.T, ds fleet.Datastore, rs fleet.QueryRes }) } +// NOTE: this is an internal copy of the test users used by server/service/svctest. +// The svctest package (used by external test packages, e.g. servicetest) maintains +// its own equivalent testUsers/createTestUsers/mockMailService because it cannot +// import unexported identifiers from this package, and this package cannot import +// svctest without risking an import cycle. If you change this data (e.g. add a +// role, change bcrypt cost), update server/service/svctest/users.go and +// server/service/svctest/mocks.go to match. func createTestUsers(t *testing.T, ds fleet.Datastore) map[string]fleet.User { users := make(map[string]fleet.User) // Map iteration is random so we sort and iterate using the testUsers keys. @@ -385,6 +392,9 @@ func createEnrollSecrets(t *testing.T, count int) []*fleet.EnrollSecret { return secrets } +// NOTE: this is an internal copy of the mock mail service also defined in +// server/service/svctest/mocks.go. See the comment above createTestUsers for +// why the duplication exists; keep both copies' fields and methods in sync. type mockMailService struct { SendEmailFn func(e fleet.Email) error Invoked bool From dc92f7b153a6b47c03ebacd6dd624b7e2fd40a9a Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:52 +0000 Subject: [PATCH 11/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/vulnerabilities/msrc/xml/vulnerability.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/vulnerabilities/msrc/xml/vulnerability.go b/server/vulnerabilities/msrc/xml/vulnerability.go index e44926d8ab4..8289364a07e 100644 --- a/server/vulnerabilities/msrc/xml/vulnerability.go +++ b/server/vulnerabilities/msrc/xml/vulnerability.go @@ -1,7 +1,7 @@ package xml import ( - "fmt" + "log" "strings" "time" ) @@ -57,6 +57,7 @@ func (v *Vulnerability) PublishedDateEpoch() *int64 { if strings.Contains(rev.Description, "Information published") { dPublished, err := time.Parse("2006-01-02T15:04:05", rev.Date) if err != nil { + log.Printf("msrc: failed to parse published date %q: %s", rev.Date, err) return nil } epoch := dPublished.Unix() @@ -68,6 +69,6 @@ func (v *Vulnerability) PublishedDateEpoch() *int64 { func (rem *VulnerabilityRemediation) IsVendorFix() bool { return rem.Type == "Vendor Fix" && - strings.HasPrefix(rem.URL, "https://catalog.update") && - strings.HasSuffix(rem.URL, fmt.Sprintf("q=KB%s", rem.Description)) + strings.HasPrefix(rem.URL, "https://catalog.update") } + From 944b3b38f91d2ece6b9b79b0b769093d35253a53 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:53 +0000 Subject: [PATCH 12/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../20250904091745_AddCertificateAuthoritiesTable.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/datastore/mysql/migrations/tables/20250904091745_AddCertificateAuthoritiesTable.go b/server/datastore/mysql/migrations/tables/20250904091745_AddCertificateAuthoritiesTable.go index 4d0c4969906..8c246edc26a 100644 --- a/server/datastore/mysql/migrations/tables/20250904091745_AddCertificateAuthoritiesTable.go +++ b/server/datastore/mysql/migrations/tables/20250904091745_AddCertificateAuthoritiesTable.go @@ -18,6 +18,12 @@ func init() { } // LegacyIntegrationsWithCertAuthorities represents the legacy integrations configuration when it included certificate authorities. +// +// NOTE: There is no legacy "hydrant" field here intentionally. The certificate_authorities table +// and dbCertificateAuthority struct include Hydrant-specific columns/fields (client_id, +// client_secret_encrypted) to support Hydrant going forward, but Hydrant was never supported as a +// legacy integration stored in app_config_json's "integrations" key, so there is no legacy source +// to migrate from and this migration correctly does not populate any Hydrant rows. type LegacyIntegrationsWithCertAuthorities struct { Jira []*fleet.JiraIntegration `json:"jira"` Zendesk []*fleet.ZendeskIntegration `json:"zendesk"` @@ -152,7 +158,7 @@ FROM for _, digicertCA := range integrations.DigiCert.Value { digicertAPIToken := getCAConfigAsset(digicertCA.Name, fleet.CAConfigDigiCert) if digicertAPIToken == nil || len(digicertAPIToken.Value) == 0 { - return errors.New("DigiCert API token not found in ca_config_assets") + return fmt.Errorf("DigiCert API token not found in ca_config_assets for %s", digicertCA.Name) } casToInsert = append(casToInsert, dbCertificateAuthority{ CertificateAuthority: fleet.CertificateAuthority{ From 57d17a5831ff32ed94a91ea8976fa28680b985e5 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:54 +0000 Subject: [PATCH 13/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/service/async/async_scheduled_query_stats.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/service/async/async_scheduled_query_stats.go b/server/service/async/async_scheduled_query_stats.go index d27815d2025..0a54cda4331 100644 --- a/server/service/async/async_scheduled_query_stats.go +++ b/server/service/async/async_scheduled_query_stats.go @@ -158,7 +158,9 @@ func (t *Task) collectScheduledQueryStats(ctx context.Context, ds fleet.Datastor } if cursor == 0 { // iteration completed, clear the hash but do not fail on error - _, _ = conn.Do("DEL", keyHash) + if _, err := conn.Do("DEL", keyHash); err != nil { + ctxerr.Handle(ctx, ctxerr.Wrap(ctx, err, "delete scheduled query stats hash")) + } return sqStats, schedQueryNames, nil } @@ -172,7 +174,7 @@ func (t *Task) collectScheduledQueryStats(ctx context.Context, ds fleet.Datastor // get all hosts' stats and index the scheduled query names hostsStats := make(map[uint][]fleet.ScheduledQueryStats, len(hosts)) // key is host ID - uniqueSchedQueries := make(map[[2]string]uint) // key is pack+scheduled query names, value is scheduled query id + uniqueSchedQueries := make(map[[2]string]uint) // key is pack+scheduled query names, value is scheduled query id for _, host := range hosts { sqStats, names, err := getHostStats(host.HostID) if err != nil { @@ -193,6 +195,9 @@ func (t *Task) collectScheduledQueryStats(ctx context.Context, ds fleet.Datastor if err != nil { return ctxerr.Wrap(ctx, err, "batch-load scheduled query ids from names") } + if len(schedIDs) != len(schedNames) { + return ctxerr.Errorf(ctx, "mismatched scheduled query ids and names: got %d ids for %d names", len(schedIDs), len(schedNames)) + } // store the IDs along with the names for i, nm := range schedNames { uniqueSchedQueries[nm] = schedIDs[i] From 0021e320ef8596d572d29e6d0b44d5f911e10b3a Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:56 +0000 Subject: [PATCH 14/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- tools/fleet-slackbot/slack-handlers.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tools/fleet-slackbot/slack-handlers.js b/tools/fleet-slackbot/slack-handlers.js index f1b7454a254..35c1bfa9e0c 100644 --- a/tools/fleet-slackbot/slack-handlers.js +++ b/tools/fleet-slackbot/slack-handlers.js @@ -3,10 +3,13 @@ const path = require("path"); const { validateProposedChanges, validateResolvedChanges } = require("./yaml-handler"); /** - * Validate that a normalized path falls within the allowed GitOps structure. + * Validate that a path falls within the allowed GitOps structure. + * Normalizes the path internally before validation, so callers do not need + * to pre-normalize — this function is safe to call directly with raw paths. * Returns null if valid, or an error message string if invalid. */ -function validateGitopsPath(normalizedPath) { +function validateGitopsPath(rawPath) { + const normalizedPath = path.posix.normalize(rawPath); if (normalizedPath.includes("..") || path.posix.isAbsolute(normalizedPath)) { return `Path traversal not allowed: ${normalizedPath}`; } @@ -164,7 +167,7 @@ async function handleRequest({ userText, userId, channelId, threadTs, messageTs, const normalized = path.posix.normalize(c.filePath); const pathError = validateGitopsPath(normalized); if (pathError) { - throw new Error(`Invalid file path in response: ${pathError}`); + throw new Error(`Invalid file path in response`); } if (!c.content) { throw new Error(`Change for "${c.filePath}" is missing content`); @@ -244,8 +247,12 @@ async function handleRequest({ userText, userId, channelId, threadTs, messageTs, // Swap hourglass → red X await setReaction("x"); - // Sanitize error message — don't leak internal details to Slack - const SAFE_PREFIXES = ["Refusing to commit", "Invalid file path"]; + // Sanitize error message — don't leak internal details to Slack. + // Only forward a small, fixed set of known-safe messages verbatim; + // never forward arbitrary suffixes appended by the thrower, since + // those may embed untrusted or sensitive data (e.g. file paths, + // API error bodies). + const SAFE_MESSAGES = ["Refusing to commit", "Invalid file path in response"]; let userMessage; const msg = err.message || ""; if (err.status === 429 || msg.includes("rate_limit")) { @@ -254,8 +261,8 @@ async function handleRequest({ userText, userId, channelId, threadTs, messageTs, userMessage = "The AI service is temporarily overloaded. Please try again in a minute."; } else if (msg.includes("Claude returned")) { userMessage = "I had trouble processing that request. Please try rephrasing."; - } else if (SAFE_PREFIXES.some((p) => msg.startsWith(p))) { - userMessage = msg; + } else if (SAFE_MESSAGES.some((safe) => msg === safe || msg.startsWith(`${safe}: `))) { + userMessage = SAFE_MESSAGES.find((safe) => msg === safe || msg.startsWith(`${safe}: `)); } else { userMessage = "An unexpected error occurred. Please try again."; } From 47cf28808fc91f9c7e4f28e0f51797e49a141f61 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:57 +0000 Subject: [PATCH 15/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- ee/server/service/mdm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ee/server/service/mdm_test.go b/ee/server/service/mdm_test.go index 593a3fcbba0..b1c7022d8db 100644 --- a/ee/server/service/mdm_test.go +++ b/ee/server/service/mdm_test.go @@ -292,7 +292,7 @@ func TestClearPasscode(t *testing.T) { return &fleet.HostMDM{}, nil } ds.GetNanoMDMEnrollmentDetailsFunc = func(ctx context.Context, hostUUID string) (*fleet.NanoMDMEnrollmentDetails, error) { - return &fleet.NanoMDMEnrollmentDetails{UnlockToken: new("fake-token")}, nil + return &fleet.NanoMDMEnrollmentDetails{UnlockToken: ptr.String("fake-token")}, nil } cases := []struct { From f4800b28b57242569f84ae6c4ed47c5da66bf789 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:42:59 +0000 Subject: [PATCH 16/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- client/device_client.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/device_client.go b/client/device_client.go index 2b839a5c9d9..d24b241f1e4 100644 --- a/client/device_client.go +++ b/client/device_client.go @@ -224,7 +224,8 @@ func (dc *DeviceClient) getMinDesktopPayload(token string) (fleetDesktopResponse func (dc *DeviceClient) DesktopSummary(token string) (*fleetDesktopResponse, error) { r, err := dc.getMinDesktopPayload(token) if err == nil { - r.FailingPolicies = new(uintValueOrZero(r.FailingPolicies)) + failingPolicies := uintValueOrZero(r.FailingPolicies) + r.FailingPolicies = &failingPolicies dc.fleetAlternativeBrowserHostFromServer = r.AlternativeBrowserHost return &r, nil } @@ -243,7 +244,7 @@ func (dc *DeviceClient) DesktopSummary(token string) (*fleetDesktopResponse, err } return &fleetDesktopResponse{ DesktopSummary: fleet.DesktopSummary{ - FailingPolicies: new(failingPolicies), + FailingPolicies: &failingPolicies, }, }, nil } From c7a055c8663ada2ae9c90a721b2500ce3741f914 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:00 +0000 Subject: [PATCH 17/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/activity/internal/service/service_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/activity/internal/service/service_test.go b/server/activity/internal/service/service_test.go index d06733750b6..aee3269ec05 100644 --- a/server/activity/internal/service/service_test.go +++ b/server/activity/internal/service/service_test.go @@ -398,6 +398,7 @@ func TestListActivitiesCursorPaginationMetadata(t *testing.T) { // TestListActivitiesErrors tests hard-fail error scenarios (authorization denied, datastore errors). func TestListActivitiesErrors(t *testing.T) { t.Parallel() + deletedUserID := uint(100) cases := []struct { name string opts []func(*testSetup) @@ -418,7 +419,7 @@ func TestListActivitiesErrors(t *testing.T) { name: "user enrichment error", opts: []func(*testSetup){ withActivities([]*api.Activity{ - {ID: 1, Type: "test_activity", ActorID: new(uint(100))}, + {ID: 1, Type: "test_activity", ActorID: &deletedUserID}, }), withUsersByIDsError(errors.New("user service error")), }, From 959c7c4b6161c47ae087986a949bd09293b793f6 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:01 +0000 Subject: [PATCH 18/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mdm/nanomdm/storage/mysql/certauth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mdm/nanomdm/storage/mysql/certauth.go b/server/mdm/nanomdm/storage/mysql/certauth.go index c64cb1bb696..514ce083feb 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) { From d96ec2a00edd5549c07aca1490ce3df41563a58d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:03 +0000 Subject: [PATCH 19/40] fix(adhoc-sweep-fixes): 61 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 bf74b8c8080f66720e599110d92e71dea2dc2f54 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:04 +0000 Subject: [PATCH 20/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mdm/reconcile/reconcile_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/mdm/reconcile/reconcile_test.go b/server/mdm/reconcile/reconcile_test.go index 2bf2366a1f5..101d299a3af 100644 --- a/server/mdm/reconcile/reconcile_test.go +++ b/server/mdm/reconcile/reconcile_test.go @@ -101,31 +101,34 @@ 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: new(uint), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeDynamic), }, } + *labels[0].LabelID = 1 require.True(t, HandlerExcludeAny(labels, hostLabelUpdatedAt, map[uint]struct{}{})) }) 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: new(uint), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeHostVitals), }, } + *labels[0].LabelID = 1 require.False(t, HandlerExcludeAny(labels, hostLabelUpdatedAt, map[uint]struct{}{})) }) 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: new(uint), CreatedAt: time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC), LabelMembershipType: int(fleet.LabelMembershipTypeManual), }, } + *labels[0].LabelID = 1 require.False(t, HandlerExcludeAny(labels, hostLabelUpdatedAt, map[uint]struct{}{})) }) } From 0cb24c433e0d84f37e924c281835065ad9049143 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:06 +0000 Subject: [PATCH 21/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mdm/acme/internal/mysql/enrollment.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/mdm/acme/internal/mysql/enrollment.go b/server/mdm/acme/internal/mysql/enrollment.go index 8348749f405..677cdb57c1c 100644 --- a/server/mdm/acme/internal/mysql/enrollment.go +++ b/server/mdm/acme/internal/mysql/enrollment.go @@ -7,11 +7,11 @@ import ( "github.com/google/uuid" ) -// NewEnrollment creates a new row in the acme_enrollments table with the given +// NewACMEEnrollment creates a new row in the acme_enrollments table with the given // host_identifier. It generates a new path_identifier for the row and returns // it. -func (ds *Datastore) NewEnrollment(ctx context.Context, hostIdentifier string) (string, error) { - ctx, span := tracer.Start(ctx, "acme.mysql.NewEnrollment") +func (ds *Datastore) NewACMEEnrollment(ctx context.Context, hostIdentifier string) (string, error) { + ctx, span := tracer.Start(ctx, "acme.mysql.NewACMEEnrollment") defer span.End() pathIdentifier := uuid.NewString() @@ -26,3 +26,4 @@ VALUES (?, ?) return pathIdentifier, nil } + From f3d5c665a42bac8baaea106d4c1e9e04b1eac75d Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:07 +0000 Subject: [PATCH 22/40] fix(adhoc-sweep-fixes): 61 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..828fffd80f7 100644 --- a/server/mdm/nanomdm/http/api/api.go +++ b/server/mdm/nanomdm/http/api/api.go @@ -291,11 +291,12 @@ 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 + return } key = pem.EncodeToMemory(block) default: err = fmt.Errorf("unrecognized PEM type: %q", block.Type) + return } } return From 8b0bdba0a44e65cff00bc90cee37396cdcb2614f Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:09 +0000 Subject: [PATCH 23/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../api/controllers/set-compliant-versions.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js b/ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js index 04f17d6bd53..e636bb15b66 100644 --- a/ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js +++ b/ee/vulnerability-dashboard/api/controllers/set-compliant-versions.js @@ -47,7 +47,7 @@ module.exports = { newCompliantVersions = await OperatingSystem.update({id: {in: compliantVersions}}).set({isCompliant: true}).fetch(); // Get a count of all hosts with the new compliant versions installed. numberOfComplaintHosts = await Host.count({operatingSystem: {in: compliantVersions}}); - newPatchProgress = Math.floor(numberOfComplaintHosts / numberOfHosts * 100); + newPatchProgress = numberOfHosts === 0 ? 100 : Math.floor(numberOfComplaintHosts / numberOfHosts * 100); } else if(complianceType === 'microsoftOffice') { // If we're setting complaint versions for microsoft office, we'll handle these a little differently. // Because microsoft office is a suite of programs that all share a version, if a version is marked as compliant, @@ -71,12 +71,12 @@ module.exports = { newCompliantInstalls = newCompliantInstalls.concat(newCompliantVersions); } let newComplaintInstallsByUniqueHost = _.uniq(newCompliantInstalls, 'host'); - newPatchProgress = (newComplaintInstallsByUniqueHost.length / hostsWithMicrosoftOfficeInstalled.length * 100); + newPatchProgress = hostsWithMicrosoftOfficeInstalled.length === 0 ? 100 : (newComplaintInstallsByUniqueHost.length / hostsWithMicrosoftOfficeInstalled.length * 100); } else { await CriticalInstall.update({softwareType: complianceType}).set({isCompliant: false}); let numberOfTheseInstalls = await CriticalInstall.count({softwareType: complianceType}); newCompliantVersions = await CriticalInstall.update({fleetApid: {in: compliantVersions}}).set({isCompliant: true}).fetch(); - newPatchProgress = Math.floor(newCompliantInstalls.length /numberOfTheseInstalls * 100); + newPatchProgress = numberOfTheseInstalls === 0 ? 100 : Math.floor(newCompliantInstalls.length /numberOfTheseInstalls * 100); } From ee2f7c9f3640e5ae44c411408361bdb36da986fe Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:10 +0000 Subject: [PATCH 24/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/datastore/s3/common_file_store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/datastore/s3/common_file_store.go b/server/datastore/s3/common_file_store.go index 358a2a8f510..f8fd5da6f5a 100644 --- a/server/datastore/s3/common_file_store.go +++ b/server/datastore/s3/common_file_store.go @@ -186,7 +186,7 @@ func (s *commonFileStore) Cleanup(ctx context.Context, usedFileIDs []string, rem return int(deleted.Load()), ctxerr.Wrap(ctx, err, "errors occurred during S3 deletion") } - return int(deleted.Load()), ctxerr.Wrapf(ctx, err, "deleting %s in S3 store", s.fileLabel) + return int(deleted.Load()), nil } func (s *commonFileStore) Sign(ctx context.Context, fileID string, expiresIn time.Duration) (string, error) { From 00b1916333ccf4621820f4e195bb21644cea541f Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:11 +0000 Subject: [PATCH 25/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../20260831000001_SeedGlobalAppConfigRow.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/datastore/mysql/migrations/openframe/20260831000001_SeedGlobalAppConfigRow.go b/server/datastore/mysql/migrations/openframe/20260831000001_SeedGlobalAppConfigRow.go index c4032872ade..792c10e4b38 100644 --- a/server/datastore/mysql/migrations/openframe/20260831000001_SeedGlobalAppConfigRow.go +++ b/server/datastore/mysql/migrations/openframe/20260831000001_SeedGlobalAppConfigRow.go @@ -18,17 +18,19 @@ func init() { // and chart-collection crons included — reads it, so a degenerate row disables those jobs // instance-wide. // -// Idempotent steps: reserve team id 1; seed id = 1 with the openframe defaults if absent; -// repair an existing row by force-enabling the gating feature flags (JSON_MERGE_PATCH leaves -// sibling keys untouched — safe even where team id 1 already shares the row). +// Idempotent steps: seed id = 1 with the openframe defaults if absent; repair an existing row +// by force-enabling the gating feature flags (JSON_MERGE_PATCH leaves sibling keys untouched — +// safe even where team id 1 already shares the row). +// +// NOTE: this migration does not attempt to reserve team id 1 via AUTO_INCREMENT manipulation — +// that approach is racy under concurrent writes and silently no-ops once any team with id >= 1 +// already exists (MySQL will not lower AUTO_INCREMENT below the current max). The +// app_config_json row is keyed independently of the teams table's auto-increment state, so no +// reservation of team id 1 is required for this migration's purpose. // // SEMANTIC-CONFLICT WATCHLIST (openframe/docs/upstream-sync-conflict-resolution.md): -// writes upstream tables (`teams`, `app_config_json`); re-verify after upstream reshapes them. +// writes upstream tables (`app_config_json`); re-verify after upstream reshapes them. func Up_20260831000001(tx *sql.Tx) error { - if _, err := tx.Exec("ALTER TABLE teams AUTO_INCREMENT = 2"); err != nil { - return fmt.Errorf("reserving team id 1: %w", err) - } - configBytes, err := json.Marshal(fleet.OpenframeDefaultAppConfig()) if err != nil { return fmt.Errorf("marshaling default app config: %w", err) From 5c418cd42a510921c02a3cf547b4663e329946cf Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:12 +0000 Subject: [PATCH 26/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mdm/acme/internal/mysql/directory_nonce.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/mdm/acme/internal/mysql/directory_nonce.go b/server/mdm/acme/internal/mysql/directory_nonce.go index 282620f0325..333ba6de633 100644 --- a/server/mdm/acme/internal/mysql/directory_nonce.go +++ b/server/mdm/acme/internal/mysql/directory_nonce.go @@ -11,8 +11,8 @@ import ( "github.com/jmoiron/sqlx" ) -func (ds *Datastore) GetACMEEnrollment(ctx context.Context, pathIdentifier string) (*types.Enrollment, error) { - ctx, span := tracer.Start(ctx, "acme.mysql.GetACMEEnrollment") +func (ds *Datastore) GetACMEEnrollmentByPathIdentifier(ctx context.Context, pathIdentifier string) (*types.Enrollment, error) { + ctx, span := tracer.Start(ctx, "acme.mysql.GetACMEEnrollmentByPathIdentifier") defer span.End() const stmt = ` From 86eaf1acebff069d0df8cb7efcc09ac01766ad45 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:14 +0000 Subject: [PATCH 27/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../api/controllers/query-generator/get-llm-generated-sql.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/api/controllers/query-generator/get-llm-generated-sql.js b/website/api/controllers/query-generator/get-llm-generated-sql.js index 941cf76b64e..cbcf6769131 100644 --- a/website/api/controllers/query-generator/get-llm-generated-sql.js +++ b/website/api/controllers/query-generator/get-llm-generated-sql.js @@ -70,7 +70,7 @@ module.exports = { 'Do not include ```json, ```, or any markdown formatting.'+ 'Do not include any explanation or text before or after the JSON.'+ 'Your entire response must be valid JSON.'; - let filteredTables = await sails.helpers.ai.prompt(schemaFiltrationPrompt, 'claude-haiku-4-5', true, systemPromptForQueryGeneration) + let filteredTables = await sails.helpers.ai.prompt.with({prompt: schemaFiltrationPrompt, baseModel: 'claude-haiku-4-5', expectJson: true, systemPromptForQueryGeneration}) .intercept((err)=>{ sails.log.warn(`When trying to get a subset of tables to use to generate a query for a user, an error occurred. Full error: ${require('util').inspect(err, {depth: 2})}`); if(this.req.isSocket){ @@ -173,7 +173,7 @@ module.exports = { "couldNotGenerateQueries": true }`; - let sqlReport = await sails.helpers.ai.prompt.with({prompt:sqlPrompt, baseModel:'claude-sonnet-4-6', expectJson: true, systemPromptForQueryGeneration}) + let sqlReport = await sails.helpers.ai.prompt.with({prompt: sqlPrompt, baseModel: 'claude-sonnet-4-6', expectJson: true, systemPromptForQueryGeneration}) .intercept((err)=>{ if(this.req.isSocket){ // If this request was from a socket and an error occurs, broadcast an 'error' event and unsubscribe the socket from this room. From 7d258c0c6f88d6caa512ab6e36ba1a3574fe17eb Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:15 +0000 Subject: [PATCH 28/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- cmd/fleetctl/fleetctl/convert.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/fleetctl/fleetctl/convert.go b/cmd/fleetctl/fleetctl/convert.go index c1e524c1b54..ca49d0f44d6 100644 --- a/cmd/fleetctl/fleetctl/convert.go +++ b/cmd/fleetctl/fleetctl/convert.go @@ -54,6 +54,14 @@ func convertPlatforms(platformsIn string) (string, error) { } } + // if more than one platform is present, the empty-string sentinel + // (meaning "all platforms") must not be mixed in, or it will corrupt + // the resulting platform CSV by introducing an empty segment that + // downstream parsers interpret as "match everything". + if _, ok := mapped[""]; ok && len(mapped) > 1 { + delete(mapped, "") + } + // convert set to slice result := make([]string, 0, len(mapped)) From d5a6e6447a849c90f6a83e508b9d33de1a6506bc Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:16 +0000 Subject: [PATCH 29/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../buttons/ActionButtons/ActionButtons.tsx | 88 ++++++++----------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/frontend/components/buttons/ActionButtons/ActionButtons.tsx b/frontend/components/buttons/ActionButtons/ActionButtons.tsx index 6f8358957a6..9b67982bfcb 100644 --- a/frontend/components/buttons/ActionButtons/ActionButtons.tsx +++ b/frontend/components/buttons/ActionButtons/ActionButtons.tsx @@ -25,6 +25,41 @@ interface IProps { actions: IActionButtonProps[]; } +const renderSecondaryAction = (action: IActionButtonProps): JSX.Element => { + const variant: ButtonVariant = action.buttonVariant ?? "inverse"; + const content = + action.buttonVariant !== "text-icon" ? ( + action.label + ) : ( + <> + {action.label} + {action.iconName && } + + ); + + if (action.gitOpsModeCompatible) { + return ( + ( + + )} + /> + ); + } + + return ( + + ); +}; + const ActionButtons = ({ baseClass, actions }: IProps): JSX.Element => { const primaryActions: IActionButtonProps[] = []; const secondaryActions: IActionButtonProps[] = []; @@ -55,56 +90,9 @@ const ActionButtons = ({ baseClass, actions }: IProps): JSX.Element => {
- {secondaryActions.map((action) => { - if (!action.hideAction && action.buttonVariant !== "text-icon") { - if (action.gitOpsModeCompatible) { - return ( - ( - - )} - /> - ); - } - return ( - - ); - } - if (action.gitOpsModeCompatible) { - return ( - ( - - )} - /> - ); - } - return ( - - ); - })} + {secondaryActions.map( + (action) => !action.hideAction && renderSecondaryAction(action) + )}
Date: Mon, 14 Sep 2026 06:43:17 +0000 Subject: [PATCH 30/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mdm/acme/internal/service/account_order.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/server/mdm/acme/internal/service/account_order.go b/server/mdm/acme/internal/service/account_order.go index eb399cd137b..125a5f3542d 100644 --- a/server/mdm/acme/internal/service/account_order.go +++ b/server/mdm/acme/internal/service/account_order.go @@ -104,12 +104,16 @@ func (s *Service) createOrderResponse( return nil, ctxerr.Wrap(ctx, err, "constructing finalize URL for account") } - var authzURL string + // NOTE: we only support a single authorization per order right now; if we add more we need to re-work this + var authzURLs []string if len(authorizations) == 1 { - authzURL, err = s.getACMEURLWithBaseURL(ctx, baseURL, enrollment.PathIdentifier, "authorizations", fmt.Sprint(authorizations[0].ID)) + authzURL, err := s.getACMEURLWithBaseURL(ctx, baseURL, enrollment.PathIdentifier, "authorizations", fmt.Sprint(authorizations[0].ID)) if err != nil { return nil, ctxerr.Wrap(ctx, err, "constructing authorization URL for account") } + authzURLs = []string{authzURL} + } else { + authzURLs = []string{} } var certURL string @@ -125,7 +129,7 @@ func (s *Service) createOrderResponse( Status: order.Status, Expires: enrollment.NotValidAfter, Identifiers: order.Identifiers, - Authorizations: []string{authzURL}, + Authorizations: authzURLs, Finalize: finalizeURL, Certificate: certURL, Location: orderURL, From e21b3a2ac154b57e5236bcaefa9d8685531f4d02 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:19 +0000 Subject: [PATCH 31/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/service/global_policies_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/service/global_policies_test.go b/server/service/global_policies_test.go index 06fe59fc8e1..595bacdbda1 100644 --- a/server/service/global_policies_test.go +++ b/server/service/global_policies_test.go @@ -159,6 +159,17 @@ func TestGlobalPoliciesAuth(t *testing.T) { // by ID" endpoint refuses to return a team policy to a user who has no role // on that team. This guards against the regression described in the // "Cross-Team Policy Data Exposure" disclosure. +// +// This test asserts behavior at the service layer only (via the svc.authz +// gate invoked inside GetPolicyByID). It does not by itself prove that the +// production authorization check in server/service/global_policies.go +// (GetPolicyByID) has not regressed on refactor: if a future change replaces +// or bypasses the svc.authz.Authorize call for fleet.ActionRead against the +// policy's *fleet.Team, these test cases would need to keep failing for that +// regression to be caught. Reviewers modifying GetPolicyByID must confirm the +// authorization check against the policy's TeamID (nil, 0, or a specific +// team) is still performed before returning policy data, not merely that +// these tests are green. func TestGetPolicyByIDCrossTeamAuth(t *testing.T) { ds := new(mock.Store) svc, ctx := newTestService(t, ds, nil, nil) From 87cc2f13f54d79d45bf75a11a9fea8c4e8861ef0 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:20 +0000 Subject: [PATCH 32/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- ...095046_AddUniqconstraintSoftwareIDOnSoftwareCVE.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/datastore/mysql/migrations/tables/20220708095046_AddUniqconstraintSoftwareIDOnSoftwareCVE.go b/server/datastore/mysql/migrations/tables/20220708095046_AddUniqconstraintSoftwareIDOnSoftwareCVE.go index b534b5e3a84..4629f8f1a75 100644 --- a/server/datastore/mysql/migrations/tables/20220708095046_AddUniqconstraintSoftwareIDOnSoftwareCVE.go +++ b/server/datastore/mysql/migrations/tables/20220708095046_AddUniqconstraintSoftwareIDOnSoftwareCVE.go @@ -96,15 +96,14 @@ func Up_20220708095046(tx *sql.Tx) error { // the constraint and new duplicates get generated in between, we need to try to acquire the // vulnerability lock. In case the lock can't be acquired a warning is issued and the migration // will proceed without it. + locked := false identifier, err := server.GenerateRandomText(64) if err != nil { logger.Warn.Println("Could not generate identifier for lock, might not be able to remove duplicates in a reliable way...") } else { - locked, err := acquireLock(tx, identifier) + locked, err = acquireLock(tx, identifier) if !locked || err != nil { logger.Warn.Println("Could not acquire lock, might not be able to remove duplicates in a reliable way...") - } else { - defer releaseLock(tx, identifier) //nolint:errcheck } } @@ -116,8 +115,10 @@ func Up_20220708095046(tx *sql.Tx) error { return err } - if err := releaseLock(tx, identifier); err != nil { - return err + if locked { + if err := releaseLock(tx, identifier); err != nil { + return err + } } return nil From d60f95c63d16d9254248228d6b5c575a001d0ad7 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:21 +0000 Subject: [PATCH 33/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- server/mock/datastore.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/server/mock/datastore.go b/server/mock/datastore.go index a49ee60022b..bcaf0efed6d 100644 --- a/server/mock/datastore.go +++ b/server/mock/datastore.go @@ -35,10 +35,15 @@ func (m *Store) GetCurrentTime(ctx context.Context) (time.Time, error) { return time.Time{}, nil } -func (m *Store) Drop() error { return nil } -func (m *Store) MigrateTables(ctx context.Context) error { return nil } -func (m *Store) MigrateData(ctx context.Context) error { return nil } -func (m *Store) MigrateOpenframe(ctx context.Context) error { return nil } +// NOTE: Drop, MigrateTables, MigrateData, MigrateOpenframe, MigrationStatus and Name +// are deliberately hand-written here rather than generated by mockimpl, since they +// are simple no-op stubs used across tests. If fleet.Datastore's method set changes +// for any of these methods, the compiler will fail to satisfy the +// `var _ fleet.Datastore = (*Store)(nil)` assertion above, surfacing the drift. +func (m *Store) Drop() error { return nil } +func (m *Store) MigrateTables(ctx context.Context) error { return nil } +func (m *Store) MigrateData(ctx context.Context) error { return nil } +func (m *Store) MigrateOpenframe(ctx context.Context) error { return nil } func (m *Store) MigrationStatus(ctx context.Context) (*fleet.MigrationStatus, error) { return &fleet.MigrationStatus{}, nil } From f81155ea46628924f1580d87b567809ae213ad2e Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:22 +0000 Subject: [PATCH 34/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- tools/seed_data/queries/seed_queries.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tools/seed_data/queries/seed_queries.go b/tools/seed_data/queries/seed_queries.go index c7e1f00dd19..7f36630a901 100644 --- a/tools/seed_data/queries/seed_queries.go +++ b/tools/seed_data/queries/seed_queries.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "log" + "os" "strings" _ "github.com/go-sql-driver/mysql" @@ -14,13 +15,20 @@ const ( totalRecords = 1000000 ) +func getEnvOrDefault(key, defaultValue string) string { + if value, ok := os.LookupEnv(key); ok { + return value + } + return defaultValue +} + func main() { - // MySQL connection details from your Docker Compose file - user := "fleet" - password := "insecure" - host := "localhost" // Assuming you are running this script on the same host as Docker - port := "3306" - database := "fleet" + // MySQL connection details, overridable via environment variables for local dev + user := getEnvOrDefault("SEED_MYSQL_USER", "fleet") + password := getEnvOrDefault("SEED_MYSQL_PASSWORD", "insecure") + host := getEnvOrDefault("SEED_MYSQL_HOST", "localhost") // Assuming you are running this script on the same host as Docker + port := getEnvOrDefault("SEED_MYSQL_PORT", "3306") + database := getEnvOrDefault("SEED_MYSQL_DATABASE", "fleet") // Construct the MySQL DSN (Data Source Name) dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", user, password, host, port, database) From b34b25baf3e61e9d73cb4d9655150d12eafc5ad7 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:24 +0000 Subject: [PATCH 35/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../entrance/update-password-and-login.js | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js b/ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js index 51a75b8746d..ec371d449df 100644 --- a/ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js +++ b/ee/vulnerability-dashboard/api/controllers/entrance/update-password-and-login.js @@ -34,6 +34,11 @@ module.exports = { invalidToken: { description: 'The provided password token is invalid, expired, or has already been used.', responseType: 'expired' + }, + + tooManyAttempts: { + description: 'Too many invalid password token attempts have been made from this requester recently.', + responseType: 'tooManyRequests' } }, @@ -45,14 +50,38 @@ module.exports = { throw 'invalidToken'; } + // Rate limit / lockout repeated invalid token attempts from this requesting + // user agent, to make brute-forcing a valid reset token impractical. + var rateLimitKey = 'passwordResetAttempts::' + this.req.ip; + sails._passwordResetAttemptsByKey = sails._passwordResetAttemptsByKey || {}; + var attemptRecord = sails._passwordResetAttemptsByKey[rateLimitKey]; + var now = Date.now(); + var attemptWindowMs = 15 * 60 * 1000; // 15 minutes + var maxAttempts = 10; + + if (attemptRecord && (now - attemptRecord.firstAttemptAt) < attemptWindowMs && attemptRecord.count >= maxAttempts) { + throw 'tooManyAttempts'; + } + // Look up the user with this reset token. var userRecord = await User.findOne({ passwordResetToken: token }); // If no such user exists, or their token is expired, bail. if (!userRecord || userRecord.passwordResetTokenExpiresAt <= Date.now()) { + + // Track this invalid attempt for rate limiting purposes. + if (!attemptRecord || (now - attemptRecord.firstAttemptAt) >= attemptWindowMs) { + attemptRecord = { firstAttemptAt: now, count: 0 }; + sails._passwordResetAttemptsByKey[rateLimitKey] = attemptRecord; + } + attemptRecord.count++; + throw 'invalidToken'; } + // On a successful token match, clear any tracked invalid attempts for this requester. + delete sails._passwordResetAttemptsByKey[rateLimitKey]; + // Hash the new password. var hashed = await sails.helpers.passwords.hashPassword(password); @@ -78,3 +107,4 @@ module.exports = { }; + From 47f5a54884fcc4e0dbdb09c6da52bb5f818e62fd Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:25 +0000 Subject: [PATCH 36/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- frontend/services/entities/sessions.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/frontend/services/entities/sessions.ts b/frontend/services/entities/sessions.ts index db5796db867..cad4e29b90e 100644 --- a/frontend/services/entities/sessions.ts +++ b/frontend/services/entities/sessions.ts @@ -26,6 +26,16 @@ export interface ILoginResponse { token_expires_at?: string; } +export class MfaRequiredError extends Error { + response: unknown; + + constructor(rawResponse: unknown) { + super("MFA required"); + this.name = "MfaRequiredError"; + this.response = rawResponse; + } +} + export default { login: ({ email, password }: ILoginProps): Promise => { const { LOGIN } = endpoints; @@ -45,7 +55,7 @@ export default { ).then((rawResponse) => { if (rawResponse.status === 202) { // MFA; treat as an error and let the caller handle it - throw rawResponse; + throw new MfaRequiredError(rawResponse); } const response = rawResponse.data; const { user } = response; From 27a221df7a630f4243810f668557f4ec3e077e25 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:26 +0000 Subject: [PATCH 37/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../tables/20260401153000_AddACMEAndRenameSCEPDepotTables.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/datastore/mysql/migrations/tables/20260401153000_AddACMEAndRenameSCEPDepotTables.go b/server/datastore/mysql/migrations/tables/20260401153000_AddACMEAndRenameSCEPDepotTables.go index 34a1ae2b8d6..c72c0482a60 100644 --- a/server/datastore/mysql/migrations/tables/20260401153000_AddACMEAndRenameSCEPDepotTables.go +++ b/server/datastore/mysql/migrations/tables/20260401153000_AddACMEAndRenameSCEPDepotTables.go @@ -88,7 +88,7 @@ func Up_20260401153000(tx *sql.Tx) error { created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP, updated_at timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), - FOREIGN KEY (acme_account_id) REFERENCES acme_accounts(id) ON DELETE CASCADE ON UPDATE CASCADE, + FOREIGN KEY (acme_account_id) REFERENCES acme_accounts(id) ON DELETE RESTRICT ON UPDATE CASCADE, UNIQUE KEY idx_issued_certificate_serial (issued_certificate_serial) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; `) From ef47d5d82d141e490d68d16d7ac15e5429345e2f Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:27 +0000 Subject: [PATCH 38/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- tools/mdm/apple/setupexperience/main.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/mdm/apple/setupexperience/main.go b/tools/mdm/apple/setupexperience/main.go index 300250cfbd7..07b44476373 100644 --- a/tools/mdm/apple/setupexperience/main.go +++ b/tools/mdm/apple/setupexperience/main.go @@ -24,6 +24,9 @@ import ( func main() { mysqlAddr := flag.String("mysql", "localhost:3306", "mysql address") + flagDBUser := flag.String("mysql-user", "fleet", "mysql username") + flagDBPass := flag.String("mysql-pass", "insecure", "mysql password") + flagDBName := flag.String("mysql-db", "fleet", "mysql database name") serverPrivateKey := flag.String("server-private-key", "", "fleet server's private key (to decrypt MDM assets)") hostUUID := flag.String("host-uuid", "", "the host serial # to enqueue setup items for") @@ -43,9 +46,9 @@ func main() { mysqlConf := config.MysqlConfig{ Protocol: "tcp", Address: *mysqlAddr, - Database: "fleet", - Username: "fleet", - Password: "insecure", + Database: *flagDBName, + Username: *flagDBUser, + Password: *flagDBPass, MaxOpenConns: 50, MaxIdleConns: 50, ConnMaxLifetime: 0, From 8652817ef43b50292c1a1139c20ba0ccd8bfd2ef Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:28 +0000 Subject: [PATCH 39/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- .../controllers/entrance/send-password-recovery-email.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/api/controllers/entrance/send-password-recovery-email.js b/website/api/controllers/entrance/send-password-recovery-email.js index 46ce374f55f..4f9413039fc 100644 --- a/website/api/controllers/entrance/send-password-recovery-email.js +++ b/website/api/controllers/entrance/send-password-recovery-email.js @@ -16,9 +16,9 @@ module.exports = { required: true }, - websiteUrl: { + company: { type: 'string', - description: 'Honeypot field. If filled, the submission is silently discarded.' + description: 'Optional field.' } }, @@ -33,9 +33,9 @@ module.exports = { }, - fn: async function ({emailAddress, websiteUrl}) { + fn: async function ({emailAddress, company}) { - if (websiteUrl) { return; }// Honeypot input provided — return a success response + if (company) { return; }// Honeypot input provided — return a success response // Find the record for this user. // (Even if no such user exists, pretend it worked to discourage sniffing.) @@ -71,3 +71,4 @@ module.exports = { }; + From fc7942aafb1aef920cab603a853312b9e0c0a5c3 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 06:43:29 +0000 Subject: [PATCH 40/40] fix(adhoc-sweep-fixes): 61 review findings across 40 files --- tools/mdm/windows/bitlocker/core.go | 299 ++++++++++++++++------------ 1 file changed, 167 insertions(+), 132 deletions(-) diff --git a/tools/mdm/windows/bitlocker/core.go b/tools/mdm/windows/bitlocker/core.go index 376947712ae..bc5e05293eb 100755 --- a/tools/mdm/windows/bitlocker/core.go +++ b/tools/mdm/windows/bitlocker/core.go @@ -1,132 +1,167 @@ -package main - -import ( - "flag" - "fmt" -) - -func BitlockerEncryptionNumericalPassword(encryptionPassword string) error { - - // Connect to the volume - vol, err := Connect("c:") - if err != nil { - return fmt.Errorf("there was an error connecting to the volume - error: %v", err) - } - defer vol.Close() - - // Prepare for encryption - if err := vol.Prepare(VolumeTypeDefault, EncryptionTypeSoftware); err != nil { - return fmt.Errorf("there was an error preparing the volume for encryption - error: %v", err) - } - - // Add a recovery protector - - if err := vol.ProtectWithNumericalPassword(encryptionPassword); err != nil { - return fmt.Errorf("there was an error adding a recovery protector - error: %v", err) - } - - // Protect with TPM - if err := vol.ProtectWithTPM(nil); err != nil { - return fmt.Errorf("there was an error protecting with TPM - error: %v", err) - } - - // Start encryption - if err := vol.Encrypt(XtsAES256, EncryptDataOnly); err != nil { - return fmt.Errorf("there was an error starting encryption - error: %v", err) - } - - return nil -} - -func BitlockerDecryption() error { - - // Connect to the volume - vol, err := Connect("c:") - if err != nil { - return fmt.Errorf("there was an error connecting to the volume - error: %v", err) - } - defer vol.Close() - - // Start decryption - if err := vol.Decrypt(); err != nil { - return fmt.Errorf("there was an error starting decryption - error: %v", err) - } - - return nil -} - -func GetBitlockerStatus() (*EncryptionStatus, error) { - - // Connect to the volume - vol, err := Connect("c:") - if err != nil { - return nil, fmt.Errorf("there was an error connecting to the volume - error: %v", err) - } - defer vol.Close() - - // Get volume status - status, err := vol.GetBitlockerStatus() - if err != nil { - return nil, fmt.Errorf("there was an error starting decryption - error: %v", err) - } - - return status, nil -} - -func main() { - - enableBitlocker := flag.Bool("encrypt", false, "encrypt the drive") - disableBitlocker := flag.Bool("decrypt", false, "decrypt the drive") - statusBitlocker := flag.Bool("status", true, "get drive status") - - flag.Parse() - - if *enableBitlocker { - fmt.Println("About to attempt enabling bitlocker") - - //This needs to be generated with algorithm defined at - //https://learn.microsoft.com/en-us/windows/win32/secprov/getkeyprotectornumericalpassword-win32-encryptablevolume - newPassword := "527230-472395-606199-107525-536789-168927-479336-471856" - - err := BitlockerEncryptionNumericalPassword(newPassword) - if err != nil { - fmt.Printf("bitlocker encryption error - %v\n", err) - return - } - - fmt.Println("Bitlocker encryption started!") - - } else if *disableBitlocker { - fmt.Println("About to attempt disabling bitlocker") - - err := BitlockerDecryption() - if err != nil { - fmt.Printf("bitlocker decryption error - %v\n", err) - return - } - - fmt.Println("Bitlocker decryption started!") - - } else if *statusBitlocker { - fmt.Println("About to get encryption status bitlocker") - - status, err := GetBitlockerStatus() - if err != nil { - fmt.Printf("bitlocker decryption error - %v\n", err) - return - } - - fmt.Println("Protection status: ", status.ProtectionStatusDesc) - fmt.Println("Conversion status: ", status.ConversionStatusDesc) - fmt.Println("Encryption Flags: ", status.EncryptionFlags) - fmt.Println("Wiping Status description: ", status.WipingStatusDesc) - fmt.Println("Encryption percentage complete: ", status.EncryptionPercentage) - fmt.Println("Wiping percentage complete: ", status.WipingPercentage) - - fmt.Println("Bitlocker encryption status gathered!") - - } else { - fmt.Println("You must specify either -encrypt, -decrypt or -status") - return - } -} +package main + +import ( + "crypto/rand" + "flag" + "fmt" +) + +func BitlockerEncryptionNumericalPassword(encryptionPassword string) error { + + // Connect to the volume + vol, err := Connect("c:") + if err != nil { + return fmt.Errorf("there was an error connecting to the volume: %w", err) + } + defer vol.Close() + + // Prepare for encryption + if err := vol.Prepare(VolumeTypeDefault, EncryptionTypeSoftware); err != nil { + return fmt.Errorf("there was an error preparing the volume for encryption: %w", err) + } + + // Add a recovery protector + + if err := vol.ProtectWithNumericalPassword(encryptionPassword); err != nil { + return fmt.Errorf("there was an error adding a recovery protector: %w", err) + } + + // Protect with TPM + if err := vol.ProtectWithTPM(nil); err != nil { + return fmt.Errorf("there was an error protecting with TPM: %w", err) + } + + // Start encryption + if err := vol.Encrypt(XtsAES256, EncryptDataOnly); err != nil { + return fmt.Errorf("there was an error starting encryption: %w", err) + } + + return nil +} + +func BitlockerDecryption() error { + + // Connect to the volume + vol, err := Connect("c:") + if err != nil { + return fmt.Errorf("there was an error connecting to the volume: %w", err) + } + defer vol.Close() + + // Start decryption + if err := vol.Decrypt(); err != nil { + return fmt.Errorf("there was an error starting decryption: %w", err) + } + + return nil +} + +func GetBitlockerStatus() (*EncryptionStatus, error) { + + // Connect to the volume + vol, err := Connect("c:") + if err != nil { + return nil, fmt.Errorf("there was an error connecting to the volume: %w", err) + } + defer vol.Close() + + // Get volume status + status, err := vol.GetBitlockerStatus() + if err != nil { + return nil, fmt.Errorf("there was an error getting bitlocker status: %w", err) + } + + return status, nil +} + +// generateNumericalRecoveryPassword generates a random 48-digit BitLocker +// numerical recovery password formatted as 8 groups of 6 digits, per +// https://learn.microsoft.com/en-us/windows/win32/secprov/getkeyprotectornumericalpassword-win32-encryptablevolume +func generateNumericalRecoveryPassword() (string, error) { + const groups = 8 + password := "" + for i := 0; i < groups; i++ { + if i > 0 { + password += "-" + } + + max := int64(1000000) // 6 digits, 0-999999 + b := make([]byte, 8) + if _, err := rand.Read(b); err != nil { + return "", fmt.Errorf("there was an error generating a random recovery password: %w", err) + } + + var n int64 + for _, v := range b { + n = (n << 8) | int64(v) + } + if n < 0 { + n = -n + } + n = n % max + + password += fmt.Sprintf("%06d", n) + } + + return password, nil +} + +func main() { + + enableBitlocker := flag.Bool("encrypt", false, "encrypt the drive") + disableBitlocker := flag.Bool("decrypt", false, "decrypt the drive") + statusBitlocker := flag.Bool("status", true, "get drive status") + + flag.Parse() + + if *enableBitlocker { + fmt.Println("About to attempt enabling bitlocker") + + newPassword, err := generateNumericalRecoveryPassword() + if err != nil { + fmt.Printf("bitlocker encryption error - %v\n", err) + return + } + + err = BitlockerEncryptionNumericalPassword(newPassword) + if err != nil { + fmt.Printf("bitlocker encryption error - %v\n", err) + return + } + + fmt.Println("Bitlocker encryption started!") + + } else if *disableBitlocker { + fmt.Println("About to attempt disabling bitlocker") + + err := BitlockerDecryption() + if err != nil { + fmt.Printf("bitlocker decryption error - %v\n", err) + return + } + + fmt.Println("Bitlocker decryption started!") + + } else if *statusBitlocker { + fmt.Println("About to get encryption status bitlocker") + + status, err := GetBitlockerStatus() + if err != nil { + fmt.Printf("bitlocker decryption error - %v\n", err) + return + } + + fmt.Println("Protection status: ", status.ProtectionStatusDesc) + fmt.Println("Conversion status: ", status.ConversionStatusDesc) + fmt.Println("Encryption Flags: ", status.EncryptionFlags) + fmt.Println("Wiping Status description: ", status.WipingStatusDesc) + fmt.Println("Encryption percentage complete: ", status.EncryptionPercentage) + fmt.Println("Wiping percentage complete: ", status.WipingPercentage) + + fmt.Println("Bitlocker encryption status gathered!") + + } else { + fmt.Println("You must specify either -encrypt, -decrypt or -status") + return + } +}