diff --git a/changes/31303-citrix-workspace-resolved-in-version b/changes/31303-citrix-workspace-resolved-in-version new file mode 100644 index 0000000000..e83ead1c3b --- /dev/null +++ b/changes/31303-citrix-workspace-resolved-in-version @@ -0,0 +1 @@ +- Fixed a bug where certain incorrect resolved-in versions were reported for certain vulnerable versions of Citrix Workspace. diff --git a/cmd/cve/validate/main.go b/cmd/cve/validate/main.go index d3e7db664f..ea27121f0e 100644 --- a/cmd/cve/validate/main.go +++ b/cmd/cve/validate/main.go @@ -72,6 +72,14 @@ func checkNVDVulnerabilities(vulnPath string, logger *slog.Logger) { vulnEntry.Schema().Configurations.Nodes[0].CPEMatch[1].VersionEndExcluding != "2403.1" { panic(errors.New("enriched vulnerability spot-check failed for Citrix Workstation on CVE-2024-6286")) } + for _, match := range vulnEntry.Schema().Configurations.Nodes[0].CPEMatch { + // there are a number of matches here with "ltsr" in their cpe23Uri but no versionEndExcluding. + // We are only interested in confirming that the `versionEndExcluding` for the match whose CPE + // contains "ltsr", which came from NVD with an incorrect value,has been replaced with "2402" + if strings.Contains(match.Cpe23Uri, ":ltsr:") && match.VersionEndExcluding != "" && match.VersionEndExcluding != "2402" { + panic(fmt.Errorf("CVE-2024-6286 LTSR versionEndExcluding spot-check failed: got %q, expected \"2402\"", match.VersionEndExcluding)) + } + } // check CVSS score extraction; confirm that secondary CVSS scores are extracted when primary isn't set if vulns["CVE-2024-54559"].CVSSv3BaseScore() != 5.5 { // secondary source CVSS score diff --git a/server/vulnerabilities/nvd/cpe_test.go b/server/vulnerabilities/nvd/cpe_test.go index dfef68ad9c..874a6bbeb1 100644 --- a/server/vulnerabilities/nvd/cpe_test.go +++ b/server/vulnerabilities/nvd/cpe_test.go @@ -2487,3 +2487,83 @@ func TestMutateSoftware(t *testing.T) { }) } } + +func TestCitrixWorkspaceLTSR(t *testing.T) { + item := &IndexedCPEItem{ + Product: "workspace", + Vendor: "citrix", + } + + for _, tc := range []struct { + name string + software fleet.Software + wantCPE string + }{ + { + name: "Citrix Workspace 2203 LTSR on Windows", + software: fleet.Software{ + Name: "Citrix Workspace 2203", + Version: "22.3.1.41", + Source: "programs", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:2203.1.41:*:*:*:ltsr:windows:*:*", + }, + { + name: "Citrix Workspace 2402 LTSR on Windows", + software: fleet.Software{ + Name: "Citrix Workspace 2402", + Version: "24.2.0.65", + Source: "programs", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:2402.0.65:*:*:*:ltsr:windows:*:*", + }, + { + name: "Citrix Workspace non-LTSR on Windows", + software: fleet.Software{ + Name: "Citrix Workspace 2309", + Version: "23.9.1.104", + Source: "programs", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:2309.1.104:*:*:*:*:windows:*:*", + }, + { + name: "Citrix Workspace LTSR version on Mac (not programs source)", + software: fleet.Software{ + Name: "Citrix Workspace.app", + Version: "24.2.0.65", + Source: "apps", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:2402.0.65:*:*:*:*:macos:*:*", + }, + { + name: "Citrix Workspace 1912 LTSR on Windows", + software: fleet.Software{ + Name: "Citrix Workspace 1912", + Version: "19.12.0.5", + Source: "programs", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:1912.0.5:*:*:*:ltsr:windows:*:*", + }, + { + name: "Citrix Workspace 2507.1 LTSR on Windows", + software: fleet.Software{ + Name: "Citrix Workspace 2507", + Version: "25.7.1.50", + Source: "programs", + Vendor: "Citrix Systems, Inc.", + }, + wantCPE: "cpe:2.3:a:citrix:workspace:2507.1.50:*:*:*:ltsr:windows:*:*", + }, + } { + t.Run(tc.name, func(t *testing.T) { + mutateSoftware(t.Context(), &tc.software, slog.New(slog.DiscardHandler)) + got := item.FmtStr(&tc.software) + require.Equal(t, tc.wantCPE, got) + }) + } +} diff --git a/server/vulnerabilities/nvd/cve.go b/server/vulnerabilities/nvd/cve.go index 8a2a3fdfdc..6c6d0ddfdb 100644 --- a/server/vulnerabilities/nvd/cve.go +++ b/server/vulnerabilities/nvd/cve.go @@ -723,6 +723,10 @@ func getMatchingVersionEndExcluding(ctx context.Context, cve string, hostSoftwar if attr.Product != hostSoftwareMeta.Product || attr.Vendor != hostSoftwareMeta.Vendor { continue } + if attr.SWEdition != wfn.Any && attr.SWEdition != hostSoftwareMeta.SWEdition && + !(hostSoftwareMeta.SWEdition == wfn.Any && attr.SWEdition == wfn.NA) { + continue + } // versionEnd is the version string that the vulnerable host software version must be less than versionEnd, err := checkVersion(rule, hostSoftwareVersion) diff --git a/server/vulnerabilities/nvd/cve_test.go b/server/vulnerabilities/nvd/cve_test.go index fbd603380e..b2fe3b86a4 100644 --- a/server/vulnerabilities/nvd/cve_test.go +++ b/server/vulnerabilities/nvd/cve_test.go @@ -429,13 +429,18 @@ func TestTranslateCPEToCVE(t *testing.T) { excludedCVEs: []string{"CVE-2024-6286"}, continuesToUpdate: true, }, - // FIXME: https://github.com/fleetdm/fleet/issues/31303 - // "cpe:2.3:a:citrix:workspace:2309.0:*:*:*:*:windows:*:*": { - // includedCVEs: []cve{ - // {ID: "CVE-2024-6286", resolvedInVersion: "2402"}, - // }, - // continuesToUpdate: true, - // }, + "cpe:2.3:a:citrix:workspace:2203.1:*:*:*:ltsr:windows:*:*": { + includedCVEs: []cve{ + {ID: "CVE-2024-6286", resolvedInVersion: "2402"}, + }, + continuesToUpdate: true, + }, + "cpe:2.3:a:citrix:workspace:2311.1:*:*:*:*:windows:*:*": { + includedCVEs: []cve{ + {ID: "CVE-2024-6286", resolvedInVersion: "2403.1"}, + }, + continuesToUpdate: true, + }, "cpe:2.3:a:python:python:3.9.6:*:*:*:*:macos:*:*": { excludedCVEs: []string{"CVE-2024-4030"}, continuesToUpdate: true, diff --git a/server/vulnerabilities/nvd/indexed_cpe_item.go b/server/vulnerabilities/nvd/indexed_cpe_item.go index c42d939b09..6c9bfae475 100644 --- a/server/vulnerabilities/nvd/indexed_cpe_item.go +++ b/server/vulnerabilities/nvd/indexed_cpe_item.go @@ -18,6 +18,18 @@ type IndexedCPEItem struct { Weight int `db:"weight"` } +// TODO in future as needed - automate updates of this set +var citrixLTSRVersions = []string{"2507.1", "2402", "2203.1", "1912"} + +func isCitrixWorkspaceLTSR(version string) bool { + for _, ltsr := range citrixLTSRVersions { + if version == ltsr || strings.HasPrefix(version, ltsr+".") { + return true + } + } + return false +} + func (i *IndexedCPEItem) FmtStr(s *fleet.Software) string { cpe := wfn.NewAttributesWithAny() cpe.Part = "a" @@ -37,6 +49,11 @@ func (i *IndexedCPEItem) FmtStr(s *fleet.Software) string { cpe.Update = wfn.NA } + if s.Source == "programs" && s.Vendor == "Citrix Systems, Inc." && + strings.HasPrefix(s.Name, "Citrix Workspace") && isCitrixWorkspaceLTSR(version) { + cpe.SWEdition = "ltsr" + } + if i.Part != "" { cpe.Part = i.Part } diff --git a/server/vulnerabilities/nvd/sync/cve_syncer.go b/server/vulnerabilities/nvd/sync/cve_syncer.go index 8be153e715..ecbc422661 100644 --- a/server/vulnerabilities/nvd/sync/cve_syncer.go +++ b/server/vulnerabilities/nvd/sync/cve_syncer.go @@ -528,12 +528,26 @@ func transformVuln(year int, item nvdapi.CVEItem) nvdapi.CVEItem { for configID := range item.CVE.Configurations { for nodeID := range item.CVE.Configurations[configID].Nodes { for matchID := range item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch { - item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch[matchID].Criteria = - strings.ReplaceAll( - item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch[matchID].Criteria, - "docker_desktop", - "desktop", - ) + item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch[matchID].Criteria = strings.ReplaceAll( + item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch[matchID].Criteria, + "docker_desktop", + "desktop", + ) + } + } + } + } + + // This corrects the resolved-in version to what Citrix actually reports it is + if item.CVE.ID != nil && *item.CVE.ID == "CVE-2024-6286" { + for configID := range item.CVE.Configurations { + for nodeID := range item.CVE.Configurations[configID].Nodes { + for matchID := range item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch { + match := &item.CVE.Configurations[configID].Nodes[nodeID].CPEMatch[matchID] + if strings.Contains(match.Criteria, ":ltsr:") && + match.VersionEndExcluding != nil && *match.VersionEndExcluding == "2203.1" { + match.VersionEndExcluding = ptr.String("2402") + } } } } diff --git a/tools/software/vulnerabilities/README.md b/tools/software/vulnerabilities/README.md index 7840f1444b..89e7395230 100644 --- a/tools/software/vulnerabilities/README.md +++ b/tools/software/vulnerabilities/README.md @@ -18,7 +18,7 @@ into your local development Fleet server without needing a real host or osquery- 3. Run the data seeder ```bash -go run ./tools/software/vulnerabilities/seed_vuln_data.go --ubuntu 1 --macos 1 --windows 1 --linux-kernels 1 +go run ./tools/software/vulnerabilities/seed_data/seed_vuln_data.go --ubuntu 1 --macos 1 --windows 1 --linux-kernels 1 ``` You should now see new hosts with the configured software attached in the UI and database. This