Dotnet Vulnerable Packages Integration with DefectDojo
Dotnet Vulnerable Packages Integration with DefectDojo
Dotnet Vulnerable Packages refers to the report produced by dotnet list package --vulnerable, a command built into the Microsoft .NET SDK. After a project is restored, the command checks its NuGet packages against known advisories from the GitHub Advisory Database and lists each vulnerable package with its resolved version, the advisory severity, and a link to the advisory. With --include-transitive it also covers packages pulled in indirectly, and with --format json it writes a machine-readable report that DefectDojo imports.
Dotnet Vulnerable Packages Integration with DefectDojo
We already have the .NET SDK on every build agent, so dotnet list package --vulnerable is the cheapest software composition check we can run on our C# services. It needs no extra tool, license, or account. Importing its JSON into DefectDojo turns each vulnerable package into a Finding on the right Asset, keyed on the package, version, and GHSA ID so rebuilds do not duplicate it. The finding also says whether the package is a direct reference or a transitive one, which changes what the developer actually has to do.
Why Dotnet Vulnerable Packages Matters
NuGet dependencies make up most of the code in a typical .NET service, and much of it arrives transitively.
- The check ships with the SDK, so it works anywhere a project can be restored, including restricted build networks with access to your package feed.
- With
--include-transitive, it reports vulnerable packages your project never references directly, which is where many advisories turn up. - Advisory severity (Critical, High, Moderate, Low) comes from the NuGet advisory data, not from a separate scoring scheme.
- The console output is fine for a developer at a terminal but gives a security team no history, ownership, or SLA tracking.
Advantages of This Integration
What changes when the report goes through DefectDojo:
- Package-level deduplication. DefectDojo hashes these findings on component name, component version, and Vuln ID from Tool (the GHSA ID), so each vulnerable package version is tracked once per advisory across rebuilds.
- Direct versus transitive, spelled out. The description records whether a package is direct or transitive, and the mitigation is different for each. A transitive finding tells the developer to upgrade the dependency that pulls it in, or to add an explicit pinned reference.
- One finding per package, not per framework. A multi-targeted project that lists the same package under several target frameworks produces a single finding.
- GHSA identifiers. The report has no advisory ID field, so the parser extracts the GHSA ID from the advisory URL and stores it as a vulnerability ID for filtering.
- Lifecycle on upgrade. Reimporting after a package upgrade mitigates findings for versions no longer resolved.
- SLAs and ownership. Findings fall under severity-based SLAs, can be assigned, pushed to Jira, or risk-accepted when no fixed version exists.
How This Integration Works
DefectDojo imports this report with the Dotnet Vulnerable Packages Scan scan type, which expects the JSON written by the .NET SDK.
1. Restore and generate the report. From the project or solution directory:
dotnet restore
dotnet list package --vulnerable --include-transitive --format json > dotnet_vulnerable.json
Always pass --include-transitive. Only the transitive list names vulnerable packages that arrive through other dependencies. The DefectDojo samples were generated with .NET SDK 8.0.423. A project with nothing vulnerable omits the frameworks key entirely, and the parser handles that as a clean result.
2. Import the file. In the UI, open the Engagement, choose Import Scan Results, select Dotnet Vulnerable Packages Scan, and upload the JSON. With the API, available in Community Edition and DefectDojo Pro:
curl "https://YOUR_INSTANCE/api/v2/import-scan/"
-H "Authorization: Token $DD_API_TOKEN"
-F "scan_type=Dotnet Vulnerable Packages Scan"
-F "file=@dotnet_vulnerable.json"
-F "product_name=claims-api"
-F "engagement_name=Dependency Checks"
-F "auto_create_context=true"
DefectDojo Pro users can run the same import with Universal Importer:
universal-importer import
--defectdojo-url "https://YOUR_INSTANCE.cloud.defectdojo.com/"
--scan-type "Dotnet Vulnerable Packages Scan"
--report-path "./dotnet_vulnerable.json"
--product-name "claims-api"
--engagement-name "Dependency Checks"
--auto-create-context
3. Reimport on every build. Send later reports to /api/v2/reimport-scan/ against the same Test so upgraded packages are mitigated.
Data Granularity: What Gets Imported
| DefectDojo Field | Source in dotnet JSON | Notes |
|---|---|---|
| Title | Package id, resolvedVersion, severity |
For example "Package 1.2.3 has a known high-severity vulnerability" |
| Severity | Advisory severity |
Critical, High, Low map directly; Moderate becomes Medium; unrecognized values become Medium |
| Description | Package, resolved and requested versions, dependency type, project, target framework | Requested version appears for direct references only |
| Mitigation | Direct or transitive | Upgrade the reference, or upgrade the parent dependency or pin the package explicitly |
| Component Name | Package id |
NuGet package ID |
| Component Version | resolvedVersion |
The version actually in the build |
| Vuln ID from Tool | GHSA ID from advisoryurl |
Only identifier available; the report has no CVE field |
| Vulnerability IDs | GHSA ID | Set when the URL contains one |
| References | advisoryurl |
Link to the advisory |
| Finding type | Static | Reads the restored dependency graph |
| Deduplication | Hashcode | Component name, component version, Vuln ID from Tool |
The parser walks both topLevelPackages and transitivePackages for every project and framework in the report.
Use Cases
In a CI/CD pipeline: Every build of a .NET service restores, runs the vulnerable package check, and reimports the JSON. New advisories show up as new findings, and a dependency bump closes old ones without manual cleanup.
For solution-wide visibility: A team with a large solution imports one report covering all projects. Each vulnerable package version appears once per advisory, which gives a clear upgrade list rather than a long console printout.
When a transitive advisory lands: A High advisory hits a package nobody references directly. The finding says it is transitive, so the developer knows to update the parent package or add a pinned reference instead of searching the project file for something that is not there.
For lightweight SCA coverage: Teams not ready for a commercial SCA tool still get dependency findings into DefectDojo next to SAST and DAST results, with SLAs and reporting, using only the SDK they already have.
Operational Tips
- Run
dotnet restorefirst. The command reads the restored dependency graph, so an unrestored project reports nothing useful. - Expect no CVE IDs. Search and report on GHSA IDs instead, and follow the advisory link for CVE aliases.
- Within a single report, the same package version and advisory found in several projects becomes one finding, and the description names the first project it was seen in. Import per project if you need findings split by project.
- Reimport into a stable Test per repository so upgrades mitigate findings automatically.
- Risk-accept advisories with no fixed version yet, with an expiration date, so they are reviewed again.
- Tag imports with the repository or solution name (for example
tags=claims-api,nuget) to filter dependency findings in dashboards.