diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..8e503cbd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+*/bin/*
+*/obj/*
+*.dll
+
+*.pdb
+
+*.cache
+
+Src/.idea/.idea.NuGetDefense/.idea/
+
+*.blob
+
+Src/.idea/.idea.NuGetDefense/
diff --git a/Src/NuGetDefense.csproj b/Src/NuGetDefense.csproj
new file mode 100644
index 00000000..c47dd8d4
--- /dev/null
+++ b/Src/NuGetDefense.csproj
@@ -0,0 +1,41 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ true
+ 0.0.1
+ NuGetDefense
+ Curtis Carter
+ 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
+ https://github.com/DigitalCoyote/NuGetDefense
+ MIT
+ Initial Release: Support for checking for OSS Vulnerabilities listed on OSS Index
+ Security
+ NuGetDefense
+ NuGetDefense.nuspec
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Src/NuGetDefense.nuspec b/Src/NuGetDefense.nuspec
new file mode 100644
index 00000000..efb4a13d
--- /dev/null
+++ b/Src/NuGetDefense.nuspec
@@ -0,0 +1,28 @@
+
+
+
+ NuGetDefense
+ NuGetDefense
+ 0.0.1
+ Curtis Carter
+ Curtis Carter
+ https://github.com/DigitalCoyote/NuGetDefense
+ false
+ 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.
+ First version: OSS Index used as initial source for vulnerabilities
+
+ MIT
+ Security
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Src/NuGetDefense.sln b/Src/NuGetDefense.sln
new file mode 100644
index 00000000..a9dde097
--- /dev/null
+++ b/Src/NuGetDefense.sln
@@ -0,0 +1,16 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NuGetDefense", "NuGetDefense.csproj", "{B5542178-4516-45C6-B77D-9AF9C1DCC8EA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B5542178-4516-45C6-B77D-9AF9C1DCC8EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5542178-4516-45C6-B77D-9AF9C1DCC8EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5542178-4516-45C6-B77D-9AF9C1DCC8EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5542178-4516-45C6-B77D-9AF9C1DCC8EA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/Src/NuGetPackage.cs b/Src/NuGetPackage.cs
new file mode 100644
index 00000000..37550142
--- /dev/null
+++ b/Src/NuGetPackage.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using System.Xml.Serialization;
+
+namespace NuGetDefense
+{
+ public class NuGetPackage
+ {
+ public string Id { get; set; }
+
+ public string Version { get; set; }
+
+ public string PackageUrl => $@"pkg:nuget/{Id}@{Version}";
+ public int LinePosition { get; set; }
+ public int LineNumber { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Src/OSSIndex/ComponentReport.cs b/Src/OSSIndex/ComponentReport.cs
new file mode 100644
index 00000000..bc6843e3
--- /dev/null
+++ b/Src/OSSIndex/ComponentReport.cs
@@ -0,0 +1,28 @@
+using System.Text.Json.Serialization;
+
+namespace NuGetDefense.OSSIndex
+{
+ public class ComponentReport
+ {
+ ///
+ /// Description of the package
+ ///
+ [JsonPropertyName("description")]
+ public string Description { get; set; }
+
+ ///
+ /// packageUrl for this package
+ ///
+ [JsonPropertyName("coordinates")]
+ public string Coordinates { get; set; }
+
+ ///
+ /// Component Details Reference
+ ///
+ [JsonPropertyName("reference")]
+ public string Reference { get; set; }
+
+ [JsonPropertyName("vulnerabilities")]
+ public ComponentReportVulnerability[] Vulnerabilities { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Src/OSSIndex/ComponentReportRequest.cs b/Src/OSSIndex/ComponentReportRequest.cs
new file mode 100644
index 00000000..beecb4ea
--- /dev/null
+++ b/Src/OSSIndex/ComponentReportRequest.cs
@@ -0,0 +1,7 @@
+namespace NuGetDefense.OSSIndex
+{
+ public class ComponentReportRequest
+ {
+ public string[] coordinates { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Src/OSSIndex/ComponentReportVulnerability.cs b/Src/OSSIndex/ComponentReportVulnerability.cs
new file mode 100644
index 00000000..fff2634d
--- /dev/null
+++ b/Src/OSSIndex/ComponentReportVulnerability.cs
@@ -0,0 +1,66 @@
+using System.Text.Json.Serialization;
+
+namespace NuGetDefense.OSSIndex
+{
+ ///
+ /// Vulnerability report for a specific package
+ ///
+ public class ComponentReportVulnerability
+ {
+ ///
+ /// public identifier
+ ///
+ [JsonPropertyName("id")]
+ public string Id { get; set; }
+
+ ///
+ /// Vulnerability Title
+ ///
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
+
+ ///
+ /// Vulnerability Description
+ ///
+ [JsonPropertyName("description")]
+ public string Description { get; set; }
+
+ ///
+ /// CVSS score (https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System)
+ ///
+ [JsonPropertyName("cvssScore")]
+ public float CvssScore { get; set; }
+
+ ///
+ /// CVSS vector
+ ///
+ [JsonPropertyName("cvssVector")]
+ public string CvssVector { get; set; }
+
+ ///
+ /// Common Weakness Enumeration
+ ///
+ ///
+ [JsonPropertyName("cwe")]
+ public string Cwe { get; set; }
+
+ ///
+ /// Common Vulnerability Enumeration
+ ///
+ [JsonPropertyName("cve")]
+ public string Cve { get; set; }
+
+ ///
+ /// Vulnerability details reference
+ ///
+ [JsonPropertyName("reference")]
+ public string Reference { get; set; }
+
+ ///
+ /// Affected version ranges
+ ///
+ ///
+ [JsonPropertyName("versionRanges")]
+ public string[] VersionRanges { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Src/OSSIndex/RestApi.cs b/Src/OSSIndex/RestApi.cs
new file mode 100644
index 00000000..38206339
--- /dev/null
+++ b/Src/OSSIndex/RestApi.cs
@@ -0,0 +1,74 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.Json;
+
+namespace NuGetDefense.OSSIndex
+{
+
+ ///
+ /// Handles interaction with the OSS Index Rest API (https://ossindex.sonatype.org/doc/rest)
+ ///
+ public static class RestApi
+ {
+ private const string ResponseContentType = "application/vnd.ossindex.component-report.v1+json";
+ private const string RequestContentType = "application/vnd.ossindex.component-report-request.v1+json";
+
+
+ ///
+ /// Gets vulnerabilities for a single NuGet Package.
+ ///
+ /// NuGetPackage to check
+ ///
+ private static async Task GetReportForPackageAsync(NuGetPackage pkg)
+ {
+ using (var client = new HttpClient())
+ {
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ResponseContentType));
+ var response = await client.GetStringAsync($"https://ossindex.sonatype.org/api/v3/component-report/{pkg.PackageUrl}");
+ return JsonSerializer.Deserialize(response, new JsonSerializerOptions());
+ }
+ }
+
+ ///
+ /// Gets Vulnerabilities for a set of NuGet Packages
+ ///
+ /// Packages to Check
+ ///
+ private static async Task GetReportsForPackagesAsync(NuGetPackage[] pkgs)
+ {
+ using var client = new HttpClient();
+ var content = JsonSerializer.Serialize(new ComponentReportRequest(){ coordinates = pkgs.Select(p => p.PackageUrl).ToArray()});
+ client.DefaultRequestHeaders.Accept.Clear();
+ client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(ResponseContentType));
+ var response = await client
+ .PostAsync("https://ossindex.sonatype.org/api/v3/component-report",
+ new StringContent(content, Encoding.UTF8, RequestContentType));
+ return await JsonSerializer.DeserializeAsync(response.Content.ReadAsStreamAsync().Result, new JsonSerializerOptions());
+ }
+
+ ///
+ /// Gets vulnerabilities for a single NuGet Package
+ ///
+ /// NuGetPAckage to check
+ ///
+ public static ComponentReportVulnerability[] GetVulnerabilitiesForPackage(NuGetPackage pkg)
+ {
+ return GetReportForPackageAsync(pkg).Result.Vulnerabilities;
+ }
+
+ ///
+ /// Gets Vulnerabilities for a set of NuGet Packages
+ ///
+ /// Packages to Check
+ ///
+ public static IEnumerable GetVulnerabilitiesForPackages(NuGetPackage[] pkgs)
+ {
+ return GetReportsForPackagesAsync(pkgs).Result.Where(report => report.Vulnerabilities.Length > 0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/Program.cs b/Src/Program.cs
new file mode 100644
index 00000000..49f6adc7
--- /dev/null
+++ b/Src/Program.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Xml;
+using System.Xml.Linq;
+using NuGetDefense.OSSIndex;
+
+namespace NuGetDefense
+{
+ class Program
+ {
+ ///
+ /// args[0] is expected to be the path to the project file.
+ ///
+ ///
+ static void Main(string[] args)
+ {
+ var exitCode = 0;
+ var pkgConfig = Path.Combine(Path.GetDirectoryName(args[0]), "packages.config");
+ var pkgs = LoadPackages(File.Exists(pkgConfig) ? pkgConfig : args[0]);
+
+ var reports = OSSIndex.RestApi.GetVulnerabilitiesForPackages(pkgs).ToArray();
+ foreach (var report in reports)
+ {
+ var StdErrWriter = Console.Error;
+ var pkg = pkgs.First(p => p.PackageUrl == report.Coordinates);
+ Console.WriteLine("*************************************");
+ //Plan to use Warning: for warnings later
+ //Plan to combine messages into a single Console.Write.
+ StdErrWriter.WriteLine($"{args[0]}({pkg.LineNumber},{pkg.LinePosition}) : Error : Vulnerabilities found for {pkg.Id} @ {pkg.Version}");
+ Console.WriteLine($"Description: {report.Description}");
+ Console.WriteLine($"Reference: {report.Reference}");
+ foreach (var vulnerability in report.Vulnerabilities)
+ {
+ exitCode++;
+ Console.WriteLine($"Title: {vulnerability.Title}");
+ Console.WriteLine($"Description: {vulnerability.Description}");
+ Console.WriteLine($"Id: {vulnerability.Id}");
+ Console.WriteLine($"CVE: {vulnerability.Cve}");
+ Console.WriteLine($"CWE: {vulnerability.Cwe}");
+ Console.WriteLine($"CVSS Score: {vulnerability.CvssScore.ToString(CultureInfo.InvariantCulture)}");
+ Console.WriteLine($"CVSS Vector: {vulnerability.CvssVector}");
+ if(vulnerability.VersionRanges?.Length > 0)Console.Error.WriteLine($"Affected Versions: {vulnerability.VersionRanges}");
+ Console.WriteLine("---------------------------");
+ }
+ }
+ }
+
+
+ ///
+ /// Loads NuGet packages in use form packages.config or PackageReferences in the project file
+ ///
+ ///
+ public static NuGetPackage[] LoadPackages(string packageSource)
+ {
+ if(Path.GetFileName(packageSource) == "packages.config")
+ {
+ return XElement.Load(packageSource, LoadOptions.SetLineInfo).DescendantsAndSelf("package").Select(x => new NuGetPackage()
+ {Id = x.Attribute("id").Value, Version = x.Attribute("version").Value, LineNumber = ((IXmlLineInfo)x).LineNumber, LinePosition = ((IXmlLineInfo)x).LinePosition}).ToArray();
+ }
+
+ return XElement.Load(packageSource, LoadOptions.SetLineInfo).DescendantsAndSelf("PackageReference").Select(x => new NuGetPackage()
+ {Id = x.Attribute("Include").Value, Version = x.Attribute("Version").Value, LineNumber = ((IXmlLineInfo)x).LineNumber, LinePosition = ((IXmlLineInfo)x).LinePosition}).ToArray();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/TestFiles/packages.config b/Src/TestFiles/packages.config
new file mode 100644
index 00000000..acd74bf4
--- /dev/null
+++ b/Src/TestFiles/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Src/build/nugetdefense.targets b/Src/build/nugetdefense.targets
new file mode 100644
index 00000000..8f06f4f0
--- /dev/null
+++ b/Src/build/nugetdefense.targets
@@ -0,0 +1,11 @@
+
+
+
+
+ $(MSBuildThisFileDirectory)../tools/netcoreapp3.1/NuGetDefense
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Src/lib/net461/_._ b/Src/lib/net461/_._
new file mode 100644
index 00000000..e69de29b
diff --git a/Src/lib/netcoreapp3.1/_._ b/Src/lib/netcoreapp3.1/_._
new file mode 100644
index 00000000..e69de29b
diff --git a/Src/lib/netstandard2.0/_._ b/Src/lib/netstandard2.0/_._
new file mode 100644
index 00000000..e69de29b