Skip to content

feat(ecosystem): add NuGet (.NET) package scanning support #23

Description

@solrevdev

Summary

docs/inventory-sources.md currently lists NuGet (packages.lock.json) under "Not currently covered". This issue requests read-only NuGet package inventory support so .NET developer endpoints can be included in the same supply-chain exposure checks as npm, PyPI, Go, RubyGems, and Composer.

Motivation

NuGet is the primary package manager for .NET. Without NuGet coverage, Bumblebee cannot answer whether a .NET-heavy developer machine has exposure to a known compromised or vulnerable package version.

Proposed sources

Bumblebee should not execute dotnet, nuget, MSBuild, or any package-manager command. It can get useful coverage by reading existing metadata files:

Source Why it matters
project.assets.json under obj/ Broadest PackageReference coverage; written by restore and contains resolved package libraries such as PackageName/1.0.0. Skip libraries entries where type != "package".
packages.lock.json Highest-confidence lock file when present; opt-in via RestorePackagesWithLockFile. Dependencies are grouped per TFM/RID and include package type and resolved version. Treat NuGet package Direct/Transitive entries as direct/transitive; skip project-reference entries.
packages.config Legacy XML format; contains <package id="..." version="..." />.
~/.nuget/packages/<id>/<version>/<id>.nuspec User global package cache; useful for the baseline profile. Parse <id> and <version> from <metadata>.

Suggested priority: project.assets.json for project-profile coverage, packages.lock.json when available for lock-file confidence, .nuspec cache entries for baseline inventory, and packages.config for legacy projects.

Expected records

Use Bumblebee's existing package record shape:

  • ecosystem: nuget.
    • Note: OSV uses NuGet as the ecosystem spelling; lowercase nuget matches Bumblebee's current local style (npm, pypi, rubygems, etc.).
  • package_name: NuGet package id, preserving observed casing.
  • normalized_name: lowercase package id, because NuGet package ids are case-insensitive.
  • version: resolved version string.
  • install_scope: direct / transitive where known from packages.lock.json.
  • source_type: for example nuget-assets, nuget-lockfile, nuget-packages-config, nuget-cache.

Current code touchpoints

Based on the current main branch:

  • internal/model/model.go: add EcosystemNuGet = "nuget" and register it in supportedEcosystems / supportedEcosystemOrder.
  • internal/scanner/scanner.go: add NuGet scanner dispatch for project.assets.json, packages.lock.json, packages.config, and guarded .nuspec files.
  • cmd/bumblebee/roots.go: add filepath.Join(home, ".nuget", "packages") to baselineHomeCandidates() with model.RootKindUserPackage.
  • internal/walk/walk.go: no walker exclude change appears necessary; obj/ and .nuget are not currently excluded.

A .nuspec path-shape guard similar to rubygems.IsInstalledGemspec would avoid treating arbitrary vendored .nuspec files as installed global-cache packages. The expected cache shape is <global-packages-root>/<package-id-lowercase>/<version>/<package-id>.nuspec.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions