Checking for vulnerabilities in transitive NuGet dependencies with OWASP Dependency-Check

Dependency-Check is a tool for checking for publically disclosed vulnerabilities in the libraries you are using. Free, backed by OWASP, integrated with SonarQube and plugins available for pretty much all pipeline platforms, Dependency-Check is a great choice for checking your NuGet package references.

But have you ever wondered if your dependency scanning is checking for vulnerabilities beyond your immediate package references? In the .Net world many tools don't!

Let's take a look at this problem using Dependency-Check and find a workaround for checking out those transitive dependencies.

Following along

If you want to follow along, this is the sample application I am checking. I am using this docker image for Dependency-Check, but you could use the Dependency-Check executable or run it as a task in your preferred pipeline platform.
I am running on Windows and will be using Powershell, so be prepared to adapt if your environment differs from mine.
Also, I will be referencing some folders I have set up on my local system, so just be aware that you would have to create those yourself.

Initial Run

First, I'm going to just target one .csproj file with some package references

docker run `
--volume  C:\repos\eShopOnContainers/src:/src `
--volume C:\temp\data:/usr/share/dependency-check/data `
--volume  C:\repos\eShopOnContainers/odc-reports:/report `
owasp/dependency-check:latest `
--scan /src/Services/Basket/Basket.API/Basket.API.csproj `
--out /report

Let's just quickly break down the arguments in this docker run command:

--volume C:\repos\eShopOnContainers/src:/src Map my local source code to /src on the container
--volume C:\temp\data:/usr/share/dependency-check/data Give a location for vulnerability database to be saved locally so does not need to be pulled in each run
--volume C:\repos\eShopOnContainers/odc-reports:/report Map location where report will be saved on my local system
--scan /src/Services/Basket/Basket.API/Basket.API.csproj Give Dependency-Check the instruction on what to check
--out /report Tell Dependency-Check where to save the report

Looking at the report, I see it found some dependencies, but not vulnerabilities. Excellent, a clean bill of health. Or are we are missing something?



Using the Assembly scanner

Alright, so after building the Basket.API project I was looking at, let's run a scan on everything to see what it finds if it also looks at the DLLs.

docker run `
--volume  C:\repos\eShopOnContainers/src:/src `
--volume C:\temp\data:/usr/share/dependency-check/data `
--volume  C:\repos\eShopOnContainers/odc-reports:/report `
owasp/dependency-check:latest `
--scan /src/Services/Basket/Basket.API/ `
--out /report

We have a hit!

Hmm, lets look a little closer here. ASP Project Management 1.0 vulnerability from 2009. I think we have a false positive.

The Workaround

If we want to check the packages our immediate NuGet references brings in, but we want to reduce false positives caused by assembly scanning, what do we do?

We need to get Dependency-Check a complete list of all the NuGet packages. I'm sure there are many workarounds and methods that could be done here, so I'm just going to show you one of them.

NuGet has support for a lock file but I'm going to work with what we already have. After building I have a C:\repos\eShopOnContainers\src\Services\Basket\Basket.API\obj\project.assets.json

Inside we have an overwhelming amount of information about the libraries being used, but most importantly we havethe name and version number.

Dependency-Check does support packages.config and .nuspec, but today we are going to convert to a .csproj. Well, kinda. It doesn't require an actual valid .csproj file. It just needs a valid xml document with PackageReference elements with the appropriate attributes. I'm going to go ahead and do some quick powershell to make a pseudo .csproj out of our projects.assets.json.

$assetsFile = Get-Item 'C:\repos\eShopOnContainers\src\Services\Basket\Basket.API\obj\project.assets.json'

# Converting the json assets file to an object we can work with easily in powershell
$assets = Get-Content $assetsFile | ConvertFrom-Json
# Choosing the correct property, swapping it from values like System.Linq/4.3.0 to System.Linq=4.3.0 and then converting that to a hash table
$dependencies = $ `
            -replace '/','=' | Out-String | ConvertFrom-StringData

# Making a root element for our new xml document
[xml]$xml = '<root></root>'

# Time to loop through the dependencies and add them to the xml so they're in a format like <PackageReference Include="System.Linq" Version="4.3.0" />
$dependencies.GetEnumerator() | ForEach-Object {

    $el = $xml.CreateElement("PackageReference")
    $el.SetAttribute("Include", $_.key)
    $el.SetAttribute("Version", $_.value)


Now time to run Dependency-Check one more time.

docker run `
--volume  C:\repos\eShopOnContainers/src:/src `
--volume C:\temp\data:/usr/share/dependency-check/data `
--volume  C:\repos\eShopOnContainers/odc-reports:/report `
owasp/dependency-check:latest `
--scan /src/Services/Basket/Basket.API/obj/dependency-check.csproj `
--out /report

And one last look at the report. Seems like we caught some actual vulnerabilities in the transitive dependencies that we were not aware of.

Final considerations

Of course when you add your workaround to your build pipeline you are going to want to loop over all the project's project.assets.json. My recommendation is to always save the pseudo .csproj within the same project so it can be easy to see within the OWASP Dependency-Check report which project the vulnerabilities came from.



Popular posts from this blog

.Net static code analysis in Azure DevOps with Security Code Scan

Implementing Log Monitor for ASP.NET Windows Containers