Skip to content

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

@solrevdev

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions