C#: Prefer framework assemblies over arbitrary nuget equivalents

This commit is contained in:
Tamas Vajk
2023-11-30 10:54:19 +01:00
parent fefc02d650
commit 0cc94b3a46
3 changed files with 67 additions and 28 deletions

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -20,7 +19,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// assembly cache.
/// </param>
/// <param name="progressMonitor">Callback for progress.</param>
public AssemblyCache(IEnumerable<string> paths, ProgressMonitor progressMonitor)
public AssemblyCache(IEnumerable<string> paths, IEnumerable<string> frameworkPaths, ProgressMonitor progressMonitor)
{
foreach (var path in paths)
{
@@ -40,7 +39,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
progressMonitor.LogInfo("AssemblyCache: Path not found: " + path);
}
}
IndexReferences();
IndexReferences(frameworkPaths);
}
/// <summary>
@@ -57,13 +56,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
/// <summary>
/// Indexes all DLLs we have located.
/// Because this is a potentially time-consuming operation, it is put into a separate stage.
/// </summary>
private void IndexReferences()
private void IndexReferences(IEnumerable<string> frameworkPaths)
{
// Read all of the files
foreach (var filename in pendingDllsToIndex)
@@ -71,13 +68,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
IndexReference(filename);
}
// Index "assemblyInfo" by version string
// The OrderBy is used to ensure that we by default select the highest version number.
foreach (var info in assemblyInfoByFileName.Values
.OrderBy(info => info.Name)
.ThenBy(info => info.NetCoreVersion ?? emptyVersion)
.ThenBy(info => info.Version ?? emptyVersion)
.ThenBy(info => info.Filename))
.OrderAssemblyInfosByPreference(frameworkPaths))
{
foreach (var index in info.IndexStrings)
{

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal static class AssemblyCacheExtensions
{
private static readonly Version emptyVersion = new Version(0, 0, 0, 0);
/// <summary>
/// This method orders AssemblyInfos by version numbers (.net core version first, then assembly version). Finally, it orders by filename to make the order deterministic.
/// </summary>
public static IOrderedEnumerable<AssemblyInfo> OrderAssemblyInfosByPreference(this IEnumerable<AssemblyInfo> assemblies, IEnumerable<string> frameworkPaths)
{
// prefer framework assemblies over others
bool initialOrdering(AssemblyInfo info) => frameworkPaths.Any(framework => info.Filename.StartsWith(framework, StringComparison.OrdinalIgnoreCase));
var ordered = assemblies is IOrderedEnumerable<AssemblyInfo> o
? o.ThenBy(initialOrdering)
: assemblies.OrderBy(initialOrdering);
return ordered
.ThenBy(info => info.NetCoreVersion ?? emptyVersion)
.ThenBy(info => info.Version ?? emptyVersion)
.ThenBy(info => info.Filename);
}
}
}

View File

@@ -128,16 +128,27 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
DownloadMissingPackages(allNonBinaryFiles, dllPaths);
}
var frameworkLocations = new List<string>();
// Find DLLs in the .Net / Asp.Net Framework
// This block needs to come after the nuget restore, because the nuget restore might fetch the .NET Core/Framework reference assemblies.
if (options.ScanNetFrameworkDlls)
{
AddNetFrameworkDlls(dllPaths);
AddAspNetCoreFrameworkDlls(dllPaths);
AddMicrosoftWindowsDesktopDlls(dllPaths);
var path = AddNetFrameworkDlls(dllPaths);
frameworkLocations.Add(path);
path = AddAspNetCoreFrameworkDlls(dllPaths);
if (path != null)
{
frameworkLocations.Add(path);
}
path = AddMicrosoftWindowsDesktopDlls(dllPaths);
if (path != null)
{
frameworkLocations.Add(path);
}
}
assemblyCache = new AssemblyCache(dllPaths, progressMonitor);
assemblyCache = new AssemblyCache(dllPaths, frameworkLocations, progressMonitor);
AnalyseSolutions(solutions);
foreach (var filename in assemblyCache.AllAssemblies.Select(a => a.Filename))
@@ -146,7 +157,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
RemoveNugetAnalyzerReferences();
ResolveConflicts();
ResolveConflicts(frameworkLocations);
// Output the findings
foreach (var r in usedReferences.Keys.OrderBy(r => r))
@@ -228,7 +239,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private void AddNetFrameworkDlls(ISet<string> dllPaths)
private string AddNetFrameworkDlls(ISet<string> dllPaths)
{
// Multiple dotnet framework packages could be present.
// The order of the packages is important, we're adding the first one that is present in the nuget cache.
@@ -248,7 +259,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
RemoveNugetPackageReference(packagesInPrioOrder[i], dllPaths);
}
return;
return frameworkPath.Path;
}
string? runtimeLocation = null;
@@ -270,6 +281,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
progressMonitor.LogInfo($".NET runtime location selected: {runtimeLocation}");
dllPaths.Add(runtimeLocation);
return runtimeLocation;
}
private void RemoveNugetPackageReference(string packagePrefix, ISet<string> dllPaths)
@@ -294,11 +306,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
private void AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
private string? AddAspNetCoreFrameworkDlls(ISet<string> dllPaths)
{
if (!fileContent.IsNewProjectStructureUsed || !fileContent.UseAspNetCoreDlls)
{
return;
return null;
}
// First try to find ASP.NET Core assemblies in the NuGet packages
@@ -306,21 +318,29 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
progressMonitor.LogInfo($"Found ASP.NET Core in NuGet packages. Not adding installation directory.");
dllPaths.Add(aspNetCorePackage);
return aspNetCorePackage;
}
else if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
if (Runtime.AspNetCoreRuntime is string aspNetCoreRuntime)
{
progressMonitor.LogInfo($"ASP.NET runtime location selected: {aspNetCoreRuntime}");
dllPaths.Add(aspNetCoreRuntime);
return aspNetCoreRuntime;
}
return null;
}
private void AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
private string? AddMicrosoftWindowsDesktopDlls(ISet<string> dllPaths)
{
if (GetPackageDirectory(FrameworkPackageNames.WindowsDesktopFramework) is string windowsDesktopApp)
{
progressMonitor.LogInfo($"Found Windows Desktop App in NuGet packages.");
dllPaths.Add(windowsDesktopApp);
return windowsDesktopApp;
}
return null;
}
private string? GetPackageDirectory(string packagePrefix)
@@ -472,7 +492,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// If the same assembly name is duplicated with different versions,
/// resolve to the higher version number.
/// </summary>
private void ResolveConflicts()
private void ResolveConflicts(IEnumerable<string> frameworkPaths)
{
var sortedReferences = new List<AssemblyInfo>();
foreach (var usedReference in usedReferences)
@@ -488,11 +508,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
var emptyVersion = new Version(0, 0);
sortedReferences = sortedReferences
.OrderBy(r => r.NetCoreVersion ?? emptyVersion)
.ThenBy(r => r.Version ?? emptyVersion)
.ThenBy(r => r.Filename)
.OrderAssemblyInfosByPreference(frameworkPaths)
.ToList();
var finalAssemblyList = new Dictionary<string, AssemblyInfo>();