diff --git a/server/service/osquery_utils/queries.go b/server/service/osquery_utils/queries.go index 7ec132f8ad..0e07446036 100644 --- a/server/service/osquery_utils/queries.go +++ b/server/service/osquery_utils/queries.go @@ -1770,6 +1770,35 @@ var ( s.Version = fmt.Sprintf("%d.%d.%s.%s", yearBasedMajorVersion, yearBasedMinorVersion, "99", eapMinorAndPatchVersion) }, }, + { + // Python versions on Windows encode ABI and release information in a way that's incompatible with NVD lookups + checkSoftware: func(h *fleet.Host, s *fleet.Software) bool { + return s.Source == "programs" && strings.HasPrefix(s.Name, "Python 3.") + }, + mutateSoftware: func(s *fleet.Software, logger log.Logger) { + versionComponents := strings.Split(s.Version, ".") + patchVersion := versionComponents[2][0 : len(versionComponents[2])-3] + releaseLevel := versionComponents[2][len(versionComponents[2])-3 : len(versionComponents[2])-1] + releaseSerial := versionComponents[2][len(versionComponents[2])-1 : len(versionComponents[2])] + + candidateSuffix := "" + switch releaseLevel { // see https://github.com/python/cpython/issues/100829#issuecomment-1374656643 + case "10": + candidateSuffix = "-alpha" + releaseSerial + case "11": + candidateSuffix = "-beta" + releaseSerial + case "12": + candidateSuffix = "-rc" + releaseSerial + } // default + + if patchVersion == "" { // dot-zero patch releases have a 3-digit patch + build number + patchVersion = "0" + } + + versionComponents[2] = patchVersion + candidateSuffix + s.Version = strings.Join(versionComponents[0:3], ".") + }, + }, } ) diff --git a/server/service/osquery_utils/queries_test.go b/server/service/osquery_utils/queries_test.go index a71b1deb80..8292832fd6 100644 --- a/server/service/osquery_utils/queries_test.go +++ b/server/service/osquery_utils/queries_test.go @@ -1975,6 +1975,76 @@ func TestSanitizeSoftware(t *testing.T) { BundleIdentifier: "com.jetbrains.intellij-EAP", }, }, + { + name: "Python for Windows GA dot-zero", + h: &fleet.Host{}, + s: &fleet.Software{ + Name: "Python 3.12 (64-bit)", + Version: "3.12.150.1013", + Source: "programs", + }, + sanitized: &fleet.Software{ + Name: "Python 3.12 (64-bit)", + Version: "3.12.0", + Source: "programs", + }, + }, + { + name: "Python for Windows GA patch release", + h: &fleet.Host{}, + s: &fleet.Software{ + Name: "Python 3.12.8 (64-bit)", + Version: "3.12.8150.0", + Source: "programs", + }, + sanitized: &fleet.Software{ + Name: "Python 3.12.8 (64-bit)", + Version: "3.12.8", + Source: "programs", + }, + }, + { + name: "Python for Windows alpha", + h: &fleet.Host{}, + s: &fleet.Software{ + Name: "Python 3.14.0a4 (64-bit)", + Version: "3.14.104.1013", + Source: "programs", + }, + sanitized: &fleet.Software{ + Name: "Python 3.14.0a4 (64-bit)", + Version: "3.14.0-alpha4", + Source: "programs", + }, + }, + { + name: "Python for Windows beta", + h: &fleet.Host{}, + s: &fleet.Software{ + Name: "Python 3.14.0b3 (64-bit)", + Version: "3.14.113.1013", + Source: "programs", + }, + sanitized: &fleet.Software{ + Name: "Python 3.14.0b3 (64-bit)", + Version: "3.14.0-beta3", + Source: "programs", + }, + }, + { + name: "Python for Windows RC", + h: &fleet.Host{}, + s: &fleet.Software{ + Name: "Python 3.14.0rc2 (64-bit)", + Version: "3.14.122.1013", + Source: "programs", + }, + sanitized: &fleet.Software{ + Name: "Python 3.14.0rc2 (64-bit)", + Version: "3.14.0-rc2", + Source: "programs", + }, + }, } { t.Run(tc.name, func(t *testing.T) { sanitizeSoftware(tc.h, tc.s, log.NewNopLogger()) diff --git a/server/vulnerabilities/nvd/cpe_test.go b/server/vulnerabilities/nvd/cpe_test.go index 8a669d81c9..380aa89c48 100644 --- a/server/vulnerabilities/nvd/cpe_test.go +++ b/server/vulnerabilities/nvd/cpe_test.go @@ -894,10 +894,19 @@ func TestCPEFromSoftwareIntegration(t *testing.T) { software: fleet.Software{ Name: "Python 3.10.6 (64-bit)", Source: "programs", - Version: "3.10.6150.0", + Version: "3.10.6", Vendor: "Python Software Foundation", BundleIdentifier: "", - }, cpe: "cpe:2.3:a:python:python:3.10.6150.0:*:*:*:*:windows:*:*", + }, cpe: "cpe:2.3:a:python:python:3.10.6:*:*:*:*:windows:*:*", + }, + { + software: fleet.Software{ + Name: "Python 3.14.0a1 (64-bit)", + Source: "programs", + Version: "3.14.0-alpha1", + Vendor: "Python Software Foundation", + // should be "cpe:2.3:a:python:python:3.14.0:alpha1:*:*:*:windows:*:*"; see #24810 + }, cpe: "cpe:2.3:a:python:python:3.14.0-alpha1:*:*:*:*:windows:*:*", }, { software: fleet.Software{ diff --git a/server/vulnerabilities/nvd/cpe_translations.json b/server/vulnerabilities/nvd/cpe_translations.json index 00ea56732d..ee81548e5c 100644 --- a/server/vulnerabilities/nvd/cpe_translations.json +++ b/server/vulnerabilities/nvd/cpe_translations.json @@ -70,6 +70,16 @@ "vendor": ["7-zip"] } }, + { + "software": { + "name": ["/^Python 3\\.\\d{1,2}/"], + "source": ["programs"] + }, + "filter": { + "product": ["python"], + "vendor": ["python"] + } + }, { "software": { "name": ["Docs"],