Reapply "Update Citrix Workspace CPE generation to distinguish betwee… (#41614)

Re-applies https://github.com/fleetdm/fleet/pull/41512 to `main`.
CPE-CVE translation tests expected to fail.
This commit is contained in:
jacobshandling 2026-03-12 16:17:40 -07:00 committed by GitHub
parent b37de7c9aa
commit 3ab4e37c8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 143 additions and 14 deletions

View file

@ -0,0 +1 @@
- Fixed a bug where certain incorrect resolved-in versions were reported for certain vulnerable versions of Citrix Workspace.

View file

@ -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

View file

@ -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)
})
}
}

View file

@ -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)

View file

@ -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,

View file

@ -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
}

View file

@ -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")
}
}
}
}

View file

@ -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