From 3ef742f552223acfd2633e03547c3b845dbdb6fc Mon Sep 17 00:00:00 2001 From: Sharon Date: Wed, 1 Jul 2026 17:11:00 -0400 Subject: [PATCH] Fix CPE matching for python3-prefixed packages on Ubuntu/Debian (#43328) Co-Authored-By: Claude Opus 4.6 (1M context) --- changes/43328-python3-prefix-cpe | 1 + server/vulnerabilities/nvd/sanitize.go | 7 +++ server/vulnerabilities/nvd/sanitize_test.go | 56 +++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 changes/43328-python3-prefix-cpe diff --git a/changes/43328-python3-prefix-cpe b/changes/43328-python3-prefix-cpe new file mode 100644 index 00000000000..7ac7501aa89 --- /dev/null +++ b/changes/43328-python3-prefix-cpe @@ -0,0 +1 @@ +- Fixed vulnerability detection for Python packages on Ubuntu/Debian devices by stripping the "python3-" name prefix during CPE matching. diff --git a/server/vulnerabilities/nvd/sanitize.go b/server/vulnerabilities/nvd/sanitize.go index 85cab125cdf..ed7565e93c0 100644 --- a/server/vulnerabilities/nvd/sanitize.go +++ b/server/vulnerabilities/nvd/sanitize.go @@ -92,6 +92,13 @@ func sanitizeSoftwareName(s *fleet.Software) string { r := strings.ToLower(s.Name) r = strings.TrimSuffix(r, ".app") + // Strip "python3-" prefix from python_packages names for CPE matching. + // On Ubuntu/Debian, pythonPackageFilter prepends "python3-" to match OVAL definitions, + // but the CPE database uses the bare package name (e.g. "geopandas" not "python3-geopandas"). + if s.Source == "python_packages" { + r = strings.TrimPrefix(r, "python3-") + } + // Remove vendor, for 'apps' the vendor name is usually after the top level domain part. r = strings.ReplaceAll(r, strings.ToLower(s.Vendor), "") bundleParts := strings.Split(s.BundleIdentifier, ".") diff --git a/server/vulnerabilities/nvd/sanitize_test.go b/server/vulnerabilities/nvd/sanitize_test.go index 215c1537ce2..4697157387d 100644 --- a/server/vulnerabilities/nvd/sanitize_test.go +++ b/server/vulnerabilities/nvd/sanitize_test.go @@ -162,6 +162,14 @@ func TestVariations(t *testing.T) { vendorVariations: []string{"microsoft", "ms-python"}, productVariations: []string{"python", "ms-python.python"}, }, + { + software: fleet.Software{Name: "python3-geopandas", Version: "1.0.1", Source: "python_packages"}, + productVariations: []string{"geopandas"}, + }, + { + software: fleet.Software{Name: "python3-django", Version: "3.2.12", Source: "python_packages"}, + productVariations: []string{"django"}, + }, } for _, tc := range variationsTestCases { @@ -378,6 +386,54 @@ func TestSanitizedSoftwareName(t *testing.T) { require.Equal(t, tc.expected, actual) } }) + + t.Run("strips python3- prefix from python_packages", func(t *testing.T) { + testCases := []struct { + software fleet.Software + expected string + }{ + { + software: fleet.Software{ + Name: "python3-geopandas", + Version: "1.0.1", + Source: "python_packages", + }, + expected: "geopandas", + }, + { + software: fleet.Software{ + Name: "python3-django", + Version: "3.2.12", + Source: "python_packages", + }, + expected: "django", + }, + { + // python_packages without the prefix should not be affected + software: fleet.Software{ + Name: "requests", + Version: "2.28.0", + Source: "python_packages", + }, + expected: "requests", + }, + { + // deb_packages with python3- prefix should NOT be stripped + software: fleet.Software{ + Name: "python3-geopandas", + Version: "1.0.1", + Source: "deb_packages", + }, + expected: "python3-geopandas", + }, + } + + for _, tc := range testCases { + tc := tc + actual := sanitizeSoftwareName(&tc.software) + require.Equal(t, tc.expected, actual) + } + }) } func TestParseUpdateFromVersion(t *testing.T) {