Files
git.stella-ops.org/docs/modules/scanner/dotnet-analyzer.md
StellaOps Bot f1a39c4ce3
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
up
2025-12-13 18:08:55 +02:00

6.5 KiB

.NET Analyzer

The .NET analyzer detects NuGet package dependencies in .NET applications by analyzing multiple dependency sources with defined precedence rules.

Detection Sources and Precedence

The analyzer uses the following sources in order of precedence (highest to lowest fidelity):

Priority Source Description
1 packages.lock.json Locked resolved versions; highest trust for version accuracy
2 *.deps.json Installed/published packages; authoritative for "what shipped"
3 SDK-style project files *.csproj/*.fsproj/*.vbproj + Directory.Packages.props (CPM) + Directory.Build.props
4 packages.config Legacy format; lowest precedence

Operating Modes

Installed Mode (deps.json present)

When *.deps.json files exist, the analyzer operates in installed mode:

  • Installed packages are emitted with pkg:nuget/<id>@<ver> PURLs
  • Declared packages not matching any installed package are emitted with declaredOnly=true and installed.missing=true
  • Installed packages without corresponding declared records are tagged with declared.missing=true

Declared-Only Mode (no deps.json)

When no *.deps.json files exist, the analyzer falls back to declared-only mode:

  • Dependencies are collected from declared sources in precedence order
  • All packages are emitted with declaredOnly=true
  • Resolved versions use pkg:nuget/<id>@<ver> PURLs
  • Unresolved versions use explicit keys (see below)

Declared-Only Components

Components emitted from declared sources include these metadata fields:

Field Description
declaredOnly Always "true" for declared-only components
declared.source Source file type (e.g., csproj, packages.lock.json, packages.config)
declared.locator Relative path to source file
declared.versionSource How version was determined: direct, centralpkg, lockfile, property, unresolved
declared.tfm[N] Target framework(s)
declared.isDevelopmentDependency "true" if marked as development dependency
provenance "declared" for declared-only components

Unresolved Version Identity

When a version cannot be resolved (e.g., CPM enabled but missing version, unresolved property placeholder), the component uses an explicit key format:

declared:nuget/<normalized-id>/<version-source-hash>

Where version-source-hash = first 8 characters of SHA-256(<source>|<locators>|<raw-version-string>)

Additional metadata for unresolved versions:

Field Description
declared.versionResolved "false"
declared.unresolvedReason One of: cpm-missing, property-unresolved, version-omitted
declared.rawVersion Original unresolved string (e.g., $(SerilogVersion))

This explicit key format prevents collisions with real pkg:nuget/<id>@<ver> PURLs.

Bundling Detection

The analyzer detects bundled executables (single-file apps, ILMerge/ILRepack assemblies) using bounded candidate selection:

Candidate Selection Rules

  • Only scan files in the same directory as *.deps.json or *.runtimeconfig.json
  • Only scan files with executable extensions: .exe, .dll, or no extension
  • Only scan files named matching the app name (e.g., if MyApp.deps.json exists, check MyApp, MyApp.exe, MyApp.dll)
  • Skip files > 500 MB (emit bundle.skipped=true with bundle.skipReason=size-exceeded)

Bundling Metadata

When bundling is detected, metadata is attached to entrypoint components (or synthetic bundle markers):

Field Description
bundle.detected "true"
bundle.filePath Relative path to bundled executable
bundle.kind singlefile, ilmerge, ilrepack, costurafody, unknown
bundle.sizeBytes File size in bytes
bundle.estimatedAssemblies Estimated number of bundled assemblies
bundle.indicator[N] Detection indicators (top 5)
bundle.skipped "true" if file was skipped
bundle.skipReason Reason for skipping (e.g., size-exceeded)

Dependency Edges

When emitDependencyEdges=true is set in the analyzer configuration (dotnet-il.config.json), the analyzer emits dependency edge metadata for both installed and declared packages.

Edge Metadata Format

Each edge is emitted with the following metadata fields:

Field Description
edge[N].target Normalized package ID of the dependency
edge[N].reason Relationship type (e.g., declared-dependency)
edge[N].confidence Confidence level (high, medium, low)
edge[N].source Source of the edge information (deps.json, packages.lock.json)

Edge Sources

  • deps.json: Dependencies from the runtime dependencies section
  • packages.lock.json: Dependencies from the lock file's per-package dependencies

Example Configuration

{
  "emitDependencyEdges": true
}

Central Package Management (CPM)

The analyzer supports .NET CPM via Directory.Packages.props:

  1. When ManagePackageVersionsCentrally=true in the project or props file
  2. Package versions are resolved from <PackageVersion> items in Directory.Packages.props
  3. If a package version cannot be found in CPM, it's marked as unresolved with declared.unresolvedReason=cpm-missing

Known Limitations

  1. No full MSBuild evaluation: The analyzer uses lightweight XML parsing, not MSBuild evaluation. Complex conditions and imports may not be fully resolved.

  2. No restore/feed access: The analyzer does not perform NuGet restore or access package feeds. Only locally available information is used.

  3. Property resolution: Property placeholders ($(PropertyName)) are resolved using Directory.Build.props and project properties, but transitive or complex property evaluation is not supported.

  4. Bundled content: Bundling detection identifies likely bundles but cannot extract embedded dependency information.

Files Created/Modified

  • src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/DotNetLanguageAnalyzer.cs
  • src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/DotNetDeclaredDependencyCollector.cs
  • src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/Internal/Bundling/DotNetBundlingSignalCollector.cs

See SPRINT_0404_0001_0001_scanner_dotnet_detection_gaps.md for implementation details and decisions.