diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs index 0b546c97c3..669207d6f9 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core.Test/Analyze/AnalyzeWorkerTests.cs @@ -478,6 +478,61 @@ await TestAnalyzeAsync( ); } + [Fact] + public async Task SafeVersionsPropertyIsHonored() + { + await TestAnalyzeAsync( + packages: + [ + MockNuGetPackage.CreateSimplePackage("Some.Package", "1.0.0", "net8.0"), // initially this + MockNuGetPackage.CreateSimplePackage("Some.Package", "1.1.0", "net8.0"), // should update to this due to `SafeVersions` + MockNuGetPackage.CreateSimplePackage("Some.Package", "1.2.0", "net8.0"), // this should not be considered + ], + discovery: new() + { + Path = "/", + Projects = [ + new() + { + FilePath = "./project.csproj", + TargetFrameworks = ["net8.0"], + Dependencies = [ + new("Some.Package", "1.0.0", DependencyType.PackageReference), + ], + ReferencedProjectPaths = [], + ImportedFiles = [], + AdditionalFiles = [], + }, + ], + }, + dependencyInfo: new() + { + Name = "Some.Package", + Version = "1.0.0", + IgnoredVersions = [], + IsVulnerable = false, + Vulnerabilities = [ + new() + { + DependencyName = "Some.Package", + PackageManager = "nuget", + VulnerableVersions = [Requirement.Parse(">= 1.0.0, < 1.1.0")], + SafeVersions = [Requirement.Parse("= 1.1.0")] + } + ], + }, + expectedResult: new() + { + UpdatedVersion = "1.1.0", + CanUpdate = true, + VersionComesFromMultiDependencyProperty = false, + UpdatedDependencies = [ + new("Some.Package", "1.1.0", DependencyType.Unknown, TargetFrameworks: ["net8.0"]), + ], + } + ); + } + [Fact] public async Task VersionFinderCanHandle404FromPackageSource_V2() { diff --git a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs index cf0555a9db..83cb185844 100644 --- a/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs +++ b/nuget/helpers/lib/NuGetUpdater/NuGetUpdater.Core/Analyze/VersionFinder.cs @@ -113,11 +113,33 @@ internal static Func CreateVersionFilter(DependencyInfo depe ? versionRange.MinVersion : null; - return version => (currentVersion is null || version > currentVersion) - && versionRange.Satisfies(version) - && (currentVersion is null || !currentVersion.IsPrerelease || !version.IsPrerelease || version.Version == currentVersion.Version) - && !dependencyInfo.IgnoredVersions.Any(r => r.IsSatisfiedBy(version)) - && !dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version)); + var safeVersions = dependencyInfo.Vulnerabilities.SelectMany(v => v.SafeVersions).ToList(); + return version => + { + if (safeVersions.Any()) + { + // only consider these + if (safeVersions.Any(s => s.IsSatisfiedBy(version))) + { + return true; + } + + return false; + } + else + { + var versionGreaterThanCurrent = currentVersion is null || version > currentVersion; + var rangeSatisfies = versionRange.Satisfies(version); + var prereleaseTypeMatches = currentVersion is null || !currentVersion.IsPrerelease || !version.IsPrerelease || version.Version == currentVersion.Version; + var isIgnoredVersion = dependencyInfo.IgnoredVersions.Any(i => i.IsSatisfiedBy(version)); + var isVulnerableVersion = dependencyInfo.Vulnerabilities.Any(v => v.IsVulnerable(version)); + return versionGreaterThanCurrent + && rangeSatisfies + && prereleaseTypeMatches + && !isIgnoredVersion + && !isVulnerableVersion; + } + }; } internal static Func CreateVersionFilter(NuGetVersion currentVersion)