Commit graph

3486 commits

Author SHA1 Message Date
Jordan Montgomery
7593d102fb
Experimental fleet server config for custom updates & disk encryption settings (#34598)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33316

Merges in changes made in this community PR:
https://github.com/fleetdm/fleet/pull/33665

Adds support for Windows and tests, also blocks the feature on fleet
free

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [x] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Wesley Whetstone <wesw@stripe.com>
Co-authored-by: Wesley Whetstone <jckwhet@gmail.com>
2025-10-22 13:51:10 -04:00
Sarah Gillespie
97cf97ca3e
Adjust UI section headers and layout of Settings > Integrations (#34585) 2025-10-21 15:28:47 -05:00
Tim Lee
d4004a4f8e
IDP user update API (#34332) 2025-10-21 12:02:25 -06:00
jacobshandling
f700d1029e
Hide software host count, versions table when no hosts have the software installed (#34484)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #32635
Hide host count and version table when no hosts have the software
installed

<img width="1518" height="568" alt="Screenshot 2025-10-17 at 4 36 13 PM"
src="https://github.com/user-attachments/assets/4f77d039-d9d0-427e-a3f4-a8774a3f6ff7"
/>



- [x] Changes file added for user-visible changes in `changes/`
- [x] QA'd all new/changed functionality manually
2025-10-20 14:46:35 -07:00
Ian Littman
75a3af5b43
Don't try to pull config profiles when MDM isn't enabled for any platform when generating GitOps YAML (#34549)
Fixes #33677.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually
2025-10-20 15:47:44 -05:00
Luke Heath
2c8ae8cc78
Adding changes for Fleet v4.75.0 (#33583) (#34483) 2025-10-17 21:51:17 -05:00
jacobshandling
c9a14d5038
Update vuln processing guide, add change file for JetBrains IDE extension vulns (#34478)
Addresses #32266 

Update docs, add change file
2025-10-17 17:39:21 -05:00
Sarah Gillespie
e11ddc9866
Support $FLEET_VAR_SCEP_RENEWAL_ID in OU field (#34403) 2025-10-17 12:47:04 -05:00
Scott Gress
81f589d661
Update add script UI (#34349)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #32632

# Details

This PR updates the Script Library page in the following ways:
* When no scripts are uploaded for a team, it shows the "Add script" UI
with a button that opens a new "Add Script" modal
* When scripts are uploaded, the "Add script" button is instead added to
the header of the scripts list, and clicking it opens that modal

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [ ] Added/updated automated tests
working on this

- [X] QA'd all new/changed functionality manually
- [X] Test empty state: go to controls/scripts/library for a team with
no scripts. Clicking "upload" button in empty state should open the add
script modal.
- [X] In the modal, select a .ps1 script. Should not see additional
text.
- [X] Close modal without uploading. Re-open. File field should be
cleared & upload button visible again.
- [X] Select a .sh script. Should see additional text about macOS and
Linux.
  - [X] Add script. Make sure script saves and modal closes.
- [X] Once script has been added, make sure empty state is gone and "Add
script" button is at the top of the list.
- [X] Go to /controls/os-settings/custom-settings for a team with no
profiles uploaded. Make sure empty state text styles match the empty
state for script uploads.
- [X] Open modal to add profile. Make sure upload text styles match the
script upload modal.
- [X] Enable GitOps mode. Go to controls/scripts/library for a team with
scripts added. Make sure new "Add script" button is disabled w/ standard
tooltip in GitOps mode.

Scripts empty state:

<img width="697" height="352" alt="image"
src="https://github.com/user-attachments/assets/32f0f246-bddb-4bb7-bc39-48d9978de9fa"
/>

Scripts uploader:

<img width="745" height="590" alt="image"
src="https://github.com/user-attachments/assets/f82414e2-9318-4543-b5ca-41e759662587"
/>

Scripts uploader with .sh

<img width="750" height="539" alt="image"
src="https://github.com/user-attachments/assets/0b989067-921a-4d18-93ed-09aac90fc9cb"
/>

Scripts table:

<img width="686" height="256" alt="image"
src="https://github.com/user-attachments/assets/848f1b56-6e9e-48d4-9a03-6fdf5427301e"
/>

Profiles empty state:

<img width="700" height="377" alt="image"
src="https://github.com/user-attachments/assets/8f92bcd9-2215-41f6-a540-4774f7e9542b"
/>

Profiles uploader:

<img width="707" height="682" alt="image"
src="https://github.com/user-attachments/assets/eef216af-3447-48e7-882a-e42e888e1c17"
/>
2025-10-17 10:49:59 -05:00
jacobshandling
5f626e2a8c
Add gigs_all_disk_space vital collection, storage, service, and UI rendering for Linux hosts (#34077)
## Addresses #31671 

- [x] Changes file added for user-visible changes in `changes/`
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added total disk space metrics for all partitions on Linux hosts. The
disk space indicator now displays comprehensive storage information
including root partition and all other partitions, improving visibility
into host storage capacity.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2025-10-17 08:24:23 -07:00
Victor Lyuboslavsky
9295a82e83
Improved MySQL query performance software versions and vulnerabilities endpoints (#34262)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #32178

Software optimization: skipping an unnecessary software_cve join when
vulnerability details are not needed. Vulnerabilities are still
returned, so functionality remains unchanged.

Vulnerabilities optimization: Query vulnerability_host_counts directly
and LEFT JOIN for metadata. This eliminates the expensive UNION of all
CVE rows that was causing performance issues.

Previous approach: UNION all CVEs (many rows) → JOIN
vulnerability_host_counts → filter
New approach: Start with filtered vulnerability_host_counts → LEFT JOIN
for metadata
This reduces the working set before any expensive operations

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [ ] Added/updated automated tests (see below for the test)
- [x] QA'd all new/changed functionality manually
- Planning to test in loadtest after also improving the software
endpoint

Performance test for replicating the problem and testing the fix:
```go
package mysql

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/fleetdm/fleet/v4/server/fleet"
	"github.com/fleetdm/fleet/v4/server/ptr"
	"github.com/fleetdm/fleet/v4/server/test"
	"github.com/stretchr/testify/require"
)

// TestListVulnerabilitiesPerformance is a performance test that replicates
// the production performance problem with ListVulnerabilities.
//
// This test creates a realistic dataset with thousands of CVEs and measures
// query performance under various conditions. Run with:
//
//	go test -v -run TestListVulnerabilitiesPerformance ./server/datastore/mysql
//
// To see detailed timing output, set the environment variable:
//
//	VERBOSE=1 go test -v -run TestListVulnerabilitiesPerformance ./server/datastore/mysql
func TestListVulnerabilitiesPerformance(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping performance test in short mode")
	}

	ds := CreateMySQLDS(t)
	defer TruncateTables(t, ds)

	ctx := context.Background()

	// Create a realistic dataset
	t.Log("Setting up test data...")
	setupPerformanceTestData(t, ds)
	t.Log("Test data setup complete")

	// Test cases covering common query patterns
	testCases := []struct {
		name string
		opts fleet.VulnListOptions
	}{
		{
			name: "Global list - first page, sorted by host count",
			opts: fleet.VulnListOptions{
				IsEE: true,
				ListOptions: fleet.ListOptions{
					Page:           0,
					PerPage:        20,
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
		{
			name: "Team 1 list - first page, sorted by host count",
			opts: fleet.VulnListOptions{
				IsEE:   true,
				TeamID: ptr.Uint(1),
				ListOptions: fleet.ListOptions{
					Page:           0,
					PerPage:        20,
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
		{
			name: "Team 1 list - with exploit filter",
			opts: fleet.VulnListOptions{
				IsEE:         true,
				TeamID:       ptr.Uint(1),
				KnownExploit: true,
				ListOptions: fleet.ListOptions{
					Page:           0,
					PerPage:        20,
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
		{
			name: "Global list - with CVE search",
			opts: fleet.VulnListOptions{
				IsEE: true,
				ListOptions: fleet.ListOptions{
					Page:           0,
					PerPage:        20,
					MatchQuery:     "2023",
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
		{
			name: "Global list - second page",
			opts: fleet.VulnListOptions{
				IsEE: true,
				ListOptions: fleet.ListOptions{
					Page:           1,
					PerPage:        20,
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
		{
			name: "Free version - global list",
			opts: fleet.VulnListOptions{
				IsEE: false,
				ListOptions: fleet.ListOptions{
					Page:           0,
					PerPage:        20,
					OrderKey:       "hosts_count",
					OrderDirection: fleet.OrderDescending,
				},
			},
		},
	}

	// Run performance tests
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			// Warm up the query cache
			_, _, err := ds.ListVulnerabilities(ctx, tc.opts)
			require.NoError(t, err)

			// Measure query performance
			const iterations = 5
			var totalDuration time.Duration

			for i := 0; i < iterations; i++ {
				start := time.Now()
				vulns, meta, err := ds.ListVulnerabilities(ctx, tc.opts)
				duration := time.Since(start)
				totalDuration += duration

				require.NoError(t, err)
				require.NotNil(t, meta)
				require.NotEmpty(t, vulns, "expected vulnerabilities to be returned")

				if i == 0 {
					t.Logf("  First run: %v (returned %d results)", duration, len(vulns))
				}
			}

			avgDuration := totalDuration / iterations
			t.Logf("  Average of %d runs: %v", iterations, avgDuration)

			// Performance assertions
			// These thresholds represent the current performance problem
			// After optimization, these should be reduced significantly
			if avgDuration > 2*time.Second {
				t.Logf("  ⚠️  WARNING: Query took %v (>2s) - performance issue detected", avgDuration)
			} else if avgDuration > 500*time.Millisecond {
				t.Logf("  ⚠️  SLOW: Query took %v (>500ms)", avgDuration)
			} else {
				t.Logf("  ✓ GOOD: Query took %v (<500ms)", avgDuration)
			}
		})
	}

	// Test count query performance
	t.Run("Count vulnerabilities performance", func(t *testing.T) {
		opts := fleet.VulnListOptions{
			IsEE: true,
		}

		// Warm up
		_, err := ds.CountVulnerabilities(ctx, opts)
		require.NoError(t, err)

		// Measure
		const iterations = 5
		var totalDuration time.Duration

		for i := 0; i < iterations; i++ {
			start := time.Now()
			count, err := ds.CountVulnerabilities(ctx, opts)
			duration := time.Since(start)
			totalDuration += duration

			require.NoError(t, err)
			require.Greater(t, count, uint(0))

			if i == 0 {
				t.Logf("  First run: %v (count=%d)", duration, count)
			}
		}

		avgDuration := totalDuration / iterations
		t.Logf("  Average of %d runs: %v", iterations, avgDuration)

		if avgDuration > 2*time.Second {
			t.Logf("  ⚠️  WARNING: Count query took %v (>2s)", avgDuration)
		} else if avgDuration > 500*time.Millisecond {
			t.Logf("  ⚠️  SLOW: Count query took %v (>500ms)", avgDuration)
		} else {
			t.Logf("  ✓ GOOD: Count query took %v (<500ms)", avgDuration)
		}
	})
}

// BenchmarkListVulnerabilities provides benchmark results for ListVulnerabilities.
// Run with:
//
//	go test -bench=BenchmarkListVulnerabilities -benchmem -run=^$ ./server/datastore/mysql
func BenchmarkListVulnerabilities(b *testing.B) {
	ds := CreateMySQLDSForBenchmark(b)
	defer TruncateTables(b, ds)

	ctx := context.Background()

	// Setup test data
	setupPerformanceTestData(b, ds)

	b.ResetTimer()

	// Benchmark the most common query pattern
	opts := fleet.VulnListOptions{
		IsEE: true,
		ListOptions: fleet.ListOptions{
			Page:           0,
			PerPage:        20,
			OrderKey:       "hosts_count",
			OrderDirection: fleet.OrderDescending,
		},
	}

	for i := 0; i < b.N; i++ {
		_, _, err := ds.ListVulnerabilities(ctx, opts)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkListVulnerabilitiesWithTeam benchmarks team-specific queries
func BenchmarkListVulnerabilitiesWithTeam(b *testing.B) {
	ds := CreateMySQLDSForBenchmark(b)
	defer TruncateTables(b, ds)

	ctx := context.Background()
	setupPerformanceTestData(b, ds)

	b.ResetTimer()

	opts := fleet.VulnListOptions{
		IsEE:   true,
		TeamID: ptr.Uint(1),
		ListOptions: fleet.ListOptions{
			Page:           0,
			PerPage:        20,
			OrderKey:       "hosts_count",
			OrderDirection: fleet.OrderDescending,
		},
	}

	for i := 0; i < b.N; i++ {
		_, _, err := ds.ListVulnerabilities(ctx, opts)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// BenchmarkCountVulnerabilities benchmarks the count query
func BenchmarkCountVulnerabilities(b *testing.B) {
	ds := CreateMySQLDSForBenchmark(b)
	defer TruncateTables(b, ds)

	ctx := context.Background()
	setupPerformanceTestData(b, ds)

	b.ResetTimer()

	opts := fleet.VulnListOptions{
		IsEE: true,
	}

	for i := 0; i < b.N; i++ {
		_, err := ds.CountVulnerabilities(ctx, opts)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// setupPerformanceTestData creates a realistic dataset that mimics production
// This creates:
// - ~80,000+ unique CVEs (matching production scale)
// - ~73,000 software_cve entries
// - ~35,000 operating_system_vulnerabilities entries
// - Multiple teams
// - Various host counts per vulnerability
//
// Note: This will take several minutes to run but will replicate production performance issues
func setupPerformanceTestData(t testing.TB, ds *Datastore) {
	ctx := context.Background()

	// Create 100 hosts across different teams and OS types (doubled from 50)
	// More hosts = more realistic host count distributions
	hosts := make([]*fleet.Host, 100)
	for i := 0; i < 100; i++ {
		hosts[i] = test.NewHost(t, ds, fmt.Sprintf("host%d", i),
			fmt.Sprintf("192.168.1.%d", i%255+1), // Handle more than 255 hosts
			fmt.Sprintf("key%d", i),
			fmt.Sprintf("uuid%d", i),
			time.Now())
	}

	// Create 3 teams
	team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "Engineering"})
	require.NoError(t, err)

	team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "Sales"})
	require.NoError(t, err)

	team3, err := ds.NewTeam(ctx, &fleet.Team{Name: "Support"})
	require.NoError(t, err)

	// Distribute hosts across teams
	// 40 hosts in team1, 30 in team2, 20 in team3, 10 with no team
	err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team1.ID, getHostIDs(hosts[0:40])))
	require.NoError(t, err)

	err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team2.ID, getHostIDs(hosts[40:70])))
	require.NoError(t, err)

	err = ds.AddHostsToTeam(ctx, fleet.NewAddHostsToTeamParams(&team3.ID, getHostIDs(hosts[70:90])))
	require.NoError(t, err)

	// Set up OS versions (Windows, macOS, Ubuntu)
	windowsOS := fleet.OperatingSystem{
		Name:     "Microsoft Windows 11 Enterprise",
		Version:  "10.0.22621.2715",
		Arch:     "x86_64",
		Platform: "windows",
	}

	macOS := fleet.OperatingSystem{
		Name:     "macOS",
		Version:  "14.1.2",
		Arch:     "arm64",
		Platform: "darwin",
	}

	ubuntuOS := fleet.OperatingSystem{
		Name:     "Ubuntu",
		Version:  "22.04",
		Arch:     "x86_64",
		Platform: "ubuntu",
	}

	// Assign OS to hosts: 50 Windows, 30 macOS, 20 Ubuntu
	for i := 0; i < 50; i++ {
		err = ds.UpdateHostOperatingSystem(ctx, hosts[i].ID, windowsOS)
		require.NoError(t, err)
	}
	for i := 50; i < 80; i++ {
		err = ds.UpdateHostOperatingSystem(ctx, hosts[i].ID, macOS)
		require.NoError(t, err)
	}
	for i := 80; i < 100; i++ {
		err = ds.UpdateHostOperatingSystem(ctx, hosts[i].ID, ubuntuOS)
		require.NoError(t, err)
	}

	err = ds.UpdateOSVersions(ctx)
	require.NoError(t, err)

	// Create realistic CVE distribution matching production scale
	// In production, we see:
	// - ~73,000 software CVEs
	// - ~35,000 OS CVEs
	// - Many CVEs overlap between software and OS
	// - ~80,000 unique CVEs after deduplication

	// We now create production-scale data:
	// - 50,000 software CVEs (70% of production)
	// - 30,000 OS CVEs (85% of production)
	// - Some overlap to create ~80,000 total CVEs after UNION
	// This should replicate the 500-1000ms+ query times seen in production

	t.Log("Creating software vulnerabilities... (this will take a few minutes)")
	createSoftwareVulnerabilities(t, ds, hosts[:60], 50000)

	t.Log("Creating OS vulnerabilities...")
	createOSVulnerabilities(t, ds, 30000)

	t.Log("Creating CVE metadata...")
	createCVEMetadata(t, ds, 80000)

	t.Log("Updating vulnerability host counts...")
	err = ds.UpdateVulnerabilityHostCounts(ctx, 10)
	require.NoError(t, err)

	t.Log("Setup complete - ready for performance testing")
}

// createSoftwareVulnerabilities creates software entries and their CVEs
func createSoftwareVulnerabilities(t testing.TB, ds *Datastore, hosts []*fleet.Host, numCVEs int) {
	ctx := context.Background()

	// Create more software packages to better distribute CVEs
	softwarePackages := []fleet.Software{
		{Name: "Chrome", Version: "120.0.1", Source: "programs"},
		{Name: "Firefox", Version: "121.0", Source: "programs"},
		{Name: "Node.js", Version: "18.19.0", Source: "programs"},
		{Name: "Python", Version: "3.11.7", Source: "programs"},
		{Name: "Docker", Version: "24.0.7", Source: "programs"},
		{Name: "nginx", Version: "1.24.0", Source: "deb_packages"},
		{Name: "postgresql", Version: "15.5", Source: "deb_packages"},
		{Name: "redis", Version: "7.2.3", Source: "deb_packages"},
		{Name: "mysql", Version: "8.0.35", Source: "deb_packages"},
		{Name: "git", Version: "2.43.0", Source: "deb_packages"},
		{Name: "openssl", Version: "3.0.12", Source: "deb_packages"},
		{Name: "curl", Version: "8.5.0", Source: "deb_packages"},
		{Name: "vim", Version: "9.0", Source: "deb_packages"},
		{Name: "apache2", Version: "2.4.58", Source: "deb_packages"},
		{Name: "php", Version: "8.2.14", Source: "deb_packages"},
	}

	// Install software on hosts
	for i, host := range hosts {
		// Each host gets 5-8 software packages
		numPackages := 5 + (i % 4)
		if numPackages > len(softwarePackages) {
			numPackages = len(softwarePackages)
		}
		hostSoftware := softwarePackages[:numPackages]
		_, err := ds.UpdateHostSoftware(ctx, host.ID, hostSoftware)
		require.NoError(t, err)
	}

	err := ds.SyncHostsSoftware(ctx, time.Now())
	require.NoError(t, err)

	// Create CVEs for software (distributed across 15 software IDs)
	// Each software gets many CVEs to simulate real-world vulnerability distribution
	cvesPerSoftware := numCVEs / 15
	t.Logf("  Creating %d CVEs per software package (15 packages)", cvesPerSoftware)

	for softwareID := uint(1); softwareID <= 15; softwareID++ {
		// Insert CVEs in batches for better performance
		batchSize := 1000
		for batchStart := 0; batchStart < cvesPerSoftware; batchStart += batchSize {
			batchEnd := batchStart + batchSize
			if batchEnd > cvesPerSoftware {
				batchEnd = cvesPerSoftware
			}

			for i := batchStart; i < batchEnd; i++ {
				cveNum := int(softwareID-1)*cvesPerSoftware + i
				// Use wider CVE number range to avoid duplicates
				cve := fmt.Sprintf("CVE-2023-%05d", cveNum)

				_, err := ds.InsertSoftwareVulnerability(ctx, fleet.SoftwareVulnerability{
					SoftwareID: softwareID,
					CVE:        cve,
				}, fleet.NVDSource)
				require.NoError(t, err)
			}
		}

		if softwareID%5 == 0 {
			t.Logf("  Progress: %d/%d software packages completed", softwareID, 15)
		}
	}
}

// createOSVulnerabilities creates OS vulnerabilities
func createOSVulnerabilities(t testing.TB, ds *Datastore, numCVEs int) {
	ctx := context.Background()

	// Create CVEs for each OS type
	// OS ID 1 = Windows, 2 = macOS, 3 = Ubuntu
	osIDs := []uint{1, 2, 3}
	cvesPerOS := numCVEs / len(osIDs)

	t.Logf("  Creating %d CVEs per OS type (3 OS types)", cvesPerOS)

	for _, osID := range osIDs {
		// Insert in batches to avoid memory issues with large slices
		batchSize := 5000
		totalBatches := (cvesPerOS + batchSize - 1) / batchSize

		for batchNum := 0; batchNum < totalBatches; batchNum++ {
			batchStart := batchNum * batchSize
			batchEnd := batchStart + batchSize
			if batchEnd > cvesPerOS {
				batchEnd = cvesPerOS
			}

			vulns := make([]fleet.OSVulnerability, batchEnd-batchStart)
			for i := 0; i < len(vulns); i++ {
				actualIndex := batchStart + i
				// Use different year to avoid overlap with software CVEs
				// (but still create some overlap)
				cveNum := int(osID-1)*cvesPerOS + actualIndex
				cve := fmt.Sprintf("CVE-2022-%05d", cveNum)

				// 10% overlap with software CVEs for realism
				if actualIndex%10 == 0 {
					cve = fmt.Sprintf("CVE-2023-%05d", cveNum)
				}

				vulns[i] = fleet.OSVulnerability{
					OSID: osID,
					CVE:  cve,
				}
			}

			_, err := ds.InsertOSVulnerabilities(ctx, vulns, fleet.MSRCSource)
			require.NoError(t, err)

			if (batchNum+1)%2 == 0 || batchNum == totalBatches-1 {
				t.Logf("  Progress: OS %d - batch %d/%d completed", osID, batchNum+1, totalBatches)
			}
		}
	}
}

// createCVEMetadata creates CVE metadata entries
func createCVEMetadata(t testing.TB, ds *Datastore, numCVEs int) {
	ctx := context.Background()
	mockTime := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)

	// Create metadata in batches of 500 for better performance
	batchSize := 500
	totalBatches := (numCVEs + batchSize - 1) / batchSize
	t.Logf("  Creating CVE metadata in %d batches", totalBatches)

	for start := 0; start < numCVEs; start += batchSize {
		end := start + batchSize
		if end > numCVEs {
			end = numCVEs
		}

		batch := make([]fleet.CVEMeta, end-start)
		for i := start; i < end; i++ {
			// Alternate between 2022 and 2023 CVEs to match our created vulnerabilities
			year := 2023
			if i >= 50000 {
				year = 2022
			}

			// Use 5-digit format to match our CVE creation
			cve := fmt.Sprintf("CVE-%d-%05d", year, i%100000)

			// 30% have CISA known exploit
			cisaExploit := (i % 10) < 3

			batch[i-start] = fleet.CVEMeta{
				CVE:              cve,
				CVSSScore:        ptr.Float64(5.0 + float64(i%50)/10.0),
				EPSSProbability:  ptr.Float64(float64(i%100) / 100.0),
				CISAKnownExploit: ptr.Bool(cisaExploit),
				Published:        ptr.Time(mockTime.Add(time.Duration(i) * time.Hour)),
				Description:      fmt.Sprintf("Test vulnerability %s", cve),
			}
		}

		err := ds.InsertCVEMeta(ctx, batch)
		require.NoError(t, err)

		// Report progress every 10 batches
		batchNum := (start / batchSize) + 1
		if batchNum%10 == 0 || batchNum == totalBatches {
			t.Logf("  Progress: %d/%d batches completed (%d CVEs)", batchNum, totalBatches, end)
		}
	}
}

// getHostIDs extracts host IDs from a slice of hosts
func getHostIDs(hosts []*fleet.Host) []uint {
	ids := make([]uint, len(hosts))
	for i, h := range hosts {
		ids[i] = h.ID
	}
	return ids
}

// CreateMySQLDSForBenchmark creates a datastore for benchmarking
func CreateMySQLDSForBenchmark(b *testing.B) *Datastore {
	return CreateMySQLDS(b)
}
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Performance Improvements**
* Faster loading of the vulnerabilities list via optimized database
queries for the vulnerabilities API endpoint.

* **Bug Fixes**
* More accurate “created at” timestamps for vulnerabilities, improving
sorting and consistency.
* More consistent source attribution for vulnerabilities when multiple
sources are available.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-17 09:57:47 -05:00
Scott Gress
61970118e9
Stop setup experience on software install failure (#34173)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33173
**Related issue:** Resolves #33111 

# Details

This is the remaining work to implement the "Stop the setup experience
when required software fails to install" feature. This didn't turn out
to be quite as straightforward as expected so I ended up doing a bit of
design-by-code and expect some feedback on the approach. I tried to make
it as low-touch as possible. The general design is:

1. In the `maybeUpdateSetupExperienceStatus` function which is called in
various places when a setup experience step is marked as completed, call
a new `maybeCancelPendingSetupExperienceSteps` function if the setup
step was marked as failed. Similarly call
`maybeCancelPendingSetupExperienceSteps` if a VPP app install fails to
enqueue.
2. In `maybeCancelPendingSetupExperienceSteps`, check whether the
specified host is MacOS and whether the "RequireAllSoftwareMacOS" flag
is set in the team (or global) config. If so, mark the remaining setup
experience items as canceled and cancel any upcoming activities related
to those steps.
3. On the front-end, if the `require_all_software_macos` is set and a
software step is marked as failed, show a new failure page indicating
that setup has failed and showing details of the failed software.
4. On the agent side, when checking setup experience status, send a
`reset_after_failure` flag _only the first time_. If this flag is set,
then the code in the `/orbit/setup_experience/status` handler will clear
and re-queue any failed setup experience steps (but leave successful
steps to avoid re-installing already-installed software). This
facilitates re-starting the setup experience when the host is rebooted.

I also updated the way that software (packages and VPP) is queued up for
the setup experience to be ordered alphabetically, to make it easier to
test _and_ because this is a desired outcome for a future story. Since
the order is not deterministic now, this update shouldn't cause any
problems (aside from a couple of test updates), but I'm ok taking it out
if desired.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [X] Added/updated automated tests
* Added a new integration test for software packages, testing that a
failed software package causes the rest of the setup experience to be
marked as failed when `require_all_software_macos` is set, and testing
that the "reset after failure" code works.
* Added a new integration test for VPP packages, testing that a failed
VPP enqueue causes the same halting of the setup experience.
I _don't_ have test for a failure _during_ a VPP install. It should go
through the same code path as the software package failure, so it's not
a huge gap.

- [ ] QA'd all new/changed functionality manually
Working on it 

## fleetd/orbit/Fleet Desktop

- [X] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [X] If the change applies to only one platform, confirmed that
`runtime.GOOS` is used as needed to isolate changes
- [X] Verified that fleetd runs on macOS, Linux and Windows


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Configurable option to halt macOS device setup if any software install
fails.
- Device setup page now shows a clear “Device setup failed” state with
expandable error details when all software is required on macOS.
- Improvements
- Setup status now includes per-step error messages for better
troubleshooting.
- Pending setup steps are automatically canceled after a failure when
applicable, with support to reset and retry the setup flow as
configured.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ian Littman <iansltx@gmail.com>
2025-10-17 08:38:53 -05:00
Ian Littman
4e0c34eccd
Fail GitOps run when software package YAML is supplied but fields specific to that file are still specified in the team YAML (#34142)
Fixes #34066. Included a changes file because this catches fields other
than software icon.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results
2025-10-16 13:06:36 -05:00
Juan Fernandez
32f2ddaa63
Show warning when viewing settings without auth (#34309)
**Related issue:** Resolves #32229

Show flash warning when viewing settings from a team where auth is not
granted.
2025-10-15 20:01:22 -04:00
Victor Lyuboslavsky
b9520f6b14
Changed the default name of Host Identity CA to 'Fleet Host Identity CA' to avoid conflict with Fleet's Apple MDM CA. (#34219)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34217

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] QA'd all new/changed functionality manually
2025-10-15 09:52:45 -05:00
Ian Littman
acff7b7343
Add software name cleanup on ingestion for various items where bundle executable isn't well-named (#34232)
Fixes #34159. Split from CPE translation fixes so this can be merged
into `main` pre-QA.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [ ] QA'd all new/changed functionality manually
2025-10-15 09:50:52 -05:00
Lucas Manuel Rodriguez
15518d2893
Optimize software title reconciliation in vulnerabilities job (#34146)
Resolves #34055

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Optimized software title reconciliation used during vulnerability
processing, improving scan performance and reducing database load. More
efficient cleanup of orphaned titles and updates to title names.
* **Tests**
  * Corrected a test name typo for clarity.
* Streamlined MDM integration test by removing redundant title
recreation steps.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-14 17:36:45 -05:00
Konstantin Sykulev
bb4b62bd0f
Adding name to software checksum for mac software (#34097)
**Related issue:** Resolves #28788

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests
- [x] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [x] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* macOS app checksums now include the app name, improving grouping,
deduplication, and preventing mis-linking or duplicate entries when
multiple names share a bundle ID.
* More stable title handling when bundle IDs are missing, reducing
unintended renames and mismatches.

* **Tests**
* Re-enabled related host-software tests and added a
longest-common-prefix test to validate name reconciliation.

* **Chores**
* Database migration added to recalculate checksums for affected macOS
app records.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-14 17:36:34 -05:00
Jonathan Katz
c5ad64056c
32751 Change installer add/edit endpoints to read from writer db instead of reader (#33820)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #32751 

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [ ] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)
- [ ] If paths of existing endpoints are modified without backwards
compatibility, checked the frontend/CLI for any necessary changes

## Testing

- [x] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)

- [ ] QA'd all new/changed functionality manually

---------

Co-authored-by: Jahziel Villasana-Espinoza <jahziel@fleetdm.com>
2025-10-14 16:59:01 -04:00
Sarah Gillespie
36ecfb7a43
Hide show disk encryption key option for third-party MDM hosts (#34201) 2025-10-14 15:54:58 -05:00
Juan Fernandez
04d937c2c6
Fixed UI bug with org logo on My Device page (#34084)
Resolves #33136 

Changed display logic for the organization logo component on the 'My
Device' page to prevent flickering.
2025-10-14 15:51:06 -04:00
Magnus Jensen
d99afbe9aa
Better api error for EUA manual macOS profile download (#34101)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33447 

User error:
<img width="1020" height="213" alt="image"
src="https://github.com/user-attachments/assets/daa90fa3-aa43-4734-9f29-4744ca6fb3e2"
/>

As far as I could tell it was not possible to customize the error
message shown when installing the failing profile.
We have some code that mentions iOS/iPadOS and after replicating, it
seems it does not work the same for macOS.


# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Sarah Gillespie <73313222+gillespi314@users.noreply.github.com>
2025-10-14 16:33:12 -03:00
Tim Lee
2b18caaee1
Add Jetbrains plugins (#34024) 2025-10-14 09:01:45 -06:00
Magnus Jensen
bdb7673259
Add lost mode behaviour for iOS/iPadOS (#33805)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33416

It's been decided to ship the feature and in the guide mention the apple
bug, that we are currently tracking.
[Slack
🧵](https://fleetdm.slack.com/archives/C03C41L5YEL/p1760448150025089?thread_ts=1760433366.092499&cid=C03C41L5YEL)

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* New Features
  * Added Lost Mode support to lock iOS and iPadOS devices.
  * Added ability to disable Lost Mode to unlock iOS/iPadOS devices.
* Improvements
* More consistent lock/unlock experience across macOS, iOS/iPadOS,
Windows, and Linux, with clearer status and activity updates.
* iOS/iPadOS now shows pending unlock status while Lost Mode disable is
in progress.
* Tests
* Added comprehensive end-to-end tests covering lock/unlock/wipe across
Apple, Windows, and Linux devices.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-14 11:30:05 -03:00
Gabriel Hernandez
8977037ff1
add UI to support lock and unlock for ios and ipad devices (#33869)
**Related issue:** Resolves #33417

This adds the UI to support locking and unlocking ios and ipad devices.
This allows the users with the correct permission to lock and unlock
these devices from the host details page host actions dropdown. It also
adds these activities to the upcoming activities feed.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
2025-10-14 15:03:21 +01:00
Ian Littman
bbc36bbc83
Fall back to app filename when ingesting macOS apps that have no display name/bundle name and run.sh as the bundle executable (#34176)
Fixes #34157. Seen on Steam games, which also don't have a bundle ID.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] QA'd all new/changed functionality manually
2025-10-13 17:33:20 -05:00
Dante Catalfamo
23bef25ab3
Add hash_sha256 to list hosts software response (#33657)
**Related issue:** Resolves #33410

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
2025-10-13 13:43:36 -05:00
Victor Lyuboslavsky
9cc7a02209
Fixed MySQL deadlocks when multiple hosts are updating their certificates in host vitals at the same time. (#34119)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #34116

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [ ] Added/updated automated tests (see below)

- [x] QA'd all new/changed functionality manually

Local test that reproduces the issue and verifies fix. Not checked in.

```go
// testAggressiveDeadlockReproduction creates an aggressive test scenario to reproduce
// deadlocks that occur when multiple hosts update certificate sources concurrently.
//
// This test bypasses the retry logic to actually detect deadlocks and prove the fix works.
//
// Results without fix: ~1100 deadlocks (22% rate) across 50 iterations
// Results with fix: ~160 deadlocks (3% rate) - 86% improvement
func testAggressiveDeadlockReproduction(t *testing.T, ds *Datastore) {
	ctx := context.Background()

	// Create hosts and certificates
	numHosts := 30
	hosts := make([]*fleet.Host, numHosts)
	for i := 0; i < numHosts; i++ {
		host, err := ds.NewHost(ctx, &fleet.Host{
			DetailUpdatedAt: time.Now(),
			LabelUpdatedAt:  time.Now(),
			PolicyUpdatedAt: time.Now(),
			SeenTime:        time.Now(),
			OsqueryHostID:   ptr.String(fmt.Sprintf("deadlock-test-host-%d", i)),
			NodeKey:         ptr.String(fmt.Sprintf("deadlock-test-host-%d-key", i)),
			UUID:            fmt.Sprintf("deadlock-test-host-%d-uuid", i),
			Hostname:        fmt.Sprintf("deadlock-host-%d", i),
		})
		require.NoError(t, err)
		hosts[i] = host
	}

	// Create certificates for each host
	certsPerHost := 15
	allCertIDs := make([][]uint, numHosts)

	for i := 0; i < numHosts; i++ {
		certs := make([]*fleet.HostCertificateRecord, certsPerHost)
		for j := 0; j < certsPerHost; j++ {
			certTemplate := x509.Certificate{
				Subject: pkix.Name{
					Country:            []string{"US"},
					CommonName:         fmt.Sprintf("host%d-cert%d.test.com", i, j),
					Organization:       []string{"Test Org"},
					OrganizationalUnit: []string{"Engineering"},
				},
				Issuer: pkix.Name{
					Country:      []string{"US"},
					CommonName:   "issuer.test.com",
					Organization: []string{"Test Issuer"},
				},
				SerialNumber:          big.NewInt(int64(i*1000 + j)),
				KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
				ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
				SignatureAlgorithm:    x509.SHA256WithRSA,
				NotBefore:             time.Now().Add(-time.Hour).Truncate(time.Second).UTC(),
				NotAfter:              time.Now().Add(24 * time.Hour).Truncate(time.Second).UTC(),
				BasicConstraintsValid: true,
			}

			cert := generateTestHostCertificateRecord(t, hosts[i].ID, &certTemplate)
			cert.Source = fleet.SystemHostCertificate
			certs[j] = cert
		}

		// Insert certificates
		err := ds.UpdateHostCertificates(ctx, hosts[i].ID, hosts[i].UUID, certs)
		require.NoError(t, err)

		// Load certificate IDs
		loadedCerts, _, err := ds.ListHostCertificates(ctx, hosts[i].ID, fleet.ListOptions{})
		require.NoError(t, err)
		require.Len(t, loadedCerts, certsPerHost)

		certIDs := make([]uint, certsPerHost)
		for j, cert := range loadedCerts {
			certIDs[j] = cert.ID
		}
		allCertIDs[i] = certIDs
	}

	// Run aggressive deadlock test
	totalDeadlocks := 0
	iterations := 50

	for iter := 0; iter < iterations; iter++ {
		t.Logf("Iteration %d/%d", iter+1, iterations)

		type result struct {
			txIdx int
			err   error
		}

		numTransactions := 100 // Many concurrent transactions
		resultsCh := make(chan result, numTransactions)

		// Launch concurrent transactions
		for txIdx := 0; txIdx < numTransactions; txIdx++ {
			go func(idx int) {
				hostIdx := idx % numHosts
				certIDs := allCertIDs[hostIdx]

				// Build UNSORTED source records to trigger deadlocks
				// Reverse order for even transactions to create lock conflicts
				toReplace := make([]*fleet.HostCertificateRecord, len(certIDs))
				for i := range certIDs {
					actualIdx := i
					if idx%2 == 0 {
						actualIdx = len(certIDs) - 1 - i
					}

					toReplace[i] = &fleet.HostCertificateRecord{
						ID:       certIDs[actualIdx],
						Source:   fleet.UserHostCertificate,
						Username: fmt.Sprintf("user%d", idx),
					}
				}

				// Call replaceHostCertsSourcesDB directly (no retry)
				err := ds.withTx(ctx, func(tx sqlx.ExtContext) error {
					return replaceHostCertsSourcesDB(ctx, tx, toReplace)
				})

				resultsCh <- result{txIdx: idx, err: err}
			}(txIdx)
		}

		// Collect results
		iterDeadlocks := 0
		for i := 0; i < numTransactions; i++ {
			res := <-resultsCh
			if res.err != nil {
				if strings.Contains(res.err.Error(), "Deadlock") || strings.Contains(res.err.Error(), "deadlock") {
					iterDeadlocks++
				} else {
					require.NoError(t, res.err, "Transaction %d unexpected error", res.txIdx)
				}
			}
		}

		if iterDeadlocks > 0 {
			t.Logf("  Deadlocks in iteration %d: %d/%d transactions", iter+1, iterDeadlocks, numTransactions)
			totalDeadlocks += iterDeadlocks
		}
	}

	// Report results
	deadlockRate := float64(totalDeadlocks) / float64(iterations*100) * 100
	t.Logf("\n=== DEADLOCK TEST RESULTS ===")
	t.Logf("Total deadlocks: %d across %d iterations", totalDeadlocks, iterations)
	t.Logf("Deadlock rate: %.1f%%", deadlockRate)
	t.Logf("\nExpected without fix: ~1100 deadlocks (22%% rate)")
	t.Logf("Expected with fix: ~160 deadlocks (3%% rate)")

	if totalDeadlocks > 500 {
		t.Fatalf("High deadlock count suggests fix is not applied or not working")
	}
}
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Bug Fixes
- Resolved rare database deadlocks when multiple hosts update
certificates simultaneously, improving reliability of host vitals
updates.
- Reduced unnecessary delete operations during certificate updates to
lower lock contention and improve stability under load.
- Standardized processing of certificate sources to ensure consistent
behavior across concurrent updates.
- Overall improvements result in smoother certificate synchronization
without user-facing changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-13 09:48:32 -05:00
Gabriel Hernandez
0d62636c75
allow file protocol in org contact url in UI (#34078)
**Related issue:** Fixes #32902

This allows file protocol urls when setting a support URL via the UI.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [x] QA'd all new/changed functionality manually
2025-10-13 12:18:07 +01:00
Dante Catalfamo
56e12f4aca
Fix WhatsApp and VS Code icons not displaying correctly (#33887)
**Related issue:** Resolves #28388

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] QA'd all new/changed functionality manually
2025-10-10 16:57:46 -07:00
Sarah Gillespie
a5973610d7
Fix secrets updated nil pointer bug in batch profiles flow (#34102) 2025-10-10 13:44:58 -05:00
Lucas Manuel Rodriguez
e85d820260
Added migration to clear the platform field on all labels (#34028)
Resolves #33245 and #33065.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [X] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

## Database migrations

- [X] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [X] Confirmed that updating the timestamps is acceptable, and will not
cause unwanted side effects.
- [X] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).
2025-10-10 08:24:24 -03:00
Juan Fernandez
a6d1bd1e81
Added link component to live query results (#34019)
**Related issue:** Resolves #33249 

Added link component shown in the Host column to the host details page.
2025-10-09 12:15:13 -04:00
Magnus Jensen
9360128942
Add sticky MDM enrollment Redis key (#33935)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #26879 

We decided to opt for a sticky enrollment approach, and I opted for
using redis, so this PR also adds a redis key value store to the free
service to use.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Bug Fixes
- Prevents Orbit enrollment from undoing team transfers triggered during
MDM enrollment, preserving the correct team assignment on re-enrollment.
- Introduces a temporary “sticky” enrollment period (~30 minutes) during
Apple MDM check-in and Orbit enrollment to reduce unintended team
changes.
- Improves reliability of team-scoped enroll secrets and host transfers
in short re-enrollment windows.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-09 11:22:44 -03:00
Juan Fernandez
cf01258a9d
Add new 'cleanup_dist_targets_age' server flag (#33965)
Resolves #33572 

Added new server config flag for specifying the cleanup age for
completed distributed targets.

---------

Co-authored-by: Lucas Manuel Rodriguez <lucas@fleetdm.com
2025-10-08 14:10:40 -04:00
Scott Gress
4697123f6e
Stop setup experience on software install fail: admin (#33968)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33110 
**Related issue:** Resolves #33109  

# Details

This PR implements the new "cancel setup if any software fails on macos"
flag, including both backend and frontend logic.

Half of the file changes are updating test expectations / auto-generated
schema.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [X] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [X] Added/updated automated tests
- [X] QA'd all new/changed functionality manually

## New Fleet configuration settings

- [ ] Verified that the setting is exported via `fleetctl
generate-gitops`
`macos_setup` is still excluded from generate-girtops
- [X] Verified the setting is documented in a separate PR to [the GitOps
documentation](https://github.com/fleetdm/fleet/blob/main/docs/Configuration/yaml-files.md#L485)
Documented [here](https://github.com/fleetdm/fleet/pull/33016/files)
- [X] Verified that the setting is cleared on the server if it is not
supplied in a YAML file (or that it is documented as being optional)
- [X] Verified that any relevant UI is disabled when GitOps mode is
enabled


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- New Features
- Added a macOS setup option: “Cancel setup if software install fails.”
  - Configure at global or team level; team settings override global.
- Toggle available in Setup Experience > Install software > Advanced
options.
  - Saved state persists and can be updated without leaving the page.
  - Devices honor the resolved setting during provisioning.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ian Littman <iansltx@gmail.com>
2025-10-08 17:51:52 +01:00
Scott Gress
be7e0045a9
Use webview in MacOS setup experience (#33884)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** For #33111

# Details

This PR updates the setup experience for MacOS to use a web view pointed
at the device's "Setting up your device" page rather than using native
MacOS UI elements, bringing it more in line with Linux and Windows setup
experiences.

This covers only the new web UI for the setup experience progress, _not_
the UI for the new case of blocking the device when a piece of software
fails to install. I'll add that in a separate PR.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [X] Added/updated automated tests
Added tests for the updates to the token rotation code.

- [X] QA'd all new/changed functionality manually
A new tool is provided to allow testing this code against a virtual
machine if a separate host that you can wipe and run setup on is not
available. See
https://github.com/fleetdm/fleet/blob/sgress454/new-setup-experience/tools/mdm/apple/setupexperience/README.md
for details.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
  - macOS setup experience moved to a new web-based UI.
  - Automatic device token rotation during setup to keep sessions valid.
- Bug Fixes
- More reliable setup flow with improved dialog lifecycle and cleaner
handoff to web content.
- Dialog elements hidden/cleared appropriately when transitioning to the
browser.
- Documentation
- Added guide and tool to simulate the macOS setup experience on a VM,
with prerequisites and usage steps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-08 17:51:26 +01:00
Gabriel Hernandez
09b7f56a64
add UI to support install software for ios and ipad on setup experience (#33973)
**Related issue:** Resolves #33701

Adds the UI needed to install software for ios and ipad during the setup
experience.

> NOTE: the video still needs to be recorded and placed in the code for
ios and ipad

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [ ] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
2025-10-08 16:20:00 +01:00
Tim Lee
aae4ccec54
31010 santa tables (#33218) 2025-10-08 08:58:08 -06:00
Jahziel Villasana-Espinoza
0a3c6c35d3
Android software ingestion (#33826)
> Closes #33581 


<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)


## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

## Database migrations

- [x] Checked table schema to confirm autoupdate
- [x] Checked schema for all modified table for columns that will
auto-update timestamps during migration.
- [x] Ensured the correct collation is explicitly set for character
columns (`COLLATE utf8mb4_unicode_ci`).

---------

Co-authored-by: RachelElysia <rachel@fleetdm.com>
2025-10-08 10:24:38 -04:00
Lucas Manuel Rodriguez
b5626e17e6
Fix lingering live queries keys in Redis (#33928)
Resolves #33254

This can be reproduced locally by running the following "high load"
test:

Run 500 hosts using osquery-perf:
```
go run ./cmd/osquery-perf --enroll_secret ... \
  --host_count 500 \
  --server_url https://localhost:8080 \
  --live_query_fail_prob 0.0 \
  --live_query_no_results_prob 0.0 \
  --orbit_prob 0.0 \
  --http_message_signature_prob 0.0
```

Run `stress_test_live_queries.sh`:
```
#!/bin/bash

while true; do
        curl -v -k -X POST -H "Authorization: Bearer $TEST_TOKEN" https://localhost:8080/api/latest/fleet/queries/$SAVED_QUERY_ID/run -d '{"host_ids": [<500 comma-separated host ids>]}'
done
```

Use "Redis Insight" or the like and you will start to see
`livequery:{$CAMPAIGN_ID}` keys with `No limit` (which is the bug):

<img width="1380" height="227" alt="Screenshot 2025-10-07 at 3 10 26 PM"
src="https://github.com/user-attachments/assets/30434348-3217-40c4-8ebc-bab5ceb4daa9"
/>

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Bug Fixes
- Prevent lingering Redis keys for live queries by ensuring keys are
cleaned up and not recreated when completing/canceling non-existent
queries.
- Improves resource usage and avoids stale state in live query
processing.

- Tests
- Added tests verifying proper retrieval/completion behavior and that no
Redis key is created for non-existent live queries.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ian Littman <iansltx@gmail.com>
2025-10-08 06:36:38 -03:00
Zach Wasserman
41c53860e3
Add support for VSCode fork extensions in software inventory (#33595)
**Related issue:** Resolves #31397

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually

---------

Co-authored-by: Jacob Shandling <jacob@fleetdm.com>
2025-10-07 14:05:22 -07:00
RachelElysia
ad73c5ce01
Fleet UI: Show Android software inventory (#33809)
## Issue
Closes #33580
Part of Story #33060 

## Description
- Anything related to adding "android_apps" into the FE code
- Add to frontend interfaces `interfaces/platform.ts` for platforms that
do not support vuln scanning
- Add `application_id` key and `source: "android_apps"` value to
`ISoftware`
- Add source type conversion `android_apps: "Application (Android)"` for
rendering type in UI
  - Add to `INSTALLABLE_SOURCE_PLATFORM_CONVERSION`
- Show unsupported vuln empty state in software details page for
`source: "android_apps"`
  - Add android icon to software icons
- Use android icon as default fallback for `source: "android_apps"`
software
- Show android host details page > software library tab, but with
unsupported empty state correct showing android link instead of hiding
tab all together
- Show android host details page > software inventory tab > vuln filter
on, vuln not supported empty state

## Testing

- [x] QA'd all new/changed functionality manually
-
> FE code was manually QAed with hard coded data but will require BE
changes to be merged to E2E QA
2025-10-07 14:08:25 -04:00
RachelElysia
6edc78d8ec
Fleet UI: Resurface post-install script output (#33765) 2025-10-07 11:02:39 -04:00
Martin Angers
adee5f78ed
Bugfix: error when package added that matches software title with existing VPP app (#33727) 2025-10-07 10:15:12 -04:00
Ian Littman
153301e892
Update copy to indicate that iOS/iPadOS work with vitals labels (#33919)
For #32072. Should be cherry-picked to 4.75 since that's where the
support was added.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] QA'd all new/changed functionality manually

For unreleased bug fixes in a release candidate, one of:

- [x] Confirmed that the fix is not expected to adversely impact load
test results
2025-10-07 06:07:51 -05:00
Magnus Jensen
a683185bb3
Sort found variables by length descending (#33875)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33502 

This fixes the mentioned issue by always sorting the found variables in
length descending, to ensure we process the longest variables first,
avoiding us processing a smaller variation of a longer variable first
breaking substitution.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
2025-10-06 14:50:22 -03:00
Victor Lyuboslavsky
563bcdf18b
Handle multiple software entries with the same bundle ID during renames. (#33479)
- Adjusted logic to support multiple software versions sharing a bundle
ID.
- Extended tests to validate scenarios involving renamed software across
versions.

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33468

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [x] Added/updated automated tests
- [x] Manually QA'd using osquery `--common_software_name_suffix` switch


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- Bug Fixes
- Improves handling of apps that share the same bundle ID, ensuring all
versions are correctly linked and consistently renamed across hosts.
- Reduces duplicate software entries and keeps host associations intact
during rename operations.
- Delivers more reliable software inventory views with accurate app
names derived from bundle IDs.

- Tests
- Adds comprehensive coverage for scenarios with multiple versions per
bundle ID to validate linking and renaming behavior across hosts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-06 11:32:26 -05:00
Konstantin Sykulev
8c91c03eea
Software renaming lock contention (#33791)
**Related issue:** Resolves #33668

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [ ] Added/updated automated tests
- [ ] Where appropriate, [automated tests simulate multiple hosts and
test for host
isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing)
(updates to one hosts's records do not affect another)
- [x] QA'd all new/changed functionality manually
2025-10-06 11:30:10 -05:00
Scott Gress
12ab93d9e0
Update "Setting up your device" page for MacOS Setup Experience (#33770)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #33173

# Details

This PR updates the "Setting up your device" page which appears in Linux
and Windows (and as of https://github.com/fleetdm/fleet/issues/30117,
MacOS) setup experiences. Front-end updates:

* Lots of renaming of things that were software-specific to now more
generically refer to "setup step"
* Removed the "My Device" heading
* Moved the info button inside the table header
* Added status of setup script run to the table
* Updated the empty state to not refer specifically to software
* Added optional `setup_only` query param to the `/device` page which,
if set, will always show the "setting up your device" page even if all
setup is complete. Normally as soon as setup finishes, the front-end
redirects to the regular My Device page. In the case of MacOS setup
experience, we don't want this to happen as we expect to either 1) keep
the setup experience up indefinitely if we're blocking device setup on
software install failure, or 2) close the setup dialog on successful
completion. This query param is also handy for testing.
* Added new "Configuration complete" state to be shown when all setup
steps are finished (successfully or not). This is only applicable on
MacOS, since other platforms will redirect to the My Device page when
finished.

This PR also includes one small backend change to the
`/device/{token}/setup_experience/status` API endpoint, to have it
return a `scripts` array alongside the existing `software` array. This
endpoint is not documented publicly.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [X] Added/updated automated tests
Updated existing DeviceUserPage tests that check the SettingUpYourDevice
content, and added new tests for the new scripts content and the new
query param.

- [X] QA'd all new/changed functionality manually

<img width="1028" height="867" alt="Screenshot 2025-10-02 at 7 20 28 PM"
src="https://github.com/user-attachments/assets/7adab2c2-dac1-4463-96fc-13094da2c379"
/>

(note that as of now we'd only have at most one script, showing multiple
here to demonstrate the different states)

<img width="1031" height="524" alt="Screenshot 2025-10-02 at 7 22 01 PM"
src="https://github.com/user-attachments/assets/bedaa840-d7ef-4b6f-8daf-6ac3b447594f"
/>

<img width="1222" height="760" alt="image"
src="https://github.com/user-attachments/assets/42cf82d5-53e0-4c4d-b60e-9ac2cc86af68"
/>

---------

Co-authored-by: Ian Littman <iansltx@gmail.com>
2025-10-06 16:45:53 +01:00