
OWASP - Secure your dotnet app by scanning for vulnerable nuget dependencies in CI pipelines
Author - Abdul Rahman (Bhai)
OWASP
1 Articles
Table of Contents
What we gonna do?
In this article, let's learn about scanning for Vulnerable Nuget Dependency and break the build to avoid malicious code in dotnet.
Understanding and mitigating vulnerabilities in software dependencies is crucial in modern development. While leveraging open-source libraries enhances productivity, it also introduces potential risks that necessitate robust defenses. Nuget packages are the popular way to consume external libraries in .NET applications. However, these packages can contain vulnerabilities that can be exploited by attackers.
This can be manually checked by developers, but it is not a scalable solution. It is also not feasible to keep track of all the dependencies and their versions. Starting .NET 8, there is a support for auditing packages. But as of writing this, NuGet Audit is available starting from NuGet 6.8, the .NET 8 SDK (8.0.100). I'll also teach you alternate approach if you are not in .NET 8. In this article, we will learn how to scan for vulnerable Nuget dependencies and break the build in CI pipelines.
Why we gonna do?
Software supply chain attacks are increasing every year. When you add a NuGet package to your project, you are trusting that the package is secure and free from vulnerabilities. However, known vulnerabilities are discovered regularly in popular libraries, and attackers actively exploit them.
Without automated scanning:
- Developers may unknowingly ship applications with vulnerable dependencies
- Vulnerabilities can remain undetected for months or years
- Manual audits are error-prone and do not scale with growing dependency trees
- Transitive dependencies (packages your packages depend on) are often overlooked
By integrating vulnerability scanning into your CI pipeline, you ensure that:
- Every build is automatically checked for known vulnerabilities
- Vulnerable code is never deployed to production
- The team is immediately alerted when a dependency becomes vulnerable
- Your application adheres to secure coding standards and compliance requirements
How we gonna do?
Nuget Audit
The restore command automatically runs when you do a common package operation such as loading a project for the first time, adding a new package, updating a package version, or removing a package from your project. A description of your dependencies is checked against a report of known vulnerabilities on the GitHub Advisory Database.
To enable the audit during restore, you need to add the following MSBuild properties in your .csproj file.
- <NuGetAudit>
- <NuGetAuditLevel>
- <NuGetAuditMode>
There are different values available for above properties as shown below.
| MSBuild Property | Default | Available Values | Description |
|---|---|---|---|
| NuGetAudit | true | true and false | If you wish to not receive security audit reports, you can opt-out of the experience entirely by setting the value to false |
| NuGetAuditLevel | low | low, moderate, high and critical | If you'd like to see moderate, high, and critical advisories, set the value to moderate |
| NuGetAuditMode | direct | direct and all | If you'd like to audit both top-level and transitive dependencies, you can set the value to all. NuGetAuditMode is not applicable for packages.config projects |
Here is what all .csproj file looks like with the above properties in I Love DotNet. Note: <NoWarn>NU1900</NoWarn> and <WarningsNotAsErrors>NU1900</WarningsNotAsErrors> suppress communication errors from the vulnerability database (covered in the next section).
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion>
<NoWarn>NU1900</NoWarn>
<WarningsNotAsErrors>NU1900</WarningsNotAsErrors>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>critical</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode>
</PropertyGroup>
Excluding Vulnerabilities
First of all, I will not recommend excluding vulnerabilities. But if you have a valid reason to exclude vulnerabilities, you can do so by adding <NoWarn> to suppress NU1901-NU1904 warnings or use the <NuGetAuditLevel> functionality in your .csproj to ensure your audit reports are useful to your workflow.
| Warning Code | Reason |
|---|---|
| NU1900 | Error communicating with package source, while getting vulnerability information. |
| NU1901 | Package with low severity detected |
| NU1902 | Package with moderate severity detected |
| NU1903 | Package with high severity detected |
| NU1904 | Package with critical severity detected |
Scanning in CI Pipeline
If your projects target .NET 7 or earlier, this section covers the primary approach. If you are on .NET 8+, you can use this approach in addition to NuGet Audit for an explicit CI gate.
You can now list any known vulnerabilities in your dependencies within your projects & solutions with the dotnet list package --vulnerable command. You will see any vulnerabilities within your top-level packages. You will be able to understand the version resolved, the severity of the advisory, and a link to the advisory for you to view. If you are interested in seeing vulnerabilities within your transitive packages, you can use the --include-transitive parameter to see those.
We can add --format json parameter to get the output in json format. The specification of the json can be found here. This can be used to parse the output and take necessary actions. So all we need to do is to run this command in our CI pipeline and fail the build if there are any vulnerabilities.
# Check Vulnerable Nuget Packages
- name: Checking Vulnerable Nuget Packages
run: |
dotnet list package --vulnerable --include-transitive --format=json > list.json
if jq -cre '.projects | .. | .severity? // empty' list.json; then
echo 'Vulnerabilities found! Exiting...'
jq . list.json
exit 1
else
echo 'No vulnerabilities found!'
fi
The above code dumps the JSON output to list.json file and recursively checks for severity key under projects and returns non-zero exit code if there are any severity KV pairs. If there are no severity keys then just print the message and continue the build.

That's it. This should work as expected. But this will fail even if a Low severity vulnerability is found. You can adjust this solution to fail your workflow for some specific severity level e.g. Critical or High by updating jq filter i.e. select(test("Critical|High")). This is how it is done in the ilovedotnet CI pipeline.
# Check Vulnerable Nuget Packages
- name: Checking Vulnerable Nuget Packages
run: |
dotnet list package --vulnerable --include-transitive --format=json > list.json
if jq -cre '.projects | .. | .severity? // empty | select(test("Critical|High"))' list.json; then
echo 'Vulnerabilities found! Exiting...'
jq . list.json
exit 1
else
echo 'No vulnerabilities found!'
fi

Summary
In this article, we have learned about the new tools that NuGet provides to help you scan your NuGet packages for security vulnerabilities. These tools should help you secure your software supply chain and take action today. We also leveraged this tool and added functionality to break the build if any vulnerability is found. This is a great way to ensure that your software is secure and that you are not introducing any new vulnerabilities into your software. If you're interested in the best practices that you can check out the Microsoft documentation on best practices for a secure software supply chain.