| File: SdkVulnerability\SdkVulnerabilityChecker.cs | Web Access |
| Project: src\sdk\src\Cli\dotnet\dotnet.csproj (dotnet) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Deployment.DotNet.Releases; namespace Microsoft.DotNet.Cli.SdkVulnerability; /// <summary> /// Checks an SDK version against release metadata for vulnerabilities and EOL status. /// Pure logic — no I/O or network calls. /// </summary> internal static class SdkVulnerabilityChecker { /// <summary> /// Checks the given SDK version against the product collection and release data. /// </summary> /// <param name="sdkVersionString">The resolved SDK version string (e.g. "9.0.300").</param> /// <param name="productCollection">The product collection from releases-index.json.</param> /// <param name="getProductReleases">A function to get releases for a product (may read from cache).</param> /// <returns>Vulnerability info, or null if the SDK version cannot be matched to release data.</returns> public static SdkVulnerabilityInfo? Check( string sdkVersionString, ProductCollection productCollection, Func<Product, IEnumerable<ProductRelease>> getProductReleases) { if (!ReleaseVersion.TryParse(sdkVersionString, out ReleaseVersion? sdkVersion)) { return null; } // Find the product channel for this SDK version (e.g. "9.0" for SDK "9.0.300") string channelVersion = $"{sdkVersion.Major}.{sdkVersion.Minor}"; Product? product = productCollection .FirstOrDefault(p => p.ProductVersion.Equals(channelVersion)); if (product is null) { return null; } bool isEol = product.IsOutOfSupport(); DateTime? eolDate = product.EndOfLifeDate; // Get all releases for this channel to find CVEs IEnumerable<ProductRelease> releases = getProductReleases(product); List<ProductRelease> releaseList = releases.ToList(); if (releaseList.Count == 0) { // No release data available — can still report EOL status return new SdkVulnerabilityInfo { IsEol = isEol, EolDate = eolDate, }; } // Find which release contains this SDK version int currentReleaseIndex = -1; for (int i = 0; i < releaseList.Count; i++) { if (releaseList[i].Sdks.Any(sdk => sdk.Version == sdkVersion)) { currentReleaseIndex = i; break; } } // If the SDK version isn't present in any release, we can't determine // its vulnerability status — return null per the documented contract. if (currentReleaseIndex < 0) { return null; } // Collect CVEs from all releases newer than the current one. // Releases are ordered newest-first from the API. // All CVEs in releases after our SDK's release are vulnerabilities that affect us. var cves = new List<SdkCveInfo>(); if (currentReleaseIndex > 0) { for (int i = 0; i < currentReleaseIndex; i++) { foreach (var cve in releaseList[i].Cves) { cves.Add(new SdkCveInfo { Id = cve.Id, Url = cve.DescriptionLink?.ToString() ?? $"https://cve.mitre.org/cgi-bin/cvename.cgi?name={cve.Id}" }); } } } // Deduplicate CVEs by ID (same CVE can appear in multiple releases) cves = cves.DistinctBy(c => c.Id).ToList(); // Find the latest SDK version, preferring the same feature band. // If the user's band has no newer SDK but a newer SDK exists in a different // band on the channel, recommend that and flag the band as discontinued. string? latestInBand = FindLatestSdkInFeatureBand(sdkVersion, product, releaseList); string? recommendedVersion = latestInBand; bool featureBandDiscontinued = false; if (latestInBand is null && product.LatestSdkVersion is not null && product.LatestSdkVersion > sdkVersion) { recommendedVersion = product.LatestSdkVersion.ToString(); featureBandDiscontinued = true; } return new SdkVulnerabilityInfo { IsEol = isEol, EolDate = eolDate, Cves = cves, LatestSdkVersion = recommendedVersion, FeatureBandDiscontinued = featureBandDiscontinued, }; } private static string? FindLatestSdkInFeatureBand( ReleaseVersion sdkVersion, Product product, List<ProductRelease> releases) { // If the product's latest SDK is in the same feature band, use it directly if (product.LatestSdkVersion?.SdkFeatureBand == sdkVersion.SdkFeatureBand && product.LatestSdkVersion > sdkVersion) { return product.LatestSdkVersion.ToString(); } // Otherwise, search through releases for the newest SDK in our feature band ReleaseVersion? latest = null; foreach (var release in releases) { foreach (var sdk in release.Sdks) { if (sdk.Version.SdkFeatureBand == sdkVersion.SdkFeatureBand && sdk.Version > sdkVersion && (latest is null || sdk.Version > latest)) { latest = sdk.Version; } } } return latest?.ToString(); } }