diff --git a/changes/40036-windows-server-2025-vulns b/changes/40036-windows-server-2025-vulns new file mode 100644 index 0000000000..b136cc040c --- /dev/null +++ b/changes/40036-windows-server-2025-vulns @@ -0,0 +1 @@ +* Added vulnerability scanning support for Windows Server 2025 hosts. diff --git a/server/vulnerabilities/msrc/parsed/product.go b/server/vulnerabilities/msrc/parsed/product.go index 09b9caea87..1cba37b36e 100644 --- a/server/vulnerabilities/msrc/parsed/product.go +++ b/server/vulnerabilities/msrc/parsed/product.go @@ -76,6 +76,9 @@ func NewProductFromFullName(fullName string) Product { // We need this to match the product with a host's OS, so we'll add them here. versionString := "" switch { + case strings.Contains(fullName, "Windows Server 2025"): + versionString = "24H2" + case strings.Contains(fullName, "Windows Server 2022"): versionString = "21H2" @@ -196,6 +199,8 @@ func (p Product) Name() string { return "Windows Server 2019" case strings.Contains(val, "Windows Server 2022"): return "Windows Server 2022" + case strings.Contains(val, "Windows Server 2025"): + return "Windows Server 2025" case strings.Contains(val, "Windows Server,"): return "Windows Server" diff --git a/server/vulnerabilities/msrc/parsed/product_test.go b/server/vulnerabilities/msrc/parsed/product_test.go index 4164617b06..7b53ad15c9 100644 --- a/server/vulnerabilities/msrc/parsed/product_test.go +++ b/server/vulnerabilities/msrc/parsed/product_test.go @@ -150,6 +150,18 @@ func TestFullProductName(t *testing.T) { prodName: "Windows Server 2022", finalName: "Windows Server 2022 (Server Core installation) Version 21H2", }, + { + fullName: "Windows Server 2025", + arch: "all", + prodName: "Windows Server 2025", + finalName: "Windows Server 2025 Version 24H2", + }, + { + fullName: "Windows Server 2025 (Server Core installation)", + arch: "all", + prodName: "Windows Server 2025", + finalName: "Windows Server 2025 (Server Core installation) Version 24H2", + }, { fullName: "Windows 10 Version 20H2 for x64-based Systems", arch: "64-bit", @@ -554,6 +566,7 @@ var msrcWinProducts = Products{ "11923": "Windows Server 2022", "11924": "Windows Server 2022 (Server Core installation)", "12244": "Windows Server 2022, 23H2 Edition (Server Core installation)", + "12436": "Windows Server 2025 Version 24H2", } func TestMatchesOperatingSystem(t *testing.T) { @@ -633,6 +646,16 @@ func TestMatchesOperatingSystem(t *testing.T) { want: "12244", err: nil, }, + { + name: "Windows Server 2025 with display version", + os: fleet.OperatingSystem{ + Name: "Microsoft Windows Server 2025 Datacenter 24H2", + Arch: "64-bit", + DisplayVersion: "24H2", + }, + want: "12436", + err: nil, + }, { name: "unknown OS", os: fleet.OperatingSystem{ diff --git a/server/vulnerabilities/msrc/parser_test.go b/server/vulnerabilities/msrc/parser_test.go index 598d2c705e..f7c7eb924a 100644 --- a/server/vulnerabilities/msrc/parser_test.go +++ b/server/vulnerabilities/msrc/parser_test.go @@ -1336,6 +1336,41 @@ func TestParser(t *testing.T) { } }) + t.Run("should include Windows Server 2025 from 2026-Feb feed", func(t *testing.T) { + febSrcPath := filepath.Join("..", "testdata", "msrc-2026-feb.xml.bz2") + febDstPath := filepath.Join(t.TempDir(), "msrc-2026-feb.xml") + extractXMLFixtureFile(t, febSrcPath, febDstPath) + + febF, err := os.Open(febDstPath) + require.NoError(t, err) + febXML, err := parseXML(febF) + febF.Close() + require.NoError(t, err) + + febBulletins, err := mapToSecurityBulletins(febXML) + require.NoError(t, err) + + // Windows Server 2025 bulletin should exist + winServer2025 := febBulletins["Windows Server 2025"] + require.NotNil(t, winServer2025, "expected a bulletin for Windows Server 2025") + require.Equal(t, "Windows Server 2025", winServer2025.ProductName) + + // Should contain both base and Server Core products + require.Contains(t, winServer2025.Products, "12436", "expected product ID 12436 (Windows Server 2025)") + require.Contains(t, winServer2025.Products, "12437", "expected product ID 12437 (Windows Server 2025 Server Core)") + + // Product names should be correctly normalized with version string + require.Equal(t, parsed.Product("Windows Server 2025 Version 24H2"), winServer2025.Products["12436"]) + require.Equal(t, + parsed.Product("Windows Server 2025 (Server Core installation) Version 24H2"), + winServer2025.Products["12437"], + ) + + // Should have vulnerabilities with vendor fixes + require.NotEmpty(t, winServer2025.Vulnerabities, "expected vulnerabilities for Windows Server 2025") + require.NotEmpty(t, winServer2025.VendorFixes, "expected vendor fixes for Windows Server 2025") + }) + t.Run("the remediations are parsed correctly", func(t *testing.T) { // Check the remediations of a random CVE (CVE-2022-29126) expectedRemediations := []msrcxml.VulnerabilityRemediation{ diff --git a/server/vulnerabilities/testdata/msrc-2026-feb.xml.bz2 b/server/vulnerabilities/testdata/msrc-2026-feb.xml.bz2 new file mode 100644 index 0000000000..44e6c79024 Binary files /dev/null and b/server/vulnerabilities/testdata/msrc-2026-feb.xml.bz2 differ