From ab958704f7e86e85b92dcddfaae88cc985e246be Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Thu, 17 Jul 2025 17:40:21 -0500 Subject: [PATCH] Fix insufficient deduplication on vulnerabilities count query (#31021) Fixes #27580. # 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] Added/updated automated tests - [x] Manual QA for all new/changed functionality --- changes/27580-vuln-counts | 1 + server/datastore/mysql/vulnerabilities.go | 6 +++--- server/datastore/mysql/vulnerabilities_test.go | 10 +++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 changes/27580-vuln-counts diff --git a/changes/27580-vuln-counts b/changes/27580-vuln-counts new file mode 100644 index 0000000000..92907e78bf --- /dev/null +++ b/changes/27580-vuln-counts @@ -0,0 +1 @@ +* Fixed cases where the vulnerabilities list endpoint would count the same CVE multiple times for the `count` field returned with a result set diff --git a/server/datastore/mysql/vulnerabilities.go b/server/datastore/mysql/vulnerabilities.go index 2dab5e127b..d2b0af7168 100644 --- a/server/datastore/mysql/vulnerabilities.go +++ b/server/datastore/mysql/vulnerabilities.go @@ -309,11 +309,11 @@ func (ds *Datastore) ListVulnerabilities(ctx context.Context, opt fleet.VulnList func (ds *Datastore) CountVulnerabilities(ctx context.Context, opt fleet.VulnListOptions) (uint, error) { selectStmt := ` SELECT - COUNT(*) + COUNT(DISTINCT combined.cve) FROM ( - SELECT cve, created_at, source FROM software_cve + SELECT cve FROM software_cve UNION - SELECT cve, created_at, source FROM operating_system_vulnerabilities + SELECT cve FROM operating_system_vulnerabilities ) AS combined INNER JOIN vulnerability_host_counts vhc ON vhc.cve = combined.cve LEFT JOIN cve_meta cm ON cm.cve = combined.cve diff --git a/server/datastore/mysql/vulnerabilities_test.go b/server/datastore/mysql/vulnerabilities_test.go index 7d08837e29..1a8bf52cd0 100644 --- a/server/datastore/mysql/vulnerabilities_test.go +++ b/server/datastore/mysql/vulnerabilities_test.go @@ -1140,6 +1140,10 @@ func seedVulnerabilities(t *testing.T, ds *Datastore) { CVE: "CVE-2020-1235", ResolvedInVersion: ptr.String("1.0.1"), }, + { + SoftwareID: 2, + CVE: "CVE-2020-1235", // overlaps software ID 1 + }, { SoftwareID: 2, CVE: "CVE-2020-1236", @@ -1148,6 +1152,10 @@ func seedVulnerabilities(t *testing.T, ds *Datastore) { SoftwareID: 2, CVE: "CVE-2020-1237", }, + { + SoftwareID: 2, + CVE: "CVE-2020-1238", // overlaps between software and OS + }, } osVulns := []fleet.OSVulnerability{ @@ -1374,7 +1382,7 @@ func seedVulnerabilities(t *testing.T, ds *Datastore) { // Insert Software Vuln for _, vuln := range softwareVulns { - _, err = ds.InsertSoftwareVulnerability(context.Background(), vuln, fleet.NVDSource) + _, err = ds.InsertSoftwareVulnerability(context.Background(), vuln, fleet.CustomSource) require.NoError(t, err) }