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
150 lines
6.5 KiB
Markdown
150 lines
6.5 KiB
Markdown
# .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.
|