up
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

This commit is contained in:
StellaOps Bot
2025-12-13 18:08:55 +02:00
parent 6e45066e37
commit f1a39c4ce3
234 changed files with 24038 additions and 6910 deletions

View File

@@ -0,0 +1,149 @@
# .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
```json
{
"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`
## Related Sprint
See [SPRINT_0404_0001_0001_scanner_dotnet_detection_gaps.md](../../implplan/SPRINT_0404_0001_0001_scanner_dotnet_detection_gaps.md) for implementation details and decisions.