-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental NuGet detector for framework and dev dependencies (#…
…1285) * Add experimental NuGet detector for framework and dev dependencies This PR does 3 things. 1. Adds `TargetFramework` to NuGet package references. This can be useful when querying component data to understand if components are used in a place where a vulnerability applies. 2. Adds _framework package_ handling. The .NET SDK will do [conflict resolution](https://github.com/dotnet/sdk/tree/main/src/Tasks/Common/ConflictResolution) and drop assets from packages that overlap with the framework. NuGet is planning to do the same NuGet/Home#7344 but until then, it's beneficial to have component detection duplicate some of this logic. When a package is identified as overlapping with the framework we'll treat it as a Development Dependency so that it might be auto-dismissed. - .NETFramework projects do not get this - .NETFramework does not participate in conflict resolution by default. Even when enabled framework assemblies can be bypassed using bindingRedirects, or avoiding references to them. Due this fragility it's not safe to apply framework package rules to .NETFramework. - packages.config usage is also excluded since it precludes SDK conflict resolution (and is also only used on .NETFramework projects). 3. Recognizes `ExcludeAssets="Runtime"` usage as a Development Dependencies, also any packages which don't contribute to "runtime" will be developement dependencies. I reused _Development Dependency_ rather than plumbing a new concept. I only mapped data for the `Microsoft.NETCore.App` - the default shared framework. We could consider doing the same for `Microsoft.ASPNETCore.App` and `Microsoft.WindowsDesktop.App` but we'd need to plumb the reference information out of the assets file - currently that's not read and I'm not aware of a supported NuGet API for reading it (though it is present under `project/frameworks/<framework>/frameworkReferences/<name>` .NET Core 1.x has no data since it was packages itself. I have a fallback for future frameworks to read the data from the targeting packs. * Address feedback
- Loading branch information
Showing
21 changed files
with
1,304 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
src/Microsoft.ComponentDetection.Detectors/nuget/FrameworkPackages/FrameworkPackages.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
namespace Microsoft.ComponentDetection.Detectors.NuGet; | ||
|
||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using global::NuGet.Frameworks; | ||
using global::NuGet.Versioning; | ||
|
||
/// <summary> | ||
/// Represents a set of packages that are provided by a specific framework. | ||
/// At the moment this only represents the packages that are provided by the Microsoft.NETCore.App framework. | ||
/// We could extend this to represent the packages provided by other frameworks like Microsoft.AspNetCore.App and Microsoft.WindowsDesktop.App. | ||
/// </summary> | ||
internal sealed partial class FrameworkPackages : IEnumerable<KeyValuePair<string, NuGetVersion>>, IEnumerable | ||
{ | ||
private static readonly Dictionary<NuGetFramework, FrameworkPackages> FrameworkPackagesByFramework = []; | ||
|
||
static FrameworkPackages() | ||
{ | ||
AddPackages(NETStandard20.Instance); | ||
AddPackages(NETStandard21.Instance); | ||
AddPackages(NETCoreApp20.Instance); | ||
AddPackages(NETCoreApp21.Instance); | ||
AddPackages(NETCoreApp22.Instance); | ||
AddPackages(NETCoreApp30.Instance); | ||
AddPackages(NETCoreApp31.Instance); | ||
AddPackages(NETCoreApp50.Instance); | ||
AddPackages(NETCoreApp60.Instance); | ||
AddPackages(NETCoreApp70.Instance); | ||
AddPackages(NETCoreApp80.Instance); | ||
AddPackages(NETCoreApp90.Instance); | ||
|
||
static void AddPackages(FrameworkPackages packages) => FrameworkPackagesByFramework[packages.Framework] = packages; | ||
} | ||
|
||
public FrameworkPackages(NuGetFramework framework) => this.Framework = framework; | ||
|
||
public FrameworkPackages(NuGetFramework framework, FrameworkPackages frameworkPackages) | ||
: this(framework) => this.Packages = new(frameworkPackages.Packages); | ||
|
||
public NuGetFramework Framework { get; } | ||
|
||
public Dictionary<string, NuGetVersion> Packages { get; } = new Dictionary<string, NuGetVersion>(StringComparer.OrdinalIgnoreCase); | ||
|
||
public static FrameworkPackages GetFrameworkPackages(NuGetFramework framework) | ||
{ | ||
if (FrameworkPackagesByFramework.TryGetValue(framework, out var frameworkPackages)) | ||
{ | ||
return frameworkPackages; | ||
} | ||
|
||
// if we didn't predefine the package overrides, load them from the targeting pack | ||
// we might just leave this out since in future frameworks we'll have this functionality built into NuGet. | ||
var frameworkPackagesFromPack = LoadFrameworkPackagesFromPack(framework); | ||
|
||
return FrameworkPackagesByFramework[framework] = frameworkPackagesFromPack ?? new FrameworkPackages(framework); | ||
} | ||
|
||
private static FrameworkPackages LoadFrameworkPackagesFromPack(NuGetFramework framework) | ||
{ | ||
if (framework is null || framework.Framework != FrameworkConstants.FrameworkIdentifiers.NetCoreApp) | ||
{ | ||
return null; | ||
} | ||
|
||
// packs location : %ProgramFiles%\dotnet\packs | ||
var packsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "packs", "Microsoft.NETCore.App.Ref"); | ||
if (!Directory.Exists(packsFolder)) | ||
{ | ||
return null; | ||
} | ||
|
||
var packVersionPattern = $"{framework.Version.Major}.{framework.Version.Minor}.*"; | ||
var packDirectories = Directory.GetDirectories(packsFolder, packVersionPattern); | ||
var packageOverridesFile = packDirectories | ||
.Select(d => (Overrides: Path.Combine(d, "data", "PackageOverrides.txt"), Version: ParseVersion(Path.GetFileName(d)))) | ||
.Where(d => File.Exists(d.Overrides)) | ||
.OrderByDescending(d => d.Version) | ||
.FirstOrDefault().Overrides; | ||
|
||
if (packageOverridesFile == null) | ||
{ | ||
// we should also try to grab them from the user's package folder - they'll be in one location or the other. | ||
return null; | ||
} | ||
|
||
// Adapted from https://github.com/dotnet/sdk/blob/c3a8f72c3a5491c693ff8e49e7406136a12c3040/src/Tasks/Common/ConflictResolution/PackageOverride.cs#L52-L68 | ||
var frameworkPackages = new FrameworkPackages(framework); | ||
var packageOverrides = File.ReadAllLines(packageOverridesFile); | ||
|
||
foreach (var packageOverride in packageOverrides) | ||
{ | ||
var packageOverrideParts = packageOverride.Trim().Split('|'); | ||
|
||
if (packageOverrideParts.Length == 2) | ||
{ | ||
var packageId = packageOverrideParts[0]; | ||
var packageVersion = ParseVersion(packageOverrideParts[1]); | ||
|
||
frameworkPackages.Packages[packageId] = packageVersion; | ||
} | ||
} | ||
|
||
return frameworkPackages; | ||
|
||
static NuGetVersion ParseVersion(string versionString) => NuGetVersion.TryParse(versionString, out var version) ? version : null; | ||
} | ||
|
||
private void Add(string id, string version) | ||
{ | ||
// intentionally redirect to indexer to allow for overwrite | ||
this.Packages[id] = NuGetVersion.Parse(version); | ||
} | ||
|
||
public bool IsAFrameworkComponent(string id, NuGetVersion version) => this.Packages.TryGetValue(id, out var frameworkPackageVersion) && frameworkPackageVersion >= version; | ||
|
||
IEnumerator<KeyValuePair<string, NuGetVersion>> IEnumerable<KeyValuePair<string, NuGetVersion>>.GetEnumerator() => this.Packages.GetEnumerator(); | ||
|
||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); | ||
} |
81 changes: 81 additions & 0 deletions
81
...icrosoft.ComponentDetection.Detectors/nuget/FrameworkPackages/FrameworkPackages.net5.0.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
namespace Microsoft.ComponentDetection.Detectors.NuGet; | ||
|
||
using global::NuGet.Frameworks; | ||
|
||
/// <summary> | ||
/// Framework packages for net5.0. | ||
/// </summary> | ||
internal partial class FrameworkPackages | ||
{ | ||
internal static class NETCoreApp50 | ||
{ | ||
internal static FrameworkPackages Instance { get; } = new(NuGetFramework.Parse("net5.0"), NETCoreApp31.Instance) | ||
{ | ||
{ "Microsoft.CSharp", "4.7.0" }, | ||
{ "runtime.debian.8-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.debian.8-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.debian.8-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.debian.8-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.debian.8-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.fedora.23-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.fedora.23-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.fedora.23-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.fedora.23-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.fedora.24-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.fedora.24-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.fedora.24-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.fedora.24-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.opensuse.13.2-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.opensuse.13.2-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.opensuse.13.2-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.opensuse.42.1-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.opensuse.42.1-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.opensuse.42.1-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.osx.10.10-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.osx.10.10-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.osx.10.10-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.osx.10.10-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.rhel.7-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.rhel.7-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.rhel.7-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.rhel.7-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.ubuntu.14.04-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.ubuntu.16.04-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "runtime.ubuntu.16.10-x64.runtime.native.System", "4.3.0" }, | ||
{ "runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression", "4.3.0" }, | ||
{ "runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http", "4.3.0" }, | ||
{ "runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security", "4.3.0" }, | ||
{ "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography", "4.3.0" }, | ||
{ "System.Buffers", "4.5.1" }, | ||
{ "System.Collections.Immutable", "5.0.0" }, | ||
{ "System.ComponentModel.Annotations", "5.0.0" }, | ||
{ "System.Diagnostics.DiagnosticSource", "5.0.0" }, | ||
{ "System.Formats.Asn1", "5.0.0" }, | ||
{ "System.Net.Http.Json", "5.0.0" }, | ||
{ "System.Reflection.DispatchProxy", "4.7.1" }, | ||
{ "System.Reflection.Metadata", "5.0.0" }, | ||
{ "System.Runtime.CompilerServices.Unsafe", "5.0.0" }, | ||
{ "System.Text.Encoding.CodePages", "5.0.0" }, | ||
{ "System.Text.Encodings.Web", "5.0.0" }, | ||
{ "System.Text.Json", "5.0.0" }, | ||
{ "System.Threading.Channels", "5.0.0" }, | ||
{ "System.Threading.Tasks.Dataflow", "5.0.0" }, | ||
}; | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...icrosoft.ComponentDetection.Detectors/nuget/FrameworkPackages/FrameworkPackages.net6.0.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
namespace Microsoft.ComponentDetection.Detectors.NuGet; | ||
|
||
using global::NuGet.Frameworks; | ||
|
||
/// <summary> | ||
/// Framework packages for net6.0. | ||
/// </summary> | ||
internal partial class FrameworkPackages | ||
{ | ||
internal static class NETCoreApp60 | ||
{ | ||
internal static FrameworkPackages Instance { get; } = new(NuGetFramework.Parse("net6.0"), NETCoreApp50.Instance) | ||
{ | ||
{ "Microsoft.Win32.Registry", "5.0.0" }, | ||
{ "System.Collections.Immutable", "6.0.0" }, | ||
{ "System.Diagnostics.DiagnosticSource", "6.0.1" }, | ||
{ "System.Formats.Asn1", "6.0.1" }, | ||
{ "System.IO.FileSystem.AccessControl", "5.0.0" }, | ||
{ "System.IO.Pipes.AccessControl", "5.0.0" }, | ||
{ "System.Net.Http.Json", "6.0.1" }, | ||
{ "System.Reflection.Metadata", "6.0.1" }, | ||
{ "System.Runtime.CompilerServices.Unsafe", "6.0.0" }, | ||
{ "System.Security.AccessControl", "6.0.1" }, | ||
{ "System.Security.Cryptography.Cng", "5.0.0" }, | ||
{ "System.Security.Cryptography.OpenSsl", "5.0.0" }, | ||
{ "System.Security.Principal.Windows", "5.0.0" }, | ||
{ "System.Text.Encoding.CodePages", "6.0.0" }, | ||
{ "System.Text.Encodings.Web", "6.0.0" }, | ||
{ "System.Text.Json", "6.0.9" }, | ||
{ "System.Threading.Channels", "6.0.0" }, | ||
{ "System.Threading.Tasks.Dataflow", "6.0.0" }, | ||
}; | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...icrosoft.ComponentDetection.Detectors/nuget/FrameworkPackages/FrameworkPackages.net7.0.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace Microsoft.ComponentDetection.Detectors.NuGet; | ||
|
||
using global::NuGet.Frameworks; | ||
|
||
/// <summary> | ||
/// Framework packages for net7.0. | ||
/// </summary> | ||
internal partial class FrameworkPackages | ||
{ | ||
internal static class NETCoreApp70 | ||
{ | ||
internal static FrameworkPackages Instance { get; } = new(NuGetFramework.Parse("net7.0"), NETCoreApp60.Instance) | ||
{ | ||
{ "System.Collections.Immutable", "7.0.0" }, | ||
{ "System.Diagnostics.DiagnosticSource", "7.0.2" }, | ||
{ "System.Formats.Asn1", "7.0.0" }, | ||
{ "System.Net.Http.Json", "7.0.1" }, | ||
{ "System.Reflection.Metadata", "7.0.2" }, | ||
{ "System.Text.Encoding.CodePages", "7.0.0" }, | ||
{ "System.Text.Encodings.Web", "7.0.0" }, | ||
{ "System.Text.Json", "7.0.4" }, | ||
{ "System.Threading.Channels", "7.0.0" }, | ||
{ "System.Threading.Tasks.Dataflow", "7.0.0" }, | ||
}; | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...icrosoft.ComponentDetection.Detectors/nuget/FrameworkPackages/FrameworkPackages.net8.0.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
namespace Microsoft.ComponentDetection.Detectors.NuGet; | ||
|
||
using global::NuGet.Frameworks; | ||
|
||
/// <summary> | ||
/// Framework packages for net8.0. | ||
/// </summary> | ||
internal partial class FrameworkPackages | ||
{ | ||
internal static class NETCoreApp80 | ||
{ | ||
internal static FrameworkPackages Instance { get; } = new(NuGetFramework.Parse("net8.0"), NETCoreApp70.Instance) | ||
{ | ||
{ "System.Collections.Immutable", "8.0.0" }, | ||
{ "System.Diagnostics.DiagnosticSource", "8.0.1" }, | ||
{ "System.Formats.Asn1", "8.0.1" }, | ||
{ "System.Net.Http.Json", "8.0.0" }, | ||
{ "System.Reflection.Metadata", "8.0.0" }, | ||
{ "System.Text.Encoding.CodePages", "8.0.0" }, | ||
{ "System.Text.Encodings.Web", "8.0.0" }, | ||
{ "System.Text.Json", "8.0.4" }, | ||
{ "System.Threading.Channels", "8.0.0" }, | ||
{ "System.Threading.Tasks.Dataflow", "8.0.1" }, | ||
}; | ||
} | ||
} |
Oops, something went wrong.