Introducing NuGet Support for deps.dev

Josie Anugerah, Open Source Insights Team

Today we’re launching support for NuGet, the .NET package manager. We have 350k NuGet packages and 5.6 million package versions available through our API, website and BigQuery dataset.

Deps.dev already supports npm, Go, Maven, PyPI, Cargo and we’re excited to add support for NuGet as another major open source ecosystem. As software supply chain attacks continue to increase in number and in complexity, it’s becoming more important than ever to understand the software that we depend on. We hope that more developers will be able to gain insight into their dependencies through our NuGet support.

Introducing Requirements

NuGet is our first supported ecosystem to feature dependency requirement data instead of dependency graphs.

Requirements are the link between a package and its dependency graph. In NuGet, requirements are specified by including a dependencies tag in the .nuspec or a package reference tag in the .csproj file.


<dependencies>
    <!-- Require a version of Castle.Core between 4.0.0 and 5.1.0,
      including 4.0.0 but excluding 5.1.0. -->
    <dependency id="Castle.Core" version="[4.0.0, 5.1.0)" />
    <!-- Require a version of Serilog that's >=2.12.0, preferring lower versions. -->
    <dependency id="Serilog" version="2.12.0" />
</dependencies>

Example of a dependencies tag in a .nuspec file. Note that 'dependency' in NuGet means 'requirement'. In deps.dev we make a distinction between a dependency (a resolved requirement) and a requirement (a package constraint).

These requirements are read by the NuGet resolver and resolved into a dependency graph. For example, the requirements above would resolve into the following graph.

The resolved dependency graph for the .nuspec snippet above. My.Package/1.0.0 depends on Castle.Core/5.0.0 and Serilog/2.12.0, and transitively depends on four more System packages.
The resolved dependency graph for the .nuspec snippet above. My.Package/1.0.0 depends on Castle.Core/5.0.0 and Serilog/2.12.0, and transitively depends on four more System packages.

In the case of NuGet, it’s possible to specify requirements for specific target frameworks.


<dependencies>
   <group targetFramework=".NETFramework4.6.2"/>
   <group targetFramework=".NETStandard1.5"">
       <dependency id="Moq" version="[4.7.0, 4.11.0)"/>
       <dependency id="NETStandard.Library" version="1.6.1"/>
   </group>
   <group targetFramework=".NETStandard2.0">
     <dependency id="Moq" version="[4.7.0, 5.0.0)"/>
     <dependency id="Castle.Core" version="5.1.1"/>
    </group>
    …
</dependencies>

An example .nuspec snippet showing multiple dependency groups for different target frameworks.

Requirements are usually interpreted within the rules of semantic versioning (semver), but the semver standard only covers version numbers. It doesn’t go into how requirements should be specified. So for each ecosystem there are different requirement operators and rules for requirement interpretation. For NuGet, there is official documentation on requirement rules and resolution. You can find a brief comparison of requirement specification rules across ecosystems in our glossary.

Since requirements determine the allowable dependency graphs, some interesting analysis can be done on how and why dependency graphs change. For example, it might be interesting to know whether a package would automatically pick a recently released version of a package for its dependency graph. Requirements can be used to determine this. Knowing whether a package would automatically pick up a new version of an existing dependency is particularly useful when we think about remediating vulnerabilities introduced by transitive dependencies.

Where does the data come from?

The main source of data is the .nuspec file of the package itself, which is available from the NuGet PackageContent API. We also use the NuGet Search API and Catalog API for version metadata fields not available in the .nuspec file.

We’d like to thank the NuGet team for helping us develop support for NuGet on deps.dev. Each time we develop support for a new ecosystem we discover interesting differences across ecosystems that require us to expand our backend data model and infrastructure. It was great having the official maintainers lend a hand in helping us understand the NuGet ecosystem.

What are some ways in which I can use the data?

NuGet data is available in our API, website and BigQuery dataset.

The new requirements tab available for NuGet.
The new requirements tab available for NuGet.

The data that’s available:

  • Requirements: The requirements tab has been added to the UI, the GetRequirements endpoint has been added to the API and the NuGetRequirements table has been added to the BigQuery dataset.
  • Hashes: You can use the Query API endpoint to query for the name of a mystery NuGet package by using its hash.
  • Advisories: You can check whether a NuGet package version is directly affected by an advisory, and if so, whether there is an unaffected version you could use.
  • Version metadata: Finally, there’s metadata like licenses, publish dates, descriptions, owners and reference links.

What’s next?

We’re hoping to improve our license support for NuGet, as NuGet allows multiple ways to specify a package’s license.

We’re also planning on adding requirement information for other ecosystems.

We’d love to hear what you think about our NuGet support. As mentioned earlier, NuGet is our first ecosystem to feature requirements, so we’re interested in hearing about your experiences (both good and bad) working with this new kind of data. Get in touch via email at depsdev@google.com or file an issue to our GitHub repository.