diff --git a/README.md b/README.md
index 46d8efa8..3f3c993c 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,15 @@ View the full documentation for NuGetDefense [here](https://digitalcoyote.github
## Features
* Uses Multiple Sources to check for known vulnerabilities in third-party libraries (NuGet packages)
- * [OSS Index](https://ossindex.sonatype.org/)
+ * [OSS Index](https://ossindex.sonatype.org/) (Caching Coming Soon!)
* [National Vulnerability Database](https://nvd.nist.gov/) (Optionally Self-Updating)
+ * [Google's Open Source Vulnerabilities Database](https://osv.dev/) ([Coming Soon!](https://github.com/digitalcoyote/NuGetDefense/discussions/53))
* Simple installation/configuration: the [NuGet Package](https://www.nuget.org/packages/NuGetDefense/) is all you need.
* Transitive Dependency Checking
* SDK style projects only (older project format is not supported by the dotnet cli)
* Uses the versions resolved by the dotnet cli at build
+* Project Reference Scanning
+ * Scan all projects in a hierarchy by installing NuGet Defense to the top level package ([pre-release](https://www.nuget.org/packages/NuGetDefense/2.1.0-pre0011))
* Allow breaking the build based on severity of vulnerability.
* Ignore specific vulnerabilities/packages.
* Sensitive/Internal Packages filtering
diff --git a/Src/NuGetDefense.sln.DotSettings b/Src/NuGetDefense.sln.DotSettings
new file mode 100644
index 00000000..05363485
--- /dev/null
+++ b/Src/NuGetDefense.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/Src/NuGetDefense/Configuration/Settings.cs b/Src/NuGetDefense/Configuration/Settings.cs
index 1c679ab5..64e3f9f4 100644
--- a/Src/NuGetDefense/Configuration/Settings.cs
+++ b/Src/NuGetDefense/Configuration/Settings.cs
@@ -10,7 +10,7 @@ namespace NuGetDefense.Configuration
{
public class Settings
{
- public bool WarnOnly { get; set; } = false;
+ public bool WarnOnly { get; set; }
public FileLogSettings Log
{
@@ -18,17 +18,18 @@ public FileLogSettings Log
set { Logs = new[] {value}; }
}
- public VulnerabilityReportsSettings VulnerabilityReports { get; set; } = new VulnerabilityReportsSettings();
+ public VulnerabilityReportsSettings VulnerabilityReports { get; set; } = new();
public FileLogSettings[] Logs { get; set; }
public bool CheckTransitiveDependencies { get; set; } = true;
+ public bool CheckReferencedProjects{ get; set; }
- public BuildErrorSettings ErrorSettings { get; set; } = new BuildErrorSettings();
+ public BuildErrorSettings ErrorSettings { get; set; } = new();
- public RemoteVulnerabilitySourceConfiguration OssIndex { get; set; } = new RemoteVulnerabilitySourceConfiguration();
+ public RemoteVulnerabilitySourceConfiguration OssIndex { get; set; } = new();
public OfflineVulnerabilitySourceConfiguration NVD { get; set; } =
- new OfflineVulnerabilitySourceConfiguration();
+ new();
public string[] SensitivePackages { get; set; } = new string[0];
diff --git a/Src/NuGetDefense/NuGetDefense.csproj b/Src/NuGetDefense/NuGetDefense.csproj
index 4100aa21..f9c282b5 100644
--- a/Src/NuGetDefense/NuGetDefense.csproj
+++ b/Src/NuGetDefense/NuGetDefense.csproj
@@ -8,7 +8,7 @@
NuGetDefense ~ Check for Known Vulnerabilities at Build
NuGetDefense was inspired by [OWASP SafeNuGet](https://nuget.org/packages/SafeNuGet/) but aims to check with multiple sources for known vulnerabilities.
Curtis Carter 2020
- 8
+ 9
Debug;Release;DotNetTool
AnyCPU
https://digitalcoyote.github.io/NuGetDefense/
@@ -30,15 +30,19 @@
nugetdefense
1.0.15
+
+
+
-
-
-
-
+
+
+
+
+
-
+
diff --git a/Src/NuGetDefense/NuGetDefense.nuspec b/Src/NuGetDefense/NuGetDefense.nuspec
index 30b4c6f1..8a6f1ed3 100644
--- a/Src/NuGetDefense/NuGetDefense.nuspec
+++ b/Src/NuGetDefense/NuGetDefense.nuspec
@@ -3,7 +3,7 @@
NuGetDefense
NuGetDefense
- 1.0.15.1
+ 1.0.16
Curtis Carter
Curtis Carter
https://digitalcoyote.github.io/NuGetDefense/
diff --git a/Src/NuGetDefense/Program.cs b/Src/NuGetDefense/Program.cs
index bd630bb4..351d27c7 100644
--- a/Src/NuGetDefense/Program.cs
+++ b/Src/NuGetDefense/Program.cs
@@ -2,21 +2,25 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
+using ByteDev.DotNet.Project;
+using ByteDev.DotNet.Solution;
using NuGet.Versioning;
using NuGetDefense.Configuration;
using NuGetDefense.Core;
-using NuGetDefense.OSSIndex;
+using NuGetDefense.NVD;
using Serilog;
using static NuGetDefense.UtilityMethods;
+using Scanner = NuGetDefense.OSSIndex.Scanner;
namespace NuGetDefense
{
- internal class Program
+ internal static class Program
{
private static readonly string UserAgentString = @$"NuGetDefense/{Version}";
@@ -24,8 +28,9 @@ internal class Program
private static string _nuGetFile;
private static string _projectFileName;
- private static NuGetPackage[] _pkgs;
+ private static Dictionary _projects;
private static Settings _settings;
+ public static int NumberOfVulnerabilities;
///
/// args[0] is expected to be the path to the project file.
@@ -38,8 +43,10 @@ private static int Main(string[] args)
{
Console.WriteLine($"NuGetDefense v{Version}");
Console.WriteLine("-------------");
- Console.WriteLine("\nUsage:");
- Console.WriteLine(" nugetdefense projectFile.proj TargetFrameworkMoniker");
+ Console.WriteLine($"{Environment.NewLine}Usage:");
+ Console.WriteLine($"{Environment.NewLine} nugetdefense projectFile.proj TargetFrameworkMoniker");
+ Console.WriteLine($"{Environment.NewLine} nugetdefense SolutionFile.sln Release");
+ Console.WriteLine($"{Environment.NewLine} nugetdefense SolutionFile.sln Debug|Any CPU");
return 0;
}
#endif
@@ -52,20 +59,70 @@ private static int Main(string[] args)
Log.Logger.Verbose("Logging Configured");
Log.Logger.Verbose("Started NuGetDefense with arguments: {args}", args);
- var nugetFile = new NuGetFile(args[0]);
- _nuGetFile = nugetFile.Path;
- Log.Logger.Verbose("NuGetFile Path: {nugetFilePath}", _nuGetFile);
+ var targetFramework = args.Length == 2 ? args[1] : "";
+ if (args[0].EndsWith(".sln", StringComparison.OrdinalIgnoreCase))
+ {
+ var projects = DotNetSolution.Load(args[0]).Projects.Select(p => p.Path).ToArray();
+ var specificFramework = !string.IsNullOrWhiteSpace(targetFramework);
+ if (specificFramework)
+ {
+ Log.Logger.Information("Target Framework: {framework}", targetFramework);
+ }
- var targetFramework = args.Length > 1 ? args[1] : "";
- Log.Logger.Information("Target Framework: {framework}", string.IsNullOrWhiteSpace(targetFramework) ? "Undefined" : targetFramework);
- Log.Logger.Verbose("Loading Packages");
- Log.Logger.Verbose("Transitive Dependencies Included: {CheckTransitiveDependencies}", _settings.CheckTransitiveDependencies);
- _pkgs = nugetFile.LoadPackages(targetFramework, _settings.CheckTransitiveDependencies).Values.ToArray();
- var nonSensitivePackages = GetNonSensitivePackages(_pkgs);
+ _projects = LoadMultipleProjects(args[0], projects, specificFramework, targetFramework, true);
+ }
+ else if (_settings.CheckReferencedProjects)
+ {
+ var projects = new List{args[0]};
+ GetProjectsReferenced(in args[0], in projects);
+ var specificFramework = !string.IsNullOrWhiteSpace(targetFramework);
+ if (specificFramework)
+ {
+ Log.Logger.Information("Target Framework: {framework}", targetFramework);
+ }
+
+ _projects = LoadMultipleProjects(args[0], projects.ToArray(), specificFramework, targetFramework, false);
+ }
+ else
+ {
+ var nugetFile = new NuGetFile(args[0]);
+ _nuGetFile = nugetFile.Path;
+
+ Log.Logger.Verbose("NuGetFile Path: {nugetFilePath}", _nuGetFile);
+
+ Log.Logger.Information("Target Framework: {framework}", string.IsNullOrWhiteSpace(targetFramework) ? "Undefined" : targetFramework);
+ Log.Logger.Verbose("Loading Packages");
+ Log.Logger.Verbose("Transitive Dependencies Included: {CheckTransitiveDependencies}", _settings.CheckTransitiveDependencies);
+
+ if (_settings.CheckTransitiveDependencies && nugetFile.PackagesConfig)
+ {
+ var projects = DotNetProject.Load(args[0]).ProjectReferences.Select(p => p.FilePath).ToArray();
+ var specificFramework = !string.IsNullOrWhiteSpace(targetFramework);
+ if (specificFramework)
+ {
+ Log.Logger.Information("Target Framework: {framework}", targetFramework);
+ }
+
+ _projects = LoadMultipleProjects(args[0], projects, specificFramework, targetFramework);
+ }
+ else
+ {
+ _projects = new Dictionary();
+ _projects.Add(nugetFile.Path, nugetFile.LoadPackages(targetFramework, _settings.CheckTransitiveDependencies).Values.ToArray());
+ }
+ }
+
+ GetNonSensitivePackages( out var nonSensitivePackages);
if (_settings.ErrorSettings.IgnoredPackages.Length > 0)
- IgnorePackages(_pkgs, _settings.ErrorSettings.IgnoredPackages, out _pkgs);
- Log.Logger.Information("Loaded {packageCount} packages", _pkgs.Length);
+ {
+ foreach (var (project, packages) in _projects)
+ {
+ IgnorePackages(in packages, _settings.ErrorSettings.IgnoredPackages, out var projPackages);
+ _projects[project] = projPackages;
+ }
+ }
+ Log.Logger.Information("Loaded {packageCount} packages", _projects.Sum(p => p.Value.Length));
if (_settings.ErrorSettings.BlockedPackages.Length > 0) CheckBlockedPackages();
if (_settings.ErrorSettings.AllowedPackages.Length > 0) CheckAllowedPackages();
@@ -75,18 +132,21 @@ private static int Main(string[] args)
Log.Logger.Verbose("Checking with OSSIndex for Vulnerabilities");
vulnDict =
new Scanner(_nuGetFile, _settings.OssIndex.BreakIfCannotRun, UserAgentString, _settings.OssIndex.Username, _settings.OssIndex.ApiToken)
- .GetVulnerabilitiesForPackages(nonSensitivePackages);
+ .GetVulnerabilitiesForPackages(nonSensitivePackages.SelectMany(p => p.Value).ToArray());
}
if (_settings.NVD.Enabled)
{
Log.Logger.Verbose("Checking the embedded NVD source for Vulnerabilities");
- vulnDict =
- new NVD.Scanner(_nuGetFile, TimeSpan.FromSeconds(_settings.NVD.TimeoutInSeconds),
- _settings.NVD.BreakIfCannotRun, _settings.NVD.SelfUpdate)
- .GetVulnerabilitiesForPackages(_pkgs,
- vulnDict);
+ foreach (var (proj, pkgs) in _projects)
+ {
+ vulnDict =
+ new NVD.Scanner(_nuGetFile, TimeSpan.FromSeconds(_settings.NVD.TimeoutInSeconds),
+ _settings.NVD.BreakIfCannotRun, _settings.NVD.SelfUpdate)
+ .GetVulnerabilitiesForPackages(pkgs,
+ vulnDict);
+ }
}
Log.Logger.Information("ignoring {ignoredCVECount} Vulnerabilities", _settings.ErrorSettings.IgnoredCvEs.Length);
@@ -94,7 +154,7 @@ private static int Main(string[] args)
VulnerabilityData.IgnoreCVEs(vulnDict, _settings.ErrorSettings.IgnoredCvEs);
ReportVulnerabilities(vulnDict);
- if (vulnDict?.Count == 0) return 0;
+ return _settings.WarnOnly ? 0 : NumberOfVulnerabilities;
}
catch (Exception e)
{
@@ -104,8 +164,76 @@ private static int Main(string[] args)
Log.Logger.Fatal(msBuildMessage);
return -1;
}
+ }
+
+ private static void GetProjectsReferenced(in string proj, in List projectList)
+ {
+ var dir = Path.GetDirectoryName(proj);
+ foreach (var referencedProj in DotNetProject.Load(proj).ProjectReferences
+ .Select(p => Path.Combine(dir!, p.FilePath.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar))))
+ {
+ if(!projectList.Contains(referencedProj))
+ projectList.Add(referencedProj);
+ GetProjectsReferenced(in referencedProj, in projectList);
+ }
+ }
+
+ private static Dictionary LoadMultipleProjects(string TopLevelProject, string[] projects, bool specificFramework, string targetFramework, bool solutionFile = false)
+ {
+ var projectPackages = new Dictionary();
+ for (var i = 0; i < projects.Length; i++)
+ {
+ var pkgs = new List();
+
+ var project = projects[i];
+ var path = Path.Combine(Path.GetDirectoryName(TopLevelProject)!, project
+ .Replace('\\', Path.DirectorySeparatorChar)
+ .Replace('/', Path.DirectorySeparatorChar));
+
+ var proj = DotNetProject.Load(path);
+ if (!specificFramework && proj.Format == ProjectFormat.New)
+ {
+ var monikersListBuilder = new StringBuilder();
+ var monikers = proj.ProjectTargets.Select(t => t.Moniker).ToArray();
+ monikersListBuilder.Append(monikers[0]);
+ for (var index = 1; index < monikers.Length; index++)
+ {
+ monikersListBuilder.Append($", {monikers[index]}");
+ }
+
+ Log.Logger.Information("Target Frameworks for {project}: {frameworks}", project, monikersListBuilder.ToString());
+ var nugetFile = new NuGetFile(path);
+ pkgs.AddDistinctPackages(monikers.SelectMany(m => nugetFile.LoadPackages(m, _settings.CheckTransitiveDependencies).Values));
+ }
+ else
+ {
+ pkgs.AddDistinctPackages(new NuGetFile(path).LoadPackages(targetFramework, _settings.CheckTransitiveDependencies).Values);
+ }
+ projectPackages.Add(path, pkgs.ToArray());
+ }
+
+ var nuGetFile = new NuGetFile(TopLevelProject);
+ _nuGetFile = nuGetFile.Path;
+
+ if (!solutionFile && !projectPackages.ContainsKey(TopLevelProject)) projectPackages.Add(TopLevelProject, nuGetFile.LoadPackages(targetFramework, _settings.CheckTransitiveDependencies).Values.ToArray());
+
+ return projectPackages;
+ }
+
+ private static void AddDistinctPackages(this List pkgs, IEnumerable newPkgs)
+ {
+ foreach (var pkg in newPkgs)
+ {
+ if (pkgs.Any(p => p.Id == pkg.Id && p.Version == pkg.Version)) continue;
+
+ pkgs.Add(pkg);
+ }
+ }
- return 0;
+ private static void ParseSolutionForProjects(string s)
+ {
+ // TODO: This will parse hte solution file into a list of projects relative to the solution file.
+ throw new NotImplementedException();
}
///
@@ -113,17 +241,23 @@ private static int Main(string[] args)
///
///
/// a list of packages that do not match the wild card strings in SensitivePackages
- public static NuGetPackage[] GetNonSensitivePackages(NuGetPackage[] nuGetPackages)
+ public static void GetNonSensitivePackages(out Dictionary nonSensitives)
{
- var sensitiveRegexSet = _settings.SensitivePackages.Select(sp => Regex.Escape(sp).Replace(@"\*", ".*"));
- return nuGetPackages.Where(p => !sensitiveRegexSet.Any(x => Regex.IsMatch(p.Id, x))).ToArray();
+ nonSensitives = new Dictionary();
+ var sensitiveRegexSet = _settings.SensitivePackages.Select(sp => Regex.Escape(sp).Replace(@"\*", ".*")).ToArray();
+ if (!sensitiveRegexSet.Any()) return;
+ foreach (var (project, packages) in _projects)
+ {
+ nonSensitives.Add(project, packages.Where(p => !sensitiveRegexSet.Any(x => Regex.IsMatch(p.Id, x))).ToArray());
+ }
}
private static void CheckAllowedPackages()
{
Log.Logger.Verbose("Checking Allowed Packages");
- foreach (var pkg in _pkgs.Where(p => !_settings.ErrorSettings.AllowedPackages.Any(b =>
+ foreach (var (project, packages) in _projects)
+ foreach (var pkg in packages.Where(p => !_settings.ErrorSettings.AllowedPackages.Any(b =>
b.Id == p.Id &&
(string.IsNullOrWhiteSpace(b.Version) || VersionRange.Parse(p.Version).Satisfies(new NuGetVersion(b.Version))))))
{
@@ -140,26 +274,30 @@ private static void ReportVulnerabilities(Dictionary p.Value.Length));
}
else
{
Log.Logger.Verbose("Building report of Vulnerabilities found in {numberOfPackages} packages", vulnDict.Keys.Count);
var vulnReporter = new VulnerabilityReporter();
- vulnReporter.BuildVulnerabilityTextReport(vulnDict, _pkgs, _nuGetFile, _settings.WarnOnly,
- _settings.ErrorSettings.Cvss3Threshold);
- if (_settings.VulnerabilityReports.OutputTextReport) Log.Logger.Information(vulnReporter.VulnerabilityTextReport);
- foreach (var msBuildMessage in vulnReporter.MsBuildMessages)
+
+ foreach (var (project, packages) in _projects)
{
- Console.WriteLine(msBuildMessage);
- Log.Logger.Debug(msBuildMessage);
+ //TODO: Losing the right file somewhere here
+ vulnReporter.BuildVulnerabilityTextReport(vulnDict, packages, project, _settings.WarnOnly,
+ _settings.ErrorSettings.Cvss3Threshold, out NumberOfVulnerabilities);
+ if (_settings.VulnerabilityReports.OutputTextReport) Log.Logger.Information(vulnReporter.VulnerabilityTextReport);
+ foreach (var msBuildMessage in vulnReporter.MsBuildMessages)
+ {
+ Console.WriteLine(msBuildMessage);
+ Log.Logger.Debug(msBuildMessage);
+ }
}
if (string.IsNullOrWhiteSpace(_settings.VulnerabilityReports.JsonReportPath) && string.IsNullOrWhiteSpace(_settings.VulnerabilityReports.XmlReportPath)) return;
- vulnReporter.BuildVulnerabilityReport(vulnDict, _pkgs, _nuGetFile, _settings.WarnOnly,
- _settings.ErrorSettings.Cvss3Threshold);
+ vulnReporter.BuildVulnerabilityReport(vulnDict, _projects, _settings.WarnOnly);
if (!string.IsNullOrWhiteSpace(_settings.VulnerabilityReports.JsonReportPath))
{
var ops = new JsonSerializerOptions
@@ -201,7 +339,8 @@ private static void ConfigureLogging()
private static void CheckBlockedPackages()
{
- foreach (var pkg in _pkgs)
+ foreach ( var (proj, pkgs) in _projects)
+ foreach (var pkg in pkgs)
{
Log.Logger.Verbose("Checking to see if {packageName}:{version} is Blocked", pkg.Id, pkg.Version);
@@ -214,7 +353,7 @@ private static void CheckBlockedPackages()
continue;
}
- var msBuildMessage = MsBuild.Log(_nuGetFile, MsBuild.Category.Error, pkg.LineNumber, pkg.LinePosition,
+ var msBuildMessage = MsBuild.Log(proj, MsBuild.Category.Error, pkg.LineNumber, pkg.LinePosition,
$"{pkg.Id}: {(string.IsNullOrEmpty(blockedPackage.CustomErrorMessage) ? "has been blocked and may not be used in this project" : blockedPackage.CustomErrorMessage)}");
Console.WriteLine(msBuildMessage);
Log.Logger.Error(msBuildMessage);
diff --git a/Src/NuGetDefense/UtilityMethods.cs b/Src/NuGetDefense/UtilityMethods.cs
index 973550ad..3ec67528 100644
--- a/Src/NuGetDefense/UtilityMethods.cs
+++ b/Src/NuGetDefense/UtilityMethods.cs
@@ -3,9 +3,9 @@
namespace NuGetDefense
{
- public class UtilityMethods
+ public static class UtilityMethods
{
- public static void IgnorePackages(NuGetPackage[] pkgs, NuGetPackage[] ignorePackages, out NuGetPackage[] unIgnoredPackages)
+ public static void IgnorePackages(in NuGetPackage[] pkgs, NuGetPackage[] ignorePackages, out NuGetPackage[] unIgnoredPackages)
{
unIgnoredPackages = pkgs.Where(p => ignorePackages.All(ip => ip.Id != p.Id || !string.IsNullOrWhiteSpace(ip.Version) && !VersionRange.Parse(ip.Version).Satisfies(new NuGetVersion(p.Version)))).ToArray();
}
diff --git a/Src/NuGetDefense/VulnerabilityReport.cs b/Src/NuGetDefense/VulnerabilityReport.cs
index 17cf03c9..8cc81b0d 100644
--- a/Src/NuGetDefense/VulnerabilityReport.cs
+++ b/Src/NuGetDefense/VulnerabilityReport.cs
@@ -12,7 +12,8 @@ public class VulnerabilityReport
public class VulnerableNuGetPackage
{
[XmlAttribute] public string Id { get; set; }
-
+
+ public string PackageUrl => $"pkg:nuget/{Id}@{Version}";
[XmlAttribute] public string Version { get; set; }
public ReportedVulnerability[] Vulnerabilities { get; set; }
diff --git a/Src/NuGetDefense/VulnerabilityReporter.cs b/Src/NuGetDefense/VulnerabilityReporter.cs
index 8df9b8ff..6a6aaf75 100644
--- a/Src/NuGetDefense/VulnerabilityReporter.cs
+++ b/Src/NuGetDefense/VulnerabilityReporter.cs
@@ -1,6 +1,8 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Microsoft.VisualBasic.CompilerServices;
using NuGetDefense.Core;
namespace NuGetDefense
@@ -10,12 +12,11 @@ public class VulnerabilityReporter
private readonly bool _separateMsBuildMessages;
public List MsBuildMessages;
public VulnerabilityReport Report;
- public string VulnerabilityTextReport;
+ public string VulnerabilityTextReport = "";
public VulnerabilityReporter(bool separateMsBuildMessages = true)
{
_separateMsBuildMessages = separateMsBuildMessages;
- if (separateMsBuildMessages) MsBuildMessages = new List();
}
///
@@ -23,51 +24,57 @@ public VulnerabilityReporter(bool separateMsBuildMessages = true)
///
///
/// Parsed Packages
- /// Either the project or packages file
/// If True, suppresses all errors
- /// Threshold CVSS score for error suppresion
public void BuildVulnerabilityReport(
Dictionary> vulnerabilityDictionary,
- IEnumerable pkgs, string nuGetFile, bool warnOnly, double cvss3Threshold)
+ Dictionary projects, bool warnOnly)
{
- Report = new VulnerabilityReport();
- Report.VulnerabilitiesCount = vulnerabilityDictionary.Sum(x => x.Value.Count);
- var nuGetPackages = pkgs as NuGetPackage[] ?? pkgs.ToArray();
- Report.Packages = nuGetPackages.Where(p =>
- p.LineNumber != null && vulnerabilityDictionary.ContainsKey(p.Id.ToLower())).Select(p => new VulnerableNuGetPackage {Id = p.Id, Version = p.Version}).ToArray();
-
- foreach (var pkg in nuGetPackages.Where(p =>
- p.LineNumber != null && vulnerabilityDictionary.ContainsKey(p.Id.ToLower())))
+ Report = new VulnerabilityReport {VulnerabilitiesCount = vulnerabilityDictionary.Sum(x => x.Value.Count)};
+ foreach (var (proj, pkgs) in projects)
{
- var vulnerabilities = vulnerabilityDictionary[pkg.Id.ToLower()];
- Report.Packages.Where(p => p.Id.ToLower() == pkg.Id.ToLower()).First()
- .Vulnerabilities = vulnerabilities.Select(v => new ReportedVulnerability
+ var nuGetPackages = pkgs as NuGetPackage[] ?? pkgs.ToArray();
+ Report.Packages = nuGetPackages.Where(p =>
+ p.LineNumber != null && vulnerabilityDictionary.ContainsKey(p.PackageUrl.ToLower()))
+ .Select(p => new VulnerableNuGetPackage {Id = p.Id, Version = p.Version})
+ .ToArray();
+
+ foreach (var pkg in nuGetPackages.Where(p =>
+ p.LineNumber != null && vulnerabilityDictionary.ContainsKey(p.PackageUrl.ToLower())))
{
- Description = v.Value.Description,
- Cve = v.Value.Cve,
- Cwe = v.Value.Cwe,
- CvssScore = v.Value.CvssScore,
- CvssVector = v.Value.Vector.ToString()
- }).ToArray();
+ var vulnerabilities = vulnerabilityDictionary[pkg.PackageUrl.ToLower()];
+ Report.Packages.First(p => string.Equals(p.Id, pkg.Id, StringComparison.CurrentCultureIgnoreCase) && p.Version == pkg.Version)
+ .Vulnerabilities = vulnerabilities.Select(v => new ReportedVulnerability
+ {
+ Description = v.Value.Description,
+ Cve = v.Value.Cve,
+ Cwe = v.Value.Cwe,
+ CvssScore = v.Value.CvssScore,
+ CvssVector = v.Value.Vector.ToString()
+ }).ToArray();
+ }
}
}
- public void BuildVulnerabilityTextReport(
- Dictionary> vulnerabilityDictionary,
- IEnumerable pkgs, string nuGetFile, bool warnOnly, double cvss3Threshold)
+ public void BuildVulnerabilityTextReport(Dictionary> vulnerabilityDictionary,
+ IEnumerable pkgs, string nuGetFile, bool warnOnly, double cvss3Threshold, out int numberOfVulns)
{
- var logBuilder = new StringBuilder();
+ numberOfVulns = 0;
+ if (_separateMsBuildMessages) MsBuildMessages = new List();
+
+ var logBuilder = new StringBuilder(VulnerabilityTextReport);
var nuGetPackages = pkgs as NuGetPackage[] ?? pkgs.ToArray();
- logBuilder.AppendLine($"{vulnerabilityDictionary.Sum(ve => ve.Value.Count)} vulnerabilities found in {nuGetPackages.Count()} packages.");
- foreach (var pkg in nuGetPackages.Where(p =>
- p.LineNumber != null && vulnerabilityDictionary.ContainsKey(p.Id.ToLower())))
+ logBuilder.AppendLine($"{vulnerabilityDictionary.Sum(ve => ve.Value.Count)} vulnerabilities found in {nuGetPackages.Count()} packages for {nuGetFile}.");
+ foreach (var pkg in nuGetPackages.Where(p => vulnerabilityDictionary.ContainsKey(p.PackageUrl.ToLower())))
{
- var vulnerabilities = vulnerabilityDictionary[pkg.Id.ToLower()];
+ var vulnerabilities = vulnerabilityDictionary[pkg.PackageUrl.ToLower()];
logBuilder.AppendLine("*************************************");
warnOnly = warnOnly ||
!vulnerabilities.Any(v => v.Value.CvssScore >= cvss3Threshold);
+
+ if (!warnOnly) numberOfVulns++;
+ // TODO: Dependencies will need to be listed by package url when this is used.
var dependantVulnerabilities = pkg.Dependencies.Where(dep => vulnerabilityDictionary.ContainsKey(dep));
var vulnTotalMSbuildMessage = MsBuild.Log(nuGetFile, warnOnly ? MsBuild.Category.Warning : MsBuild.Category.Error, pkg.LineNumber, pkg.LinePosition,
@@ -92,6 +99,7 @@ public void BuildVulnerabilityTextReport(
foreach (var cve in vulnerabilities.Keys)
{
warnOnly = warnOnly || vulnerabilities[cve].CvssScore <= cvss3Threshold && vulnerabilities[cve].CvssScore > -1;
+ if (!warnOnly) numberOfVulns++;
var vulnMsBuildMessage = MsBuild.Log(nuGetFile, warnOnly ? MsBuild.Category.Warning : MsBuild.Category.Error, cve, pkg.LineNumber, pkg.LinePosition,
$"{vulnerabilities[cve].Description}");
@@ -120,6 +128,7 @@ public void BuildVulnerabilityTextReport(
{
warnOnly = warnOnly ||
vulnerabilities[cve].CvssScore <= cvss3Threshold;
+ if (!warnOnly) numberOfVulns++;
var vulnMsBuildMessage = MsBuild.Log(nuGetFile, warnOnly ? MsBuild.Category.Warning : MsBuild.Category.Error, cve, pkg.LineNumber, pkg.LinePosition,
$"{dependancy}: {vulnerabilities[cve].Description}");
diff --git a/Src/NuGetDefenseTests/NuGetDefenseTests.csproj b/Src/NuGetDefenseTests/NuGetDefenseTests.csproj
index 632e49e8..2dfff2ed 100644
--- a/Src/NuGetDefenseTests/NuGetDefenseTests.csproj
+++ b/Src/NuGetDefenseTests/NuGetDefenseTests.csproj
@@ -11,13 +11,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Src/NuGetDefenseTests/VulnerabilityReportsTest.cs b/Src/NuGetDefenseTests/VulnerabilityReportsTest.cs
index 178a2634..bfff335d 100644
--- a/Src/NuGetDefenseTests/VulnerabilityReportsTest.cs
+++ b/Src/NuGetDefenseTests/VulnerabilityReportsTest.cs
@@ -35,7 +35,8 @@ public void ReportVulnerabilityWithNullReferences()
var pkgs = new[] {new NuGetPackage {LineNumber = 1, Id = "TestPkg", Version = "1.0.1"}};
var reporter = new VulnerabilityReporter();
- reporter.BuildVulnerabilityTextReport(vulnDict, pkgs, "NuGetDefense.dll", false, 0D);
+ reporter.BuildVulnerabilityTextReport(vulnDict, pkgs, "NuGetDefense.dll", false, 0D, out var vulnNumber);
+ Assert.Equal(0, vulnNumber);
//TODO: Assert MSBuildMessages and VulnerabilityReport
}
@@ -58,7 +59,7 @@ public void IgnoreVulnerabilitiesForPackage()
new NuGetPackage {LineNumber = 4, Id = "TestPkg4", Version = null}
};
- IgnorePackages(pkgs, ignorePkgs, out pkgs);
+ IgnorePackages(in pkgs, ignorePkgs, out pkgs);
Assert.True(pkgs.Length == 1);
Assert.True(pkgs[0].Id == "TestPkg");
Assert.True(pkgs[0].Version == "1.0.1");
diff --git a/build/_build.csproj b/build/_build.csproj
index 9166225c..8e772ab4 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.1
+ net5.0
CS0649;CS0169
..
@@ -17,7 +17,7 @@
-
+