Skip to content

Commit

Permalink
Initial Vulnerability Checks
Browse files Browse the repository at this point in the history
  • Loading branch information
digitalcoyote committed Feb 5, 2020
1 parent 843fdf7 commit b14a0a8
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
*/bin/*
*/obj/*
*.dll

*.pdb

*.cache

Src/.idea/.idea.NuGetDefense/.idea/

*.blob

Src/.idea/.idea.NuGetDefense/
41 changes: 41 additions & 0 deletions Src/NuGetDefense.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>0.0.1</PackageVersion>
<Title>NuGetDefense</Title>
<Authors>Curtis Carter</Authors>
<Description>NuGetDefense ~ Check for Known Vulnerabilities at Build</Description>
<PackageDescription>NuGetDefense was inspired by [OWASP SafeNuGet](https://nuget.org/packages/SafeNuGet/) but aims to check with multiple sources for known vulnerabilities.</PackageDescription>
<Copyright>Curtis Carter 2020</Copyright>
<PackageProjectUrl>https://github.com/DigitalCoyote/NuGetDefense</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageReleaseNotes>Initial Release: Support for checking for OSS Vulnerabilities listed on OSS Index</PackageReleaseNotes>
<PackageTags>Security</PackageTags>
<PackageId>NuGetDefense</PackageId>
<NuspecFile>NuGetDefense.nuspec</NuspecFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="4.7.0" />
</ItemGroup>

<ItemGroup>
<Compile Remove="Tests\**" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Remove="Tests\**" />
</ItemGroup>

<ItemGroup>
<None Remove="Tests\**" />
</ItemGroup>

<ItemGroup>
<None Remove="NuGetDefense.nuspec" />
<Content Include="NuGetDefense.nuspec" />
</ItemGroup>
</Project>
28 changes: 28 additions & 0 deletions Src/NuGetDefense.nuspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<package>
<metadata>
<id>NuGetDefense</id>
<Title>NuGetDefense</Title>
<version>0.0.1</version>
<authors>Curtis Carter</authors>
<owners>Curtis Carter</owners>
<projectUrl>https://github.com/DigitalCoyote/NuGetDefense</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<summary>NuGetDefense ~ Check for Known Vulnerabilities at Build</summary>
<description>NuGetDefense was inspired by [OWASP SafeNuGet](https://nuget.org/packages/SafeNuGet/) but aims to check with multiple sources for known vulnerabilities.</description>
<releaseNotes>First version: OSS Index used as initial source for vulnerabilities</releaseNotes>
<repository type="git" url="https://github.com/digitalcoyote/NuGetDefense.git"/>
<license type="expression">MIT</license>
<tags>Security</tags>
<dependencies>
<dependency id="System.Text.Json" version="4.7.0" exclude="Build,Analyzers" />
</dependencies>
</metadata>

<files>
<!-- <file src="Src/bin/Release/net461/NuGetDefense.exe" target="tools/net461/NuGetDefense.exe" /> -->
<file src="build/" target="build" />
<file src="bin/Release/" target="tools" />
<file src="lib/" target="lib" />
</files>
</package>
16 changes: 16 additions & 0 deletions Src/NuGetDefense.sln
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions Src/NuGetPackage.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
28 changes: 28 additions & 0 deletions Src/OSSIndex/ComponentReport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Text.Json.Serialization;

namespace NuGetDefense.OSSIndex
{
public class ComponentReport
{
/// <summary>
/// Description of the package
/// </summary>
[JsonPropertyName("description")]
public string Description { get; set; }

/// <summary>
/// packageUrl for this package
/// </summary>
[JsonPropertyName("coordinates")]
public string Coordinates { get; set; }

/// <summary>
/// Component Details Reference
/// </summary>
[JsonPropertyName("reference")]
public string Reference { get; set; }

[JsonPropertyName("vulnerabilities")]
public ComponentReportVulnerability[] Vulnerabilities { get; set; }
}
}
7 changes: 7 additions & 0 deletions Src/OSSIndex/ComponentReportRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace NuGetDefense.OSSIndex
{
public class ComponentReportRequest
{
public string[] coordinates { get; set; }
}
}
66 changes: 66 additions & 0 deletions Src/OSSIndex/ComponentReportVulnerability.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.Text.Json.Serialization;

namespace NuGetDefense.OSSIndex
{
/// <summary>
/// Vulnerability report for a specific package
/// </summary>
public class ComponentReportVulnerability
{
/// <summary>
/// public identifier
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; }

/// <summary>
/// Vulnerability Title
/// </summary>
[JsonPropertyName("title")]
public string Title { get; set; }

/// <summary>
/// Vulnerability Description
/// </summary>
[JsonPropertyName("description")]
public string Description { get; set; }

/// <summary>
/// CVSS score (https://en.wikipedia.org/wiki/Common_Vulnerability_Scoring_System)
/// </summary>
[JsonPropertyName("cvssScore")]
public float CvssScore { get; set; }

/// <summary>
/// CVSS vector
/// </summary>
[JsonPropertyName("cvssVector")]
public string CvssVector { get; set; }

/// <summary>
/// Common Weakness Enumeration
/// </summary>
/// <returns></returns>
[JsonPropertyName("cwe")]
public string Cwe { get; set; }

/// <summary>
/// Common Vulnerability Enumeration
/// </summary>
[JsonPropertyName("cve")]
public string Cve { get; set; }

/// <summary>
/// Vulnerability details reference
/// </summary>
[JsonPropertyName("reference")]
public string Reference { get; set; }

/// <summary>
/// Affected version ranges
/// </summary>
/// <returns></returns>
[JsonPropertyName("versionRanges")]
public string[] VersionRanges { get; set; }
}
}
74 changes: 74 additions & 0 deletions Src/OSSIndex/RestApi.cs
Original file line number Diff line number Diff line change
@@ -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
{

/// <summary>
/// Handles interaction with the OSS Index Rest API (https://ossindex.sonatype.org/doc/rest)
/// </summary>
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";


/// <summary>
/// Gets vulnerabilities for a single NuGet Package.
/// </summary>
/// <param name="pkg">NuGetPackage to check</param>
/// <returns></returns>
private static async Task<ComponentReport> 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<ComponentReport>(response, new JsonSerializerOptions());
}
}

/// <summary>
/// Gets Vulnerabilities for a set of NuGet Packages
/// </summary>
/// <param name="pkgs"> Packages to Check</param>
/// <returns></returns>
private static async Task<ComponentReport[]> 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<ComponentReport[]>(response.Content.ReadAsStreamAsync().Result, new JsonSerializerOptions());
}

/// <summary>
/// Gets vulnerabilities for a single NuGet Package
/// </summary>
/// <param name="pkg">NuGetPAckage to check</param>
/// <returns></returns>
public static ComponentReportVulnerability[] GetVulnerabilitiesForPackage(NuGetPackage pkg)
{
return GetReportForPackageAsync(pkg).Result.Vulnerabilities;
}

/// <summary>
/// Gets Vulnerabilities for a set of NuGet Packages
/// </summary>
/// <param name="pkgs"> Packages to Check</param>
/// <returns></returns>
public static IEnumerable<ComponentReport> GetVulnerabilitiesForPackages(NuGetPackage[] pkgs)
{
return GetReportsForPackagesAsync(pkgs).Result.Where(report => report.Vulnerabilities.Length > 0);
}
}
}
69 changes: 69 additions & 0 deletions Src/Program.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// args[0] is expected to be the path to the project file.
/// </summary>
/// <param name="args"></param>
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("---------------------------");
}
}
}


/// <summary>
/// Loads NuGet packages in use form packages.config or PackageReferences in the project file
/// </summary>
/// <returns></returns>
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();
}
}
}
7 changes: 7 additions & 0 deletions Src/TestFiles/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="jQuery" version="1.5.1" />
<package id="EntityFramework.SqlServerCompact" version="4.1.8482.2" />
<package id="AntiXss" version="4.0.1" />
<package id="Microsoft.Data.OData" version="5.7.2" />
</packages>
11 changes: 11 additions & 0 deletions Src/build/nugetdefense.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project>
<PropertyGroup>
<!-- <NuGetDefenseExe Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">"$(MSBuildThisFileDirectory)/net46/NuGetDefense.exe"</TsGenFileExe> -->
<!-- <NuGetDefenseExe Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' OR '$(TargetFrameworkIdentifier)' == '.NETStandard'">dotnet "$(MSBuildThisFileDirectory)/netcoreapp3.1/NuGetDefense.dll"</TsGenFileExe> -->
<NuGetDefenseExe>$(MSBuildThisFileDirectory)../tools/netcoreapp3.1/NuGetDefense</NuGetDefenseExe>
</PropertyGroup>

<Target Name="CheckForVulnerableNuGetPkgs" AfterTargets="Build">
<Exec Command="$(NuGetDefenseExe) $(MSBuildProjectFullPath)" IgnoreExitCode="true" />
</Target>
</Project>
Empty file added Src/lib/net461/_._
Empty file.
Empty file added Src/lib/netcoreapp3.1/_._
Empty file.
Empty file added Src/lib/netstandard2.0/_._
Empty file.

0 comments on commit b14a0a8

Please sign in to comment.