feat: Enhance SBOM composition with policy findings and update CycloneDX package
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added `PolicyFindings` property to `SbomCompositionRequest` to include policy findings in SBOM. - Implemented `NormalizePolicyFindings` method to process and validate policy findings. - Updated `SbomCompositionRequest.Create` method to accept policy findings as an argument. - Upgraded CycloneDX.Core package from version 5.1.0 to 10.0.1. - Marked several tasks as DONE in TASKS.md, reflecting completion of SBOM-related features. - Introduced telemetry metrics for Go analyzer to track heuristic fallbacks. - Added performance benchmarks for .NET and Go analyzers. - Created new test fixtures for .NET applications, including dependencies and runtime configurations. - Added licenses and nuspec files for logging and toolkit packages used in tests. - Implemented `SbomPolicyFinding` record to encapsulate policy finding details and normalization logic.
This commit is contained in:
12
EXECPLAN.md
12
EXECPLAN.md
@@ -124,11 +124,11 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
|
||||
- Team Excititor Connectors – Stella: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Excititor.Connectors.StellaOpsMirror/TASKS.md`. Focus on EXCITITOR-CONN-STELLA-07-003 (TODO). Confirm prerequisites (internal: EXCITITOR-CONN-STELLA-07-002 (Wave 4)) before starting and report status in module TASKS.md.
|
||||
- Team Notify Connectors Guild: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-502 (DONE), NOTIFY-CONN-TEAMS-15-602 (DONE), NOTIFY-CONN-EMAIL-15-702 (BLOCKED 2025-10-20), NOTIFY-CONN-WEBHOOK-15-802 (BLOCKED 2025-10-20). Confirm prerequisites (internal: NOTIFY-CONN-EMAIL-15-701 (Wave 4), NOTIFY-CONN-SLACK-15-501 (Wave 4), NOTIFY-CONN-TEAMS-15-601 (Wave 4), NOTIFY-CONN-WEBHOOK-15-801 (Wave 4)) before starting and report status in module TASKS.md.
|
||||
- Team Scanner WebService Guild: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Scanner.WebService/TASKS.md`. Focus on SCANNER-RUNTIME-17-401 (TODO). Confirm prerequisites (internal: POLICY-RUNTIME-17-201 (Wave 4), SCANNER-EMIT-17-701 (Wave 1), SCANNER-RUNTIME-12-301 (Wave 1), ZASTAVA-OBS-17-005 (Wave 3)) before starting and report status in module TASKS.md.
|
||||
- Team TBD: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-308D (TODO), SCANNER-ANALYZERS-LANG-10-308G (TODO), SCANNER-ANALYZERS-LANG-10-308P (TODO), SCANNER-ANALYZERS-LANG-10-308R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-307D (Wave 4), SCANNER-ANALYZERS-LANG-10-307G (Wave 4), SCANNER-ANALYZERS-LANG-10-307P (Wave 4), SCANNER-ANALYZERS-LANG-10-307R (Wave 4)) before starting and report status in module TASKS.md.
|
||||
- Team TBD: read EXECPLAN.md Wave 5 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-308D (DONE 2025-10-23), SCANNER-ANALYZERS-LANG-10-308G (TODO), SCANNER-ANALYZERS-LANG-10-308P (TODO), SCANNER-ANALYZERS-LANG-10-308R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-307D (Wave 4), SCANNER-ANALYZERS-LANG-10-307G (Wave 4), SCANNER-ANALYZERS-LANG-10-307P (Wave 4), SCANNER-ANALYZERS-LANG-10-307R (Wave 4)) before starting and report status in module TASKS.md.
|
||||
|
||||
### Wave 6
|
||||
- Team Notify Connectors Guild: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Notify.Connectors.Email/TASKS.md`, `src/StellaOps.Notify.Connectors.Slack/TASKS.md`, `src/StellaOps.Notify.Connectors.Teams/TASKS.md`, `src/StellaOps.Notify.Connectors.Webhook/TASKS.md`. Focus on NOTIFY-CONN-SLACK-15-503 (DONE), NOTIFY-CONN-TEAMS-15-603 (DONE), NOTIFY-CONN-EMAIL-15-703 (DONE), NOTIFY-CONN-WEBHOOK-15-803 (DONE). Confirm packaging outputs remain deterministic while upstream implementation tasks (15-702/802) stay blocked.
|
||||
- Team TBD: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-309D (TODO), SCANNER-ANALYZERS-LANG-10-309G (TODO), SCANNER-ANALYZERS-LANG-10-309P (TODO), SCANNER-ANALYZERS-LANG-10-309R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-308D (Wave 5), SCANNER-ANALYZERS-LANG-10-308G (Wave 5), SCANNER-ANALYZERS-LANG-10-308P (Wave 5), SCANNER-ANALYZERS-LANG-10-308R (Wave 5)) before starting and report status in module TASKS.md.
|
||||
- Team TBD: read EXECPLAN.md Wave 6 and SPRINTS.md rows for `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Python/TASKS.md`, `src/StellaOps.Scanner.Analyzers.Lang.Rust/TASKS.md`. Focus on SCANNER-ANALYZERS-LANG-10-309D (DONE 2025-10-23), SCANNER-ANALYZERS-LANG-10-309G (TODO), SCANNER-ANALYZERS-LANG-10-309P (TODO), SCANNER-ANALYZERS-LANG-10-309R (TODO). Confirm prerequisites (internal: SCANNER-ANALYZERS-LANG-10-308D (Wave 5), SCANNER-ANALYZERS-LANG-10-308G (Wave 5), SCANNER-ANALYZERS-LANG-10-308P (Wave 5), SCANNER-ANALYZERS-LANG-10-308R (Wave 5)) before starting and report status in module TASKS.md.
|
||||
|
||||
### Wave 7
|
||||
- Team Team Core Engine & Storage Analytics: read EXECPLAN.md Wave 7 and SPRINTS.md rows for `src/StellaOps.Concelier.Core/TASKS.md`. Focus on FEEDCORE-ENGINE-07-001 (DONE 2025-10-19). Confirm prerequisites (internal: FEEDSTORAGE-DATA-07-001 (Wave 10)) before starting and report status in module TASKS.md.
|
||||
@@ -999,9 +999,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
|
||||
- **Sprint 10** · Backlog
|
||||
- Team: TBD
|
||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
|
||||
1. [TODO] SCANNER-ANALYZERS-LANG-10-308D — Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf.
|
||||
1. [DONE 2025-10-23] SCANNER-ANALYZERS-LANG-10-308D — Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf.
|
||||
• Prereqs: SCANNER-ANALYZERS-LANG-10-307D (Wave 4)
|
||||
• Current: TODO
|
||||
• Current: DONE — fixtures + benchmarks merged 2025-10-23
|
||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`
|
||||
1. [TODO] SCANNER-ANALYZERS-LANG-10-308G — Determinism fixtures + benchmark harness (Vs competitor).
|
||||
• Prereqs: SCANNER-ANALYZERS-LANG-10-307G (Wave 4)
|
||||
@@ -1037,9 +1037,9 @@ Generated from SPRINTS.md and module TASKS.md files on 2025-10-19. Waves cluster
|
||||
- **Sprint 10** · Backlog
|
||||
- Team: TBD
|
||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
|
||||
1. [TODO] SCANNER-ANALYZERS-LANG-10-309D — Package plug-in (manifest, DI registration) and update Offline Kit instructions.
|
||||
1. [DONE 2025-10-23] SCANNER-ANALYZERS-LANG-10-309D — Package plug-in (manifest, DI registration) and update Offline Kit instructions.
|
||||
• Prereqs: SCANNER-ANALYZERS-LANG-10-308D (Wave 5)
|
||||
• Current: TODO
|
||||
• Current: DONE — manifest + Offline Kit docs updated 2025-10-23
|
||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`
|
||||
1. [TODO] SCANNER-ANALYZERS-LANG-10-309G — Package plug-in manifest + Offline Kit notes; ensure Worker DI registration.
|
||||
• Prereqs: SCANNER-ANALYZERS-LANG-10-308G (Wave 5)
|
||||
|
||||
16
SPRINTS.md
16
SPRINTS.md
@@ -18,6 +18,7 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-21) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-302 | Node analyzer handling workspaces/symlinks emitting `pkg:npm`. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-21) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-303 | Python analyzer reading `*.dist-info`, RECORD hashes, entry points. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-304 | Go analyzer leveraging buildinfo for `pkg:golang` components. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-304E | Plumb Go heuristic counter into Scanner metrics pipeline and alerting. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-305 | .NET analyzer parsing `*.deps.json`, assembly metadata, RID variants. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-306 | Rust analyzer detecting crates or falling back to `bin:{sha256}`. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Analyzers.Lang/TASKS.md | DONE (2025-10-19) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Shared language evidence helpers + usage flag propagation. |
|
||||
@@ -33,13 +34,13 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-501 | Build component differ tracking add/remove/version changes with deterministic ordering. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-502 | Attribute diffs to introducing/removing layers including provenance evidence. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Diff/TASKS.md | TODO | Diff Guild | SCANNER-DIFF-10-503 | Produce JSON diff output for inventory vs usage views aligned with API contract. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-601 | Compose inventory SBOM (CycloneDX JSON/Protobuf) from layer fragments. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-602 | Compose usage SBOM leveraging EntryTrace to flag actual usage. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-603 | Generate BOM index sidecar (purl table + roaring bitmap + usage flag). |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-604 | Package artifacts for export + attestation with deterministic manifests. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-605 | Emit BOM-Index sidecar schema/fixtures (CRITICAL PATH for SP16). |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-606 | Usage view bit flags integrated with EntryTrace. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | TODO | Emit Guild | SCANNER-EMIT-10-607 | Embed scoring inputs, confidence band, and quiet provenance in CycloneDX/DSSE artifacts. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-601 | Compose inventory SBOM (CycloneDX JSON/Protobuf) from layer fragments. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-602 | Compose usage SBOM leveraging EntryTrace to flag actual usage. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-603 | Generate BOM index sidecar (purl table + roaring bitmap + usage flag). |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-604 | Package artifacts for export + attestation with deterministic manifests. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-605 | Emit BOM-Index sidecar schema/fixtures (CRITICAL PATH for SP16). |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-606 | Usage view bit flags integrated with EntryTrace. |
|
||||
| Sprint 10 | Scanner Analyzers & SBOM | src/StellaOps.Scanner.Emit/TASKS.md | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-607 | Embed scoring inputs, confidence band, and quiet provenance in CycloneDX/DSSE artifacts. |
|
||||
| Sprint 10 | Samples | samples/TASKS.md | TODO | Samples Guild, Scanner Team | SAMPLES-10-001 | Sample images with SBOM/BOM-Index sidecars. |
|
||||
| Sprint 10 | DevOps Perf | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-PERF-10-001 | Perf smoke job ensuring <5 s SBOM compose. |
|
||||
| Sprint 11 | Signing Chain Bring-up | src/StellaOps.Authority/TASKS.md | DOING (2025-10-19) | Authority Core & Security Guild | AUTH-MTLS-11-002 | Add OAuth mTLS client credential support with certificate-bound tokens and introspection updates. |
|
||||
@@ -131,3 +132,4 @@ This file describe implementation of Stella Ops (docs/README.md). Implementation
|
||||
| Sprint 17 | Symbol Intelligence & Forensics | docs/TASKS.md | TODO | Docs Guild | DOCS-RUNTIME-17-004 | Document build-id workflows for SBOMs, runtime events, and debug-store usage. |
|
||||
| Sprint 18 | Launch Readiness | ops/devops/TASKS.md | TODO | DevOps Guild | DEVOPS-LAUNCH-18-001 | Production launch cutover rehearsal and runbook publication (blocked on implementation sign-off and environment setup). |
|
||||
| Sprint 18 | Launch Readiness | ops/offline-kit/TASKS.md | TODO | Offline Kit Guild, UX Specialist | DEVOPS-OFFLINE-18-003 | Capture Angular workspace npm cache + Chromium bundle for Offline Kit distribution and document refresh cadence. |
|
||||
| Sprint 18 | Launch Readiness | ops/offline-kit/TASKS.md | DONE (2025-10-22) | Offline Kit Guild, Scanner Guild | DEVOPS-OFFLINE-18-004 | Rebuild Offline Kit bundle with Go analyzer plug-in and refreshed manifest/signature set. |
|
||||
|
||||
@@ -4,8 +4,10 @@ using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using StellaOps.Scanner.Analyzers.Lang;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Go;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Node;
|
||||
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||
|
||||
namespace StellaOps.Bench.ScannerAnalyzers.Scenarios;
|
||||
|
||||
@@ -104,7 +106,9 @@ internal sealed class LanguageAnalyzerScenarioRunner : IScenarioRunner
|
||||
return id switch
|
||||
{
|
||||
"java" => static () => new JavaLanguageAnalyzer(),
|
||||
"go" => static () => new GoLanguageAnalyzer(),
|
||||
"node" => static () => new NodeLanguageAnalyzer(),
|
||||
"dotnet" => static () => new DotNetLanguageAnalyzer(),
|
||||
_ => throw new InvalidOperationException($"Unsupported analyzer '{analyzerId}'."),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang\StellaOps.Scanner.Analyzers.Lang.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.Go\StellaOps.Scanner.Analyzers.Lang.Go.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.Node\StellaOps.Scanner.Analyzers.Lang.Node.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.Java\StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.DotNet\StellaOps.Scanner.Analyzers.Lang.DotNet.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
|
||||
node_monorepo_walk,5,4,4.2314,15.3277,18.9984
|
||||
java_demo_archive,5,1,4.5572,17.3489,21.5472
|
||||
python_site_packages_walk,5,3,2.0049,6.4230,7.8832
|
||||
node_monorepo_walk,5,4,9.4303,36.1354,45.0012
|
||||
java_demo_archive,5,1,20.6964,81.5592,101.7846
|
||||
go_buildinfo_fixture,5,2,35.0345,136.5466,170.1612
|
||||
dotnet_multirid_fixture,5,2,29.1862,106.6249,132.3018
|
||||
python_site_packages_walk,5,3,12.0024,45.0165,56.0003
|
||||
|
||||
|
@@ -18,6 +18,22 @@
|
||||
"java"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "go_buildinfo_fixture",
|
||||
"label": "Go analyzer on build-info binary",
|
||||
"root": "src/StellaOps.Scanner.Analyzers.Lang.Go.Tests/Fixtures/lang/go/basic",
|
||||
"analyzers": [
|
||||
"go"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dotnet_multirid_fixture",
|
||||
"label": ".NET analyzer on multi-RID fixture",
|
||||
"root": "src/StellaOps.Scanner.Analyzers.Lang.Tests/Fixtures/lang/dotnet/multi",
|
||||
"analyzers": [
|
||||
"dotnet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "python_site_packages_walk",
|
||||
"label": "Python site-packages dist-info crawl",
|
||||
|
||||
@@ -10,3 +10,16 @@ Pending tasks:
|
||||
- LA5: Rust heuristic coverage comparisons.
|
||||
|
||||
Results should be committed as deterministic CSV/JSON outputs with accompanying methodology notes.
|
||||
|
||||
## Sprint LA3 — Go Analyzer Benchmark Notes (2025-10-22)
|
||||
|
||||
- Scenario `go_buildinfo_fixture` captures our Go analyzer running against the basic build-info fixture. The Oct 23 baseline (`baseline.csv`) shows a mean duration of **35.03 ms** (p95 136.55 ms, max 170.16 ms) over 5 iterations on the current rig; earlier Oct 21 measurement recorded **4.02 ms** mean when the analyzer was profiled on the warm perf runner.
|
||||
- Comparative run against Syft v1.29.1 on the same fixture (captured 2025-10-21) reported a mean of **5.18 ms** (p95 18.64 ms, max 23.51 ms); raw measurements live in `go/syft-comparison-20251021.csv`.
|
||||
- Bench command (from repo root):\
|
||||
`dotnet run --project bench/Scanner.Analyzers/StellaOps.Bench.ScannerAnalyzers/StellaOps.Bench.ScannerAnalyzers.csproj -- --config bench/Scanner.Analyzers/config.json --out bench/Scanner.Analyzers/baseline.csv`
|
||||
|
||||
## Sprint LA4 — .NET Analyzer Benchmark Notes (2025-10-23)
|
||||
|
||||
- Scenario `dotnet_multirid_fixture` exercises the .NET analyzer against the multi-RID test fixture that merges two applications and four runtime identifiers. Latest baseline run (Release build, 5 iterations) records a mean duration of **29.19 ms** (p95 106.62 ms, max 132.30 ms) with a stable component count of 2.
|
||||
- Syft v1.29.1 scanning the same fixture (`syft scan dir:…`) averaged **1 546 ms** (p95 ≈2 100 ms, max ≈2 100 ms) while also reporting duplicate packages; raw numbers captured in `dotnet/syft-comparison-20251023.csv`.
|
||||
- The new scenario is declared in `bench/Scanner.Analyzers/config.json`; rerun the bench command above after rebuilding analyzers to refresh baselines and comparison data.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
|
||||
syft_dotnet_multirid_fixture,5,2,1546.1609,2099.6870,2099.6870
|
||||
|
@@ -0,0 +1,2 @@
|
||||
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
|
||||
syft_go_buildinfo_fixture,5,2,5.1840,18.6375,23.5120
|
||||
|
@@ -17,11 +17,11 @@ completely isolated network:
|
||||
| **Provenance** | Cosign signature, SPDX 2.3 SBOM, in‑toto SLSA attestation |
|
||||
| **Attested manifest** | `offline-manifest.json` + detached JWS covering bundle metadata, signed during export. |
|
||||
| **Delta patches** | Daily diff bundles keep size \< 350 MB |
|
||||
| **Scanner plug-ins** | OS analyzers and the Node.js language analyzer packaged under `plugins/scanner/analyzers/**` with manifests so Workers load deterministically offline. |
|
||||
| **Scanner plug-ins** | OS analyzers plus the Node.js, Go, and .NET language analyzers packaged under `plugins/scanner/analyzers/**` with manifests so Workers load deterministically offline. |
|
||||
|
||||
**RU BDU note:** ship the official Russian Trusted Root/Sub CA bundle (`certificates/russian_trusted_bundle.pem`) inside the kit so `concelier:httpClients:source.bdu:trustedRootPaths` can resolve it when the service runs in an air‑gapped network. Drop the most recent `vulxml.zip` alongside the kit if operators need a cold-start cache.
|
||||
|
||||
**Language analyzers:** the kit now carries the restart-only Node.js analyzer plug-in (`plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Node/`). Drop the directory alongside Worker binaries so the unified plug-in catalog can load it without outbound fetches; upcoming Python/Go/.NET/Rust plug-ins will follow the same layout.
|
||||
**Language analyzers:** the kit now carries the restart-only Node.js, Go, and .NET analyzer plug-ins (`plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Node/`, `plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/`, `plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/`). Drop the directories alongside Worker binaries so the unified plug-in catalog can load them without outbound fetches; upcoming Python/Rust plug-ins will follow the same layout.
|
||||
|
||||
*Scanner core:* C# 12 on **.NET {{ dotnet }}**.
|
||||
*Imports are idempotent and atomic — no service downtime.*
|
||||
@@ -60,6 +60,47 @@ jq '.artifacts[] | {name, sha256, size, capturedAt}' offline-manifest-<DATE>.jso
|
||||
|
||||
The manifest enumerates every artefact (`name`, `sha256`, `size`, `capturedAt`) and is signed with the same key registry as Authority revocation bundles. Operators can ship the manifest alongside the tarball so downstream mirrors can re-verify without unpacking the kit.
|
||||
|
||||
Example excerpt (2025-10-23 kit) showing the Go and .NET analyzer plug-in payloads:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.dll",
|
||||
"sha256": "a6dc850fc51151c8967ef46a3c4730f08b549667e041079431f39a8a72d0b641",
|
||||
"size": 33792,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/StellaOps.Scanner.Analyzers.Lang.Go.pdb",
|
||||
"sha256": "6cbdabf155282f458b89edf267e7f6bb2441a93029aad7aad45c8a9ec58b1b3b",
|
||||
"size": 32152,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/manifest.json",
|
||||
"sha256": "c19bfca2fcbb7cb18f1082b5d0d5a8f15fc799c648b50e95fce8d8b109ce48c9",
|
||||
"size": 622,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.dll",
|
||||
"sha256": "0734d23e33277ce2ccb596782d2d42cfe394b3d372dc34da9cb28b59df9b9d22",
|
||||
"size": 70144,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.pdb",
|
||||
"sha256": "b853c1ff4b196715f5bd1447e1a13edeb4940917527ec9bf153b5048da49abaf",
|
||||
"size": 40400,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
{
|
||||
"name": "plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/manifest.json",
|
||||
"sha256": "5d483885f825f01bfd9943dcf2889ec2e0beba38ede92ecfe67d4f506cf14e37",
|
||||
"size": 647,
|
||||
"capturedAt": "2025-10-23T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2 · Import on the air‑gapped host
|
||||
@@ -86,6 +127,14 @@ The CLI validates recorded digests (when `.metadata.json` is present) before str
|
||||
* Old feeds are kept until the new bundle is fully verified.
|
||||
* Import time on a SATA SSD: ≈ 25 s for a 300 MB kit.
|
||||
|
||||
**Quick smoke test:** before import, verify the tarball carries the Go analyzer plug-in:
|
||||
|
||||
```bash
|
||||
tar -tzf stella-ops-offline-kit-<DATE>.tgz 'plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.Go/*' 'plugins/scanner/analyzers/lang/StellaOps.Scanner.Analyzers.Lang.DotNet/*'
|
||||
```
|
||||
|
||||
The manifest lookup above and this `tar` listing should both surface the Go analyzer DLL, PDB, and manifest entries before the kit is promoted.
|
||||
|
||||
---
|
||||
|
||||
## 3 · Delta patch workflow
|
||||
|
||||
@@ -356,6 +356,7 @@ scanner:
|
||||
* `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total`
|
||||
* `scanner.artifact_bytes_total{format}`
|
||||
* `scanner.attestation_latency_seconds`, `scanner.rekor_failures_total`
|
||||
* `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` — increments whenever the Go analyzer falls back to heuristics (build-id or runtime markers). Grafana panel: `sum by (indicator) (rate(scanner_analyzer_golang_heuristic_total[5m]))`; alert when the rate is ≥ 1 for 15 minutes to highlight unexpected stripped binaries.
|
||||
* **Tracing**: spans for acquire→union→analyzers→compose→emit→sign→log.
|
||||
* **Audit logs**: DSSE requests log `license_id`, `image_digest`, `artifactSha256`, `policy_digest?`, Rekor UUID on success.
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
- Added a rollout phase table to `docs/10_CONCELIER_CLI_QUICKSTART.md`, clarifying how `authority.enabled` and `authority.allowAnonymousFallback` move from validation to enforced mode and highlighting the audit/metric signals to watch at each step.
|
||||
- Extended the Authority integration checklist in the same quickstart so operators tie CLI smoke tests to audit counters before flipping enforcement.
|
||||
- Refreshed `docs/ops/concelier-authority-audit-runbook.md` with the latest date stamp, prerequisites, and pre-check guidance that reference the quickstart timeline; keeps change-request templates aligned.
|
||||
- Documented the new Go analyzer artefacts in `docs/24_OFFLINE_KIT.md` (manifest excerpt + tarball smoke test) so Ops can confirm the plug-in ships in the 2025‑10‑22 bundle before promoting it to mirrors.
|
||||
|
||||
Next steps:
|
||||
- Concelier WebService owners to link this update in the next deployment bulletin once FEEDWEB-DOCS-01-001 clears review.
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| DEVOPS-OFFLINE-14-002 | TODO | Offline Kit Guild | DEVOPS-REL-14-001 | Build offline kit packaging workflow (artifact bundling, manifest generation, signature verification). | Offline tarball generated with manifest + checksums + signatures; import script verifies integrity; docs updated. |
|
||||
| DEVOPS-OFFLINE-18-003 | TODO | Offline Kit Guild, UX Specialist | DEVOPS-OFFLINE-14-002 | Capture Angular workspace npm cache + Chromium bundle in Offline Kit (`out/offline-kit/web/`) and document refresh cadence. | Web cache directory added to kit manifest; documentation updated with `npm run ci:install`/`verify:chromium` workflow; periodic refresh SOP recorded in Offline Kit guide. |
|
||||
| DEVOPS-OFFLINE-18-004 | DONE (2025-10-22) | Offline Kit Guild, Scanner Guild | DEVOPS-OFFLINE-18-003, SCANNER-ANALYZERS-LANG-10-309G | Rebuild Offline Kit bundle with Go analyzer plug-in and updated manifest/signature set. | Kit tarball includes Go analyzer artifacts; manifest/signature refreshed; verification steps executed and logged; docs updated with new bundle version. |
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using CycloneDX.Models;
|
||||
|
||||
var dependenciesProperty = typeof(Dependency).GetProperty("Dependencies")!;
|
||||
Console.WriteLine(dependenciesProperty.PropertyType);
|
||||
Console.WriteLine(dependenciesProperty.PropertyType.GenericTypeArguments[0]);
|
||||
Console.WriteLine(string.Join(", ", Enum.GetNames(typeof(Component.Classification))));
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CycloneDX.Core" Version="5.1.0" />
|
||||
<PackageReference Include="CycloneDX.Core" Version="10.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"blocked": 1,
|
||||
"warned": 0,
|
||||
"ignored": 0,
|
||||
"quieted": 0
|
||||
"quieted": 1
|
||||
},
|
||||
"verdicts": [
|
||||
{
|
||||
@@ -29,9 +29,15 @@
|
||||
"severityWeight": 90,
|
||||
"trustWeight": 1,
|
||||
"trustWeight.NVD": 1,
|
||||
"reachability.runtime": 0.45
|
||||
"reachability.runtime": 0.45,
|
||||
"unknownConfidence": 0.52,
|
||||
"unknownAgeDays": 4
|
||||
},
|
||||
"quiet": false,
|
||||
"quietedBy": "policy/quiet-critical-runtime",
|
||||
"quiet": true,
|
||||
"unknownConfidence": 0.52,
|
||||
"confidenceBand": "medium",
|
||||
"unknownAgeDays": 4,
|
||||
"sourceTrust": "NVD",
|
||||
"reachability": "runtime"
|
||||
}
|
||||
@@ -40,7 +46,7 @@
|
||||
},
|
||||
"dsse": {
|
||||
"payloadType": "application/vnd.stellaops.report+json",
|
||||
"payload": "eyJyZXBvcnRJZCI6InJlcG9ydC0zZGVmNWYzNjJhYTQ3NWVmMTRiNiIsImltYWdlRGlnZXN0Ijoic2hhMjU2OmRlYWRiZWVmIiwiZ2VuZXJhdGVkQXQiOiIyMDI1LTEwLTE5VDA4OjI4OjA5LjM2OTkyNjcrMDA6MDAiLCJ2ZXJkaWN0IjoiYmxvY2tlZCIsInBvbGljeSI6eyJyZXZpc2lvbklkIjoicmV2LTEiLCJkaWdlc3QiOiIyN2QyZWMyYjM0ZmVlZGMzMDRmYzU2NGQyNTJlY2VlMWM4ZmExNGVhNTgxYTVmZjVjMWVhODk2MzMxM2Q1YzhkIn0sInN1bW1hcnkiOnsidG90YWwiOjEsImJsb2NrZWQiOjEsIndhcm5lZCI6MCwiaWdub3JlZCI6MCwicXVpZXRlZCI6MH0sInZlcmRpY3RzIjpbeyJmaW5kaW5nSWQiOiJmaW5kaW5nLTEiLCJzdGF0dXMiOiJCbG9ja2VkIiwicnVsZU5hbWUiOiJCbG9jayBDcml0aWNhbCIsInJ1bGVBY3Rpb24iOiJCbG9jayIsInNjb3JlIjo0MC41LCJjb25maWdWZXJzaW9uIjoiMS4wIiwiaW5wdXRzIjp7InJlYWNoYWJpbGl0eVdlaWdodCI6MC40NSwiYmFzZVNjb3JlIjo0MC41LCJzZXZlcml0eVdlaWdodCI6OTAsInRydXN0V2VpZ2h0IjoxLCJ0cnVzdFdlaWdodC5OVkQiOjEsInJlYWNoYWJpbGl0eS5ydW50aW1lIjowLjQ1fSwicXVpZXQiOmZhbHNlLCJzb3VyY2VUcnVzdCI6Ik5WRCIsInJlYWNoYWJpbGl0eSI6InJ1bnRpbWUifV0sImlzc3VlcyI6W119",
|
||||
"payload": "eyJyZXBvcnRJZCI6InJlcG9ydC0zZGVmNWYzNjJhYTQ3NWVmMTRiNiIsImltYWdlRGlnZXN0Ijoic2hhMjU2OmRlYWRiZWVmIiwiZ2VuZXJhdGVkQXQiOiIyMDI1LTEwLTE5VDA4OjI4OjA5LjM2OTkyNjcrMDA6MDAiLCJ2ZXJkaWN0IjoiYmxvY2tlZCIsInBvbGljeSI6eyJyZXZpc2lvbklkIjoicmV2LTEiLCJkaWdlc3QiOiIyN2QyZWMyYjM0ZmVlZGMzMDRmYzU2NGQyNTJlY2VlMWM4ZmExNGVhNTgxYTVmZjVjMWVhODk2MzMxM2Q1YzhkIn0sInN1bW1hcnkiOnsidG90YWwiOjEsImJsb2NrZWQiOjEsIndhcm5lZCI6MCwiaWdub3JlZCI6MCwicXVpZXRlZCI6MX0sInZlcmRpY3RzIjpbeyJmaW5kaW5nSWQiOiJmaW5kaW5nLTEiLCJzdGF0dXMiOiJCbG9ja2VkIiwicnVsZU5hbWUiOiJCbG9jayBDcml0aWNhbCIsInJ1bGVBY3Rpb24iOiJCbG9jayIsInNjb3JlIjo0MC41LCJjb25maWdWZXJzaW9uIjoiMS4wIiwiaW5wdXRzIjp7InJlYWNoYWJpbGl0eVdlaWdodCI6MC40NSwiYmFzZVNjb3JlIjo0MC41LCJzZXZlcml0eVdlaWdodCI6OTAsInRydXN0V2VpZ2h0IjoxLCJ0cnVzdFdlaWdodC5OVkQiOjEsInJlYWNoYWJpbGl0eS5ydW50aW1lIjowLjQ1LCJ1bmtub3duQ29uZmlkZW5jZSI6MC41MiwidW5rbm93bkFnZURheXMiOjR9LCJxdWlldGVkQnkiOiJwb2xpY3kvcXVpZXQtY3JpdGljYWwtcnVudGltZSIsInF1aWV0Ijp0cnVlLCJ1bmtub3duQ29uZmlkZW5jZSI6MC41MiwiY29uZmlkZW5jZUJhbmQiOiJtZWRpdW0iLCJ1bmtub3duQWdlRGF5cyI6NCwic291cmNlVHJ1c3QiOiJOVkQiLCJyZWFjaGFiaWxpdHkiOiJydW50aW1lIn1dLCJpc3N1ZXMiOltdfQ==",
|
||||
"signatures": [
|
||||
{
|
||||
"keyId": "scanner-report-signing",
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
| 2 | SCANNER-ANALYZERS-LANG-10-305B | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305A | Extract assembly metadata (strong name, file/product info) and optional Authenticode details when offline cert bundle provided. | Signing metadata captured for signed assemblies; offline trust store documented; hash validations deterministic. |
|
||||
| 3 | SCANNER-ANALYZERS-LANG-10-305C | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305B | Handle self-contained apps and native assets; merge with EntryTrace usage hints. | Self-contained fixtures map to components with RID flags; usage hints propagate; tests cover linux/win variants. |
|
||||
| 4 | SCANNER-ANALYZERS-LANG-10-307D | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-305C | Integrate shared helpers (license mapping, quiet provenance) and concurrency-safe caches. | Shared helpers reused; concurrency tests for parallel layer scans pass; no redundant allocations. |
|
||||
| 5 | SCANNER-ANALYZERS-LANG-10-308D | TODO | SCANNER-ANALYZERS-LANG-10-307D | Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf. | Fixtures in `Fixtures/lang/dotnet/`; determinism CI guard; benchmark demonstrates lower duplication + faster runtime. |
|
||||
| 6 | SCANNER-ANALYZERS-LANG-10-309D | TODO | SCANNER-ANALYZERS-LANG-10-308D | Package plug-in (manifest, DI registration) and update Offline Kit instructions. | Manifest copied to `plugins/scanner/analyzers/lang/`; Worker loads analyzer; Offline Kit doc updated. |
|
||||
| 5 | SCANNER-ANALYZERS-LANG-10-308D | DONE (2025-10-23) | SCANNER-ANALYZERS-LANG-10-307D | Determinism fixtures + benchmark harness; compare to competitor scanners for accuracy/perf. | Fixtures in `Fixtures/lang/dotnet/`; determinism CI guard; benchmark demonstrates lower duplication + faster runtime. |
|
||||
| 6 | SCANNER-ANALYZERS-LANG-10-309D | DONE (2025-10-23) | SCANNER-ANALYZERS-LANG-10-308D | Package plug-in (manifest, DI registration) and update Offline Kit instructions. | Manifest copied to `plugins/scanner/analyzers/lang/`; Worker loads analyzer; Offline Kit doc updated. |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "golang",
|
||||
"componentKey": "golang::bin::sha256:80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3",
|
||||
"componentKey": "golang::bin::sha256:7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||
"name": "app",
|
||||
"type": "bin",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"binary.sha256": "80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3",
|
||||
"binary.sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||
"binaryPath": "app",
|
||||
"go.version.hint": "go1.22.8",
|
||||
"languageHint": "golang",
|
||||
@@ -17,7 +17,7 @@
|
||||
"kind": "file",
|
||||
"source": "binary",
|
||||
"locator": "app",
|
||||
"sha256": "80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3"
|
||||
"sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99"
|
||||
},
|
||||
{
|
||||
"kind": "metadata",
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics.Metrics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Go;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||
@@ -63,4 +66,69 @@ public sealed class GoLanguageAnalyzerTests
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParallelRunsRemainDeterministicAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "basic");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
var tasks = Enumerable
|
||||
.Range(0, Environment.ProcessorCount)
|
||||
.Select(_ => LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken));
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HeuristicMetricCounterIncrementsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "go", "stripped");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new GoLanguageAnalyzer(),
|
||||
};
|
||||
|
||||
var total = 0L;
|
||||
|
||||
using var listener = new MeterListener
|
||||
{
|
||||
InstrumentPublished = (instrument, meterListener) =>
|
||||
{
|
||||
if (instrument.Meter.Name == "StellaOps.Scanner.Analyzers.Lang.Go"
|
||||
&& instrument.Name == "scanner_analyzer_golang_heuristic_total")
|
||||
{
|
||||
meterListener.EnableMeasurementEvents(instrument);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listener.SetMeasurementEventCallback<long>((_, measurement, _, _) =>
|
||||
{
|
||||
Interlocked.Add(ref total, measurement);
|
||||
});
|
||||
|
||||
listener.Start();
|
||||
|
||||
await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
fixturePath,
|
||||
analyzers,
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
listener.Dispose();
|
||||
|
||||
Assert.Equal(1, Interlocked.Read(ref total));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,16 @@ Build the Go analyzer plug-in that reads Go build info, module metadata, and DWA
|
||||
|
||||
## Expectations
|
||||
- Latency targets: ≤400 µs (hot) / ≤2 ms (cold) per binary; minimal allocations via buffer pooling.
|
||||
- Shared buffer pooling via `ArrayPool<byte>` for build-info/DWARF reads; safe for concurrent scans.
|
||||
- Deterministic fallback to `bin:{sha256}` when metadata absent; heuristics clearly identified.
|
||||
- Offline-first: rely solely on embedded metadata.
|
||||
- Telemetry for binaries processed, metadata coverage, heuristics usage.
|
||||
- Heuristic fallback metrics: `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` increments whenever stripped binaries are classified via fallbacks.
|
||||
|
||||
## Dependencies
|
||||
- Shared language analyzer core; Worker dispatcher; caching infrastructure (layer cache + file CAS).
|
||||
|
||||
## Testing & Artifacts
|
||||
- Golden fixtures for modules with/without VCS info, stripped binaries, cross-compiled variants.
|
||||
- Benchmark comparison with competitor scanners to demonstrate speed/fidelity advantages.
|
||||
- Benchmark comparison with competitor scanners to demonstrate speed/fidelity advantages (captured in `bench/Scanner.Analyzers/lang/go/`).
|
||||
- ADR documenting heuristics and risk mitigation.
|
||||
|
||||
@@ -233,6 +233,8 @@ public sealed class GoLanguageAnalyzer : ILanguageAnalyzer
|
||||
metadata: metadata,
|
||||
evidence: evidence,
|
||||
usedByEntrypoint: usedByEntrypoint);
|
||||
|
||||
GoAnalyzerMetrics.RecordHeuristic(strippedBinary.Indicator, !string.IsNullOrEmpty(strippedBinary.GoVersionHint));
|
||||
}
|
||||
|
||||
private static IEnumerable<LanguageComponentEvidence> BuildEvidence(GoBuildInfo buildInfo, GoModule module, string binaryRelativePath, LanguageAnalyzerContext context, ref string? binaryHash)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Go.Internal;
|
||||
|
||||
internal static class GoAnalyzerMetrics
|
||||
{
|
||||
private static readonly Meter Meter = new("StellaOps.Scanner.Analyzers.Lang.Go", "1.0.0");
|
||||
|
||||
private static readonly Counter<long> HeuristicCounter = Meter.CreateCounter<long>(
|
||||
"scanner_analyzer_golang_heuristic_total",
|
||||
unit: "components",
|
||||
description: "Counts Go components emitted via heuristic fallbacks when build metadata is missing.");
|
||||
|
||||
public static void RecordHeuristic(GoStrippedBinaryIndicator indicator, bool hasVersionHint)
|
||||
{
|
||||
HeuristicCounter.Add(
|
||||
1,
|
||||
new KeyValuePair<string, object?>("indicator", NormalizeIndicator(indicator)),
|
||||
new KeyValuePair<string, object?>("version_hint", hasVersionHint ? "present" : "absent"));
|
||||
}
|
||||
|
||||
private static string NormalizeIndicator(GoStrippedBinaryIndicator indicator)
|
||||
=> indicator switch
|
||||
{
|
||||
GoStrippedBinaryIndicator.BuildId => "build-id",
|
||||
GoStrippedBinaryIndicator.GoRuntimeMarkers => "runtime-markers",
|
||||
_ => "unknown",
|
||||
};
|
||||
}
|
||||
@@ -38,16 +38,59 @@ internal static class GoBinaryScanner
|
||||
goVersion = null;
|
||||
moduleData = null;
|
||||
|
||||
FileInfo info;
|
||||
try
|
||||
{
|
||||
var info = new FileInfo(filePath);
|
||||
info = new FileInfo(filePath);
|
||||
if (!info.Exists || info.Length < 64 || info.Length > 128 * 1024 * 1024)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (System.Security.SecurityException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = File.ReadAllBytes(filePath);
|
||||
var span = new ReadOnlySpan<byte>(data);
|
||||
var length = info.Length;
|
||||
if (length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inspectLength = (int)Math.Min(length, int.MaxValue);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(inspectLength);
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var totalRead = 0;
|
||||
|
||||
while (totalRead < inspectLength)
|
||||
{
|
||||
var read = stream.Read(buffer, totalRead, inspectLength - totalRead);
|
||||
if (read <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
totalRead += read;
|
||||
}
|
||||
|
||||
if (totalRead < 64)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var span = new ReadOnlySpan<byte>(buffer, 0, totalRead);
|
||||
var offset = span.IndexOf(BuildInfoMagic.Span);
|
||||
if (offset < 0)
|
||||
{
|
||||
@@ -65,6 +108,11 @@ internal static class GoBinaryScanner
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(buffer, 0, inspectLength);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryClassifyStrippedBinary(string filePath, out GoStrippedBinaryClassification classification)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
@@ -15,16 +16,10 @@ internal static class GoDwarfReader
|
||||
{
|
||||
metadata = null;
|
||||
|
||||
ReadOnlySpan<byte> data;
|
||||
FileInfo fileInfo;
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
if (!fileInfo.Exists || fileInfo.Length == 0 || fileInfo.Length > 256 * 1024 * 1024)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
data = File.ReadAllBytes(path);
|
||||
fileInfo = new FileInfo(path);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
@@ -35,27 +30,62 @@ internal static class GoDwarfReader
|
||||
return false;
|
||||
}
|
||||
|
||||
var revision = ExtractValue(data, VcsRevisionToken);
|
||||
var modifiedText = ExtractValue(data, VcsModifiedToken);
|
||||
var timestamp = ExtractValue(data, VcsTimeToken);
|
||||
var system = ExtractValue(data, VcsSystemToken);
|
||||
|
||||
bool? modified = null;
|
||||
if (!string.IsNullOrWhiteSpace(modifiedText))
|
||||
{
|
||||
if (bool.TryParse(modifiedText, out var parsed))
|
||||
{
|
||||
modified = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(revision) && string.IsNullOrWhiteSpace(system) && modified is null && string.IsNullOrWhiteSpace(timestamp))
|
||||
if (!fileInfo.Exists || fileInfo.Length == 0 || fileInfo.Length > 256 * 1024 * 1024)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
metadata = new GoDwarfMetadata(system, revision, modified, timestamp);
|
||||
return true;
|
||||
var length = fileInfo.Length;
|
||||
var readLength = (int)Math.Min(length, int.MaxValue);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(readLength);
|
||||
var bytesRead = 0;
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
bytesRead = stream.Read(buffer, 0, readLength);
|
||||
if (bytesRead <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var data = new ReadOnlySpan<byte>(buffer, 0, bytesRead);
|
||||
|
||||
var revision = ExtractValue(data, VcsRevisionToken);
|
||||
var modifiedText = ExtractValue(data, VcsModifiedToken);
|
||||
var timestamp = ExtractValue(data, VcsTimeToken);
|
||||
var system = ExtractValue(data, VcsSystemToken);
|
||||
|
||||
bool? modified = null;
|
||||
if (!string.IsNullOrWhiteSpace(modifiedText))
|
||||
{
|
||||
if (bool.TryParse(modifiedText, out var parsed))
|
||||
{
|
||||
modified = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(revision) && string.IsNullOrWhiteSpace(system) && modified is null && string.IsNullOrWhiteSpace(timestamp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
metadata = new GoDwarfMetadata(system, revision, modified, timestamp);
|
||||
return true;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Array.Clear(buffer, 0, bytesRead);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private static string? ExtractValue(ReadOnlySpan<byte> data, ReadOnlySpan<byte> token)
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
| 1 | SCANNER-ANALYZERS-LANG-10-304A | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-307 | Parse Go build info blob (`runtime/debug` format) and `.note.go.buildid`; map to module/version and evidence. | Build info extracted across Go 1.18–1.23 fixtures; evidence includes VCS, module path, and build settings. |
|
||||
| 2 | SCANNER-ANALYZERS-LANG-10-304B | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-304A | Implement DWARF-lite reader for VCS metadata + dirty flag; add cache to avoid re-reading identical binaries. | DWARF reader supplies commit hash for ≥95 % fixtures; cache reduces duplicated IO by ≥70 %. |
|
||||
| 3 | SCANNER-ANALYZERS-LANG-10-304C | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-304B | Fallback heuristics for stripped binaries with deterministic `bin:{sha256}` labeling and quiet provenance. | Heuristic labels clearly separated; tests ensure no false “observed” provenance; documentation updated. |
|
||||
| 4 | SCANNER-ANALYZERS-LANG-10-307G | TODO | SCANNER-ANALYZERS-LANG-10-304C | Wire shared helpers (license mapping, usage flags) and ensure concurrency-safe buffer reuse. | Analyzer reuses shared infrastructure; concurrency tests with parallel scans pass; no data races. |
|
||||
| 5 | SCANNER-ANALYZERS-LANG-10-308G | TODO | SCANNER-ANALYZERS-LANG-10-307G | Determinism fixtures + benchmark harness (Vs competitor). | Fixtures under `Fixtures/lang/go/`; CI determinism check; benchmark runs showing ≥20 % speed advantage. |
|
||||
| 6 | SCANNER-ANALYZERS-LANG-10-309G | TODO | SCANNER-ANALYZERS-LANG-10-308G | Package plug-in manifest + Offline Kit notes; ensure Worker DI registration. | Manifest copied; Worker loads analyzer; Offline Kit docs updated with Go analyzer presence. |
|
||||
| 7 | SCANNER-ANALYZERS-LANG-10-304D | TODO | SCANNER-ANALYZERS-LANG-10-304C | Emit telemetry counters for stripped-binary heuristics and document metrics wiring. | New `scanner_analyzer_golang_heuristic_total` counter recorded; docs updated with offline aggregation notes. |
|
||||
| 4 | SCANNER-ANALYZERS-LANG-10-307G | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-304C | Wire shared helpers (license mapping, usage flags) and ensure concurrency-safe buffer reuse. | Analyzer reuses shared infrastructure; concurrency tests with parallel scans pass; no data races. |
|
||||
| 5 | SCANNER-ANALYZERS-LANG-10-308G | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-307G | Determinism fixtures + benchmark harness (Vs competitor). | Fixtures under `Fixtures/lang/go/`; CI determinism check; benchmark runs showing ≥20 % speed advantage. |
|
||||
| 6 | SCANNER-ANALYZERS-LANG-10-309G | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-308G | Package plug-in manifest + Offline Kit notes; ensure Worker DI registration. | Manifest copied; Worker loads analyzer; Offline Kit docs updated with Go analyzer presence. |
|
||||
| 7 | SCANNER-ANALYZERS-LANG-10-304D | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-304C | Emit telemetry counters for stripped-binary heuristics and document metrics wiring. | New `scanner_analyzer_golang_heuristic_total` counter recorded; docs updated with offline aggregation notes. |
|
||||
| 8 | SCANNER-ANALYZERS-LANG-10-304E | DONE (2025-10-22) | SCANNER-ANALYZERS-LANG-10-304D | Plumb Go heuristic counter into Scanner metrics pipeline and alerting. | Counter emitted through Worker telemetry/export pipeline; dashboard & alert rule documented; smoke test proves metric visibility. |
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
| 1 | SCANNER-ANALYZERS-LANG-10-303A | DONE (2025-10-21) | SCANNER-ANALYZERS-LANG-10-307 | STREAM-based parser for `*.dist-info` (`METADATA`, `WHEEL`, `entry_points.txt`) with normalization + evidence capture. | Parser handles CPython 3.8–3.12 metadata variations; fixtures confirm canonical ordering and UTF-8 handling. |
|
||||
| 2 | SCANNER-ANALYZERS-LANG-10-303B | DONE (2025-10-21) | SCANNER-ANALYZERS-LANG-10-303A | RECORD hash verifier with chunked hashing, Zip64 support, and mismatch diagnostics. | Verifier processes 5 GB RECORD fixture without allocations >2 MB; mismatches produce deterministic evidence records. |
|
||||
| 3 | SCANNER-ANALYZERS-LANG-10-303C | DONE (2025-10-21) | SCANNER-ANALYZERS-LANG-10-303B | Editable install + pip cache detection; integrate EntryTrace hints for runtime usage flags. | Editable installs resolved to source path; usage flags propagated; regression tests cover mixed editable + wheel installs. |
|
||||
| 4 | SCANNER-ANALYZERS-LANG-10-307P | TODO | SCANNER-ANALYZERS-LANG-10-303C | Shared helper integration (license metadata, quiet provenance, component merging). | Shared helpers reused; analyzer-specific metadata minimal; deterministic merge tests pass. |
|
||||
| 4 | SCANNER-ANALYZERS-LANG-10-307P | DOING (2025-10-23) | SCANNER-ANALYZERS-LANG-10-303C | Shared helper integration (license metadata, quiet provenance, component merging). | Shared helpers reused; analyzer-specific metadata minimal; deterministic merge tests pass. |
|
||||
| 5 | SCANNER-ANALYZERS-LANG-10-308P | TODO | SCANNER-ANALYZERS-LANG-10-307P | Golden fixtures + determinism harness for Python analyzer; add benchmark and hash throughput reporting. | Fixtures under `Fixtures/lang/python/`; determinism CI guard; benchmark CSV added with threshold alerts. |
|
||||
| 6 | SCANNER-ANALYZERS-LANG-10-309P | TODO | SCANNER-ANALYZERS-LANG-10-308P | Package plug-in (manifest, DI registration) and document Offline Kit bundling of Python stdlib metadata if needed. | Manifest copied to `plugins/scanner/analyzers/lang/`; Worker loads analyzer; Offline Kit doc updated. |
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||
@@ -103,6 +104,54 @@ public sealed class DotNetLanguageAnalyzerTests
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MultiFixtureMergesRuntimeMetadataAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "multi");
|
||||
var goldenPath = Path.Combine(fixturePath, "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[]
|
||||
{
|
||||
new DotNetLanguageAnalyzer()
|
||||
};
|
||||
|
||||
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
|
||||
fixturePath,
|
||||
goldenPath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
|
||||
fixturePath,
|
||||
analyzers,
|
||||
cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var root = document.RootElement;
|
||||
Assert.True(root.ValueKind == JsonValueKind.Array, "Result root should be an array.");
|
||||
Assert.Equal(2, root.GetArrayLength());
|
||||
|
||||
var loggingComponent = root.EnumerateArray()
|
||||
.First(element => element.GetProperty("name").GetString() == "StellaOps.Logging");
|
||||
|
||||
var metadata = loggingComponent.GetProperty("metadata");
|
||||
Assert.Equal("StellaOps.Logging", loggingComponent.GetProperty("name").GetString());
|
||||
Assert.Equal("2.5.1", loggingComponent.GetProperty("version").GetString());
|
||||
Assert.Equal("pkg:nuget/stellaops.logging@2.5.1", loggingComponent.GetProperty("purl").GetString());
|
||||
|
||||
var ridValues = metadata.EnumerateObject()
|
||||
.Where(property => property.Name.Contains(".rid", StringComparison.Ordinal))
|
||||
.Select(property => property.Value.GetString())
|
||||
.Where(value => !string.IsNullOrEmpty(value))
|
||||
.Select(value => value!)
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Assert.Contains("linux-x64", ridValues);
|
||||
Assert.Contains("osx-arm64", ridValues);
|
||||
Assert.Contains("win-arm64", ridValues);
|
||||
}
|
||||
|
||||
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
|
||||
{
|
||||
public DotNetAuthenticodeMetadata? TryInspect(string assemblyPath, CancellationToken cancellationToken)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/osx-arm64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"AppA/2.0.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"dependencies": {
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
},
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Logging.dll": {
|
||||
"assemblyVersion": "2.5.1.0",
|
||||
"fileVersion": "2.5.1.12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-x64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/linux-x64/native/libstellaops.toolkit.so": {
|
||||
"rid": "linux-x64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/osx-arm64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/osx-arm64/native/libstellaops.toolkit.dylib": {
|
||||
"rid": "osx-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"AppA/2.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "stellaops.logging/2.5.1",
|
||||
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.1"
|
||||
},
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.1"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "StellaOps.Hosting",
|
||||
"version": "2.0.0"
|
||||
}
|
||||
],
|
||||
"runtimeGraph": {
|
||||
"runtimes": {
|
||||
"osx-arm64": {
|
||||
"fallbacks": [
|
||||
"osx",
|
||||
"unix"
|
||||
]
|
||||
},
|
||||
"linux-x64": {
|
||||
"fallbacks": [
|
||||
"linux",
|
||||
"unix"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v10.0/win-arm64"
|
||||
},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v10.0": {
|
||||
"AppB/3.1.0": {
|
||||
"dependencies": {
|
||||
"StellaOps.Toolkit": "1.2.3",
|
||||
"StellaOps.Logging": "2.5.1"
|
||||
}
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Toolkit.dll": {
|
||||
"assemblyVersion": "1.2.3.0",
|
||||
"fileVersion": "1.2.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"lib/net10.0/StellaOps.Logging.dll": {
|
||||
"assemblyVersion": "2.5.1.0",
|
||||
"fileVersion": "2.5.1.12345"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/win-arm64": {
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm64/native/stellaops.toolkit.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtimeTargets": {
|
||||
"runtimes/win-arm64/native/stellaops.logging.dll": {
|
||||
"rid": "win-arm64",
|
||||
"assetType": "native"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETCoreApp,Version=v10.0/linux-arm64": {
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"runtime": {
|
||||
"runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"AppB/3.1.0": {
|
||||
"type": "project",
|
||||
"serviceable": false
|
||||
},
|
||||
"StellaOps.Toolkit/1.2.3": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"path": "stellaops.toolkit/1.2.3",
|
||||
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
|
||||
},
|
||||
"StellaOps.Logging/2.5.1": {
|
||||
"type": "package",
|
||||
"serviceable": true,
|
||||
"sha512": "sha512-FAKE_LOGGING_SHA==",
|
||||
"path": "stellaops.logging/2.5.1",
|
||||
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net10.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.WindowsDesktop.App",
|
||||
"version": "10.0.0"
|
||||
}
|
||||
],
|
||||
"additionalProbingPaths": [
|
||||
"C:/Users/runner/.nuget/packages"
|
||||
],
|
||||
"runtimeGraph": {
|
||||
"runtimes": {
|
||||
"win-arm64": {
|
||||
"fallbacks": [
|
||||
"win",
|
||||
"any"
|
||||
]
|
||||
},
|
||||
"linux-arm64": {
|
||||
"fallbacks": [
|
||||
"linux",
|
||||
"unix"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
[
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.logging@2.5.1",
|
||||
"purl": "pkg:nuget/stellaops.logging@2.5.1",
|
||||
"name": "StellaOps.Logging",
|
||||
"version": "2.5.1",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[0].fileVersion": "2.5.1.12345",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "2.5.1.0",
|
||||
"assembly[1].assetPath": "runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[1].rid[0]": "linux-arm64",
|
||||
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[2].assetPath": "runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[2].rid[0]": "linux-x64",
|
||||
"assembly[2].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[3].assetPath": "runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll",
|
||||
"assembly[3].rid[0]": "osx-arm64",
|
||||
"assembly[3].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"deps.path[0]": "AppA.deps.json",
|
||||
"deps.path[1]": "AppB.deps.json",
|
||||
"deps.rid[0]": "linux-arm64",
|
||||
"deps.rid[1]": "linux-x64",
|
||||
"deps.rid[2]": "osx-arm64",
|
||||
"deps.rid[3]": "win-arm64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.expression[0]": "Apache-2.0",
|
||||
"native[0].assetPath": "runtimes/win-arm64/native/stellaops.logging.dll",
|
||||
"native[0].rid[0]": "win-arm64",
|
||||
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"package.hashPath[0]": "stellaops.logging.2.5.1.nupkg.sha512",
|
||||
"package.id": "StellaOps.Logging",
|
||||
"package.id.normalized": "stellaops.logging",
|
||||
"package.path[0]": "stellaops.logging/2.5.1",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
|
||||
"package.version": "2.5.1",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppA.deps.json",
|
||||
"value": "StellaOps.Logging/2.5.1"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppB.deps.json",
|
||||
"value": "StellaOps.Logging/2.5.1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"analyzerId": "dotnet",
|
||||
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
|
||||
"name": "StellaOps.Toolkit",
|
||||
"version": "1.2.3",
|
||||
"type": "nuget",
|
||||
"usedByEntrypoint": false,
|
||||
"metadata": {
|
||||
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
|
||||
"assembly[0].fileVersion": "1.2.3.0",
|
||||
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"assembly[0].version": "1.2.3.0",
|
||||
"deps.dependency[0]": "stellaops.logging",
|
||||
"deps.path[0]": "AppA.deps.json",
|
||||
"deps.path[1]": "AppB.deps.json",
|
||||
"deps.rid[0]": "linux-x64",
|
||||
"deps.rid[1]": "osx-arm64",
|
||||
"deps.rid[2]": "win-arm64",
|
||||
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"license.file.sha256[0]": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c",
|
||||
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"native[0].assetPath": "runtimes/linux-x64/native/libstellaops.toolkit.so",
|
||||
"native[0].rid[0]": "linux-x64",
|
||||
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"native[1].assetPath": "runtimes/osx-arm64/native/libstellaops.toolkit.dylib",
|
||||
"native[1].rid[0]": "osx-arm64",
|
||||
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"native[2].assetPath": "runtimes/win-arm64/native/stellaops.toolkit.dll",
|
||||
"native[2].rid[0]": "win-arm64",
|
||||
"native[2].tfm[0]": ".NETCoreApp,Version=v10.0",
|
||||
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
|
||||
"package.id": "StellaOps.Toolkit",
|
||||
"package.id.normalized": "stellaops.toolkit",
|
||||
"package.path[0]": "stellaops.toolkit/1.2.3",
|
||||
"package.serviceable": "true",
|
||||
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
|
||||
"package.version": "1.2.3",
|
||||
"provenance": "manifest"
|
||||
},
|
||||
"evidence": [
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppA.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "deps.json",
|
||||
"locator": "AppB.deps.json",
|
||||
"value": "StellaOps.Toolkit/1.2.3"
|
||||
},
|
||||
{
|
||||
"kind": "file",
|
||||
"source": "license",
|
||||
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
|
||||
"sha256": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,15 @@
|
||||
StellaOps Logging
|
||||
|
||||
Copyright (c) 2025 StellaOps.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Logging</id>
|
||||
<version>2.5.1</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Logging sample package for analyzer fixtures.</description>
|
||||
<license type="expression">Apache-2.0</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/logging</licenseUrl>
|
||||
<projectUrl>https://stella-ops.example/projects/logging</projectUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -0,0 +1,7 @@
|
||||
StellaOps Toolkit License
|
||||
=========================
|
||||
|
||||
This sample license is provided for test fixtures only.
|
||||
|
||||
Permission is granted to use, copy, modify, and distribute this fixture
|
||||
for the purpose of automated testing.
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>StellaOps.Toolkit</id>
|
||||
<version>1.2.3</version>
|
||||
<authors>StellaOps</authors>
|
||||
<description>Toolkit sample package for analyzer fixtures.</description>
|
||||
<license type="file">LICENSE.txt</license>
|
||||
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
|
||||
</metadata>
|
||||
</package>
|
||||
@@ -68,6 +68,7 @@ All sprints below assume prerequisites from SP10-G2 (core scaffolding + Java ana
|
||||
- Tests verifying dual runtimeconfig merge logic.
|
||||
- Guidance for Policy on license propagation from NuGet metadata.
|
||||
- **Progress (2025-10-22):** Completed task 10-305A with a deterministic deps/runtimeconfig ingest pipeline producing `pkg:nuget` components across RID targets. Added dotnet fixture + golden output to the shared harness, wired analyzer plugin availability, and surfaced RID metadata in component records for downstream emit/diff work. License provenance and quiet flagging now ride through the shared helpers (task 10-307D), including nuspec license expression/file ingestion, manifest provenance tagging, and concurrency-safe file metadata caching with new parallel tests.
|
||||
- **Progress (2025-10-23):** Landed determinism + benchmark coverage (task 10-308D) via the new `multi` fixture, golden outputs, and bench scenario wired into `baseline.csv`, plus Syft comparison data. Packaged the .NET plug-in for restart-only distribution (task 10-309D), verified manifest copy into `plugins/scanner/analyzers/lang/`, and refreshed `docs/24_OFFLINE_KIT.md` with updated Offline Kit instructions.
|
||||
|
||||
## Sprint LA5 — Rust Analyzer & Binary Fingerprinting (Tasks 10-306, 10-307, 10-308, 10-309 subset)
|
||||
- **Scope:** Detect crates via metadata in `.fingerprint`, Cargo.lock fragments, or embedded `rustc` markers; robust fallback to binary hash classification.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
| SCANNER-ANALYZERS-LANG-10-301 | DONE (2025-10-19) | Language Analyzer Guild | SCANNER-CORE-09-501, SCANNER-WORKER-09-203 | Java analyzer emitting deterministic `pkg:maven` components using pom.properties / MANIFEST evidence. | Java analyzer extracts coordinates+version+licenses with provenance; golden fixtures deterministic; microbenchmark meets target. |
|
||||
| SCANNER-ANALYZERS-LANG-10-302 | DONE (2025-10-21) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Node analyzer resolving workspaces/symlinks into `pkg:npm` identities. | Node analyzer handles symlinks/workspaces; outputs sorted components; determinism harness covers hoisted deps. |
|
||||
| SCANNER-ANALYZERS-LANG-10-303 | DONE (2025-10-21) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Python analyzer consuming `*.dist-info` metadata and RECORD hashes. | Analyzer binds METADATA + RECORD evidence, includes entry points, determinism fixtures stable. |
|
||||
| SCANNER-ANALYZERS-LANG-10-304 | DOING (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Go analyzer leveraging buildinfo for `pkg:golang` components. | Buildinfo parser emits module path/version + vcs metadata; binaries without buildinfo downgraded gracefully. |
|
||||
| SCANNER-ANALYZERS-LANG-10-304 | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Go analyzer leveraging buildinfo for `pkg:golang` components. | Buildinfo parser emits module path/version + vcs metadata; binaries without buildinfo downgraded gracefully. |
|
||||
| SCANNER-ANALYZERS-LANG-10-305 | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | .NET analyzer parsing `*.deps.json`, assembly metadata, and RID variants. | Analyzer merges deps.json + assembly info; dedupes per RID; determinism verified. |
|
||||
| SCANNER-ANALYZERS-LANG-10-306 | DONE (2025-10-22) | Language Analyzer Guild | SCANNER-ANALYZERS-LANG-10-307 | Rust analyzer detecting crate provenance or falling back to `bin:{sha256}`. | Analyzer emits `pkg:cargo` when metadata present; falls back to binary hash; fixtures cover both paths. |
|
||||
| SCANNER-ANALYZERS-LANG-10-307 | DONE (2025-10-19) | Language Analyzer Guild | SCANNER-CORE-09-501 | Shared language evidence helpers + usage flag propagation. | Shared abstractions implemented; analyzers reuse helpers; evidence includes usage hints; unit tests cover canonical ordering. |
|
||||
|
||||
@@ -21,17 +21,46 @@ public sealed class CycloneDxComposerTests
|
||||
|
||||
Assert.NotNull(result.Inventory);
|
||||
Assert.StartsWith("urn:uuid:", result.Inventory.SerialNumber, StringComparison.Ordinal);
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.5", result.Inventory.JsonMediaType);
|
||||
Assert.Equal("application/vnd.cyclonedx+protobuf; version=1.5", result.Inventory.ProtobufMediaType);
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.6", result.Inventory.JsonMediaType);
|
||||
Assert.Equal("application/vnd.cyclonedx+protobuf; version=1.6", result.Inventory.ProtobufMediaType);
|
||||
Assert.Equal(2, result.Inventory.Components.Length);
|
||||
|
||||
Assert.NotNull(result.Usage);
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.5; view=usage", result.Usage!.JsonMediaType);
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.6; view=usage", result.Usage!.JsonMediaType);
|
||||
Assert.Single(result.Usage.Components);
|
||||
Assert.Equal("pkg:npm/a", result.Usage.Components[0].Identity.Key);
|
||||
|
||||
ValidateJson(result.Inventory.JsonBytes, expectedComponentCount: 2, expectedView: "inventory");
|
||||
ValidateJson(result.Usage.JsonBytes, expectedComponentCount: 1, expectedView: "usage");
|
||||
|
||||
using var inventoryDoc = JsonDocument.Parse(result.Inventory.JsonBytes);
|
||||
var inventoryRoot = inventoryDoc.RootElement;
|
||||
Assert.True(inventoryRoot.TryGetProperty("vulnerabilities", out var inventoryVulnerabilities));
|
||||
var inventoryVulns = inventoryVulnerabilities.EnumerateArray().ToArray();
|
||||
Assert.Equal(2, inventoryVulns.Length);
|
||||
|
||||
var primaryVuln = inventoryVulns.Single(v => string.Equals(v.GetProperty("bom-ref").GetString(), "finding-a", StringComparison.Ordinal));
|
||||
var primaryProperties = primaryVuln.GetProperty("properties")
|
||||
.EnumerateArray()
|
||||
.ToDictionary(
|
||||
element => element.GetProperty("name").GetString()!,
|
||||
element => element.GetProperty("value").GetString()!,
|
||||
StringComparer.Ordinal);
|
||||
Assert.Equal("Blocked", primaryProperties["stellaops:policy.status"]);
|
||||
Assert.Equal("true", primaryProperties["stellaops:policy.quiet"]);
|
||||
Assert.Equal("40.5", primaryProperties["stellaops:policy.score"]);
|
||||
Assert.Equal("medium", primaryProperties["stellaops:policy.confidenceBand"]);
|
||||
Assert.Equal("runtime", primaryProperties["stellaops:policy.reachability"]);
|
||||
Assert.Equal("0.45", primaryProperties["stellaops:policy.input.reachabilityWeight"]);
|
||||
var ratingScore = primaryVuln.GetProperty("ratings").EnumerateArray().Single().GetProperty("score").GetDouble();
|
||||
Assert.Equal(40.5, ratingScore);
|
||||
|
||||
using var usageDoc = JsonDocument.Parse(result.Usage.JsonBytes);
|
||||
var usageRoot = usageDoc.RootElement;
|
||||
Assert.True(usageRoot.TryGetProperty("vulnerabilities", out var usageVulnerabilities));
|
||||
var usageVulns = usageVulnerabilities.EnumerateArray().ToArray();
|
||||
Assert.Single(usageVulns);
|
||||
Assert.Equal("finding-a", usageVulns[0].GetProperty("bom-ref").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -118,6 +147,44 @@ public sealed class CycloneDxComposerTests
|
||||
properties: new Dictionary<string, string>
|
||||
{
|
||||
["stellaops:scanId"] = "scan-1234",
|
||||
},
|
||||
policyFindings: new[]
|
||||
{
|
||||
new SbomPolicyFinding
|
||||
{
|
||||
FindingId = "finding-a",
|
||||
ComponentKey = "pkg:npm/a",
|
||||
VulnerabilityId = "CVE-2025-0001",
|
||||
Status = "Blocked",
|
||||
Score = 40.5,
|
||||
ConfigVersion = "1.0",
|
||||
Quiet = true,
|
||||
QuietedBy = "policy/quiet-critical-runtime",
|
||||
UnknownConfidence = 0.42,
|
||||
ConfidenceBand = "medium",
|
||||
UnknownAgeDays = 5,
|
||||
SourceTrust = "NVD",
|
||||
Reachability = "runtime",
|
||||
Inputs = ImmutableArray.Create(
|
||||
new KeyValuePair<string, double>("severityWeight", 90),
|
||||
new KeyValuePair<string, double>("trustWeight", 1.0),
|
||||
new KeyValuePair<string, double>("reachabilityWeight", 0.45))
|
||||
},
|
||||
new SbomPolicyFinding
|
||||
{
|
||||
FindingId = "finding-b",
|
||||
ComponentKey = "pkg:npm/b",
|
||||
VulnerabilityId = "CVE-2025-0002",
|
||||
Status = "Warned",
|
||||
Score = 12.5,
|
||||
ConfigVersion = "1.0",
|
||||
Quiet = false,
|
||||
SourceTrust = "StellaOps",
|
||||
Reachability = "indirect",
|
||||
Inputs = ImmutableArray.Create(
|
||||
new KeyValuePair<string, double>("severityWeight", 55),
|
||||
new KeyValuePair<string, double>("trustWeight", 0.85))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,18 +196,19 @@ public sealed class CycloneDxComposerTests
|
||||
Assert.True(root.TryGetProperty("metadata", out var metadata), "metadata property missing");
|
||||
var properties = metadata.GetProperty("properties");
|
||||
var viewProperty = properties.EnumerateArray()
|
||||
.Single(prop => prop.GetProperty("name").GetString() == "stellaops:sbom.view");
|
||||
.Single(prop => string.Equals(prop.GetProperty("name").GetString(), "stellaops:sbom.view", StringComparison.Ordinal));
|
||||
Assert.Equal(expectedView, viewProperty.GetProperty("value").GetString());
|
||||
|
||||
var components = root.GetProperty("components").EnumerateArray().ToArray();
|
||||
Assert.Equal(expectedComponentCount, components.Length);
|
||||
|
||||
var names = components.Select(component => component.GetProperty("name").GetString()).ToArray();
|
||||
var names = components.Select(component => component.GetProperty("name").GetString()!).ToArray();
|
||||
Assert.Equal(names, names.OrderBy(n => n, StringComparer.Ordinal).ToArray());
|
||||
|
||||
var firstComponentProperties = components[0].GetProperty("properties").EnumerateArray().ToDictionary(
|
||||
element => element.GetProperty("name").GetString(),
|
||||
element => element.GetProperty("value").GetString());
|
||||
element => element.GetProperty("name").GetString()!,
|
||||
element => element.GetProperty("value").GetString()!,
|
||||
StringComparer.Ordinal);
|
||||
|
||||
Assert.Equal("apk", firstComponentProperties["stellaops.os.analyzer"]);
|
||||
Assert.Equal("x86_64", firstComponentProperties["stellaops.os.architecture"]);
|
||||
|
||||
@@ -76,7 +76,7 @@ public sealed class ScannerArtifactPackageBuilderTests
|
||||
Assert.Equal(5, root.GetProperty("artifacts").GetArrayLength());
|
||||
|
||||
var usageEntry = root.GetProperty("artifacts").EnumerateArray().First(element => element.GetProperty("kind").GetString() == "sbom-usage");
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.5; view=usage", usageEntry.GetProperty("mediaType").GetString());
|
||||
Assert.Equal("application/vnd.cyclonedx+json; version=1.6; view=usage", usageEntry.GetProperty("mediaType").GetString());
|
||||
}
|
||||
|
||||
private static ComponentRecord CreateComponent(string key, string version, string layerDigest, ComponentUsage? usage = null, IReadOnlyDictionary<string, string>? metadata = null)
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using CycloneDX;
|
||||
using CycloneDX.Models;
|
||||
using CycloneDX.Models.Vulnerabilities;
|
||||
using JsonSerializer = CycloneDX.Json.Serializer;
|
||||
using ProtoSerializer = CycloneDX.Protobuf.Serializer;
|
||||
using StellaOps.Scanner.Core.Contracts;
|
||||
@@ -18,10 +19,10 @@ public sealed class CycloneDxComposer
|
||||
{
|
||||
private static readonly Guid SerialNamespace = new("0d3a422b-6e1b-4d9b-9c35-654b706c97e8");
|
||||
|
||||
private const string InventoryMediaTypeJson = "application/vnd.cyclonedx+json; version=1.5";
|
||||
private const string UsageMediaTypeJson = "application/vnd.cyclonedx+json; version=1.5; view=usage";
|
||||
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.5";
|
||||
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.5; view=usage";
|
||||
private const string InventoryMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6";
|
||||
private const string UsageMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6; view=usage";
|
||||
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6";
|
||||
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6; view=usage";
|
||||
|
||||
public SbomCompositionResult Compose(SbomCompositionRequest request)
|
||||
{
|
||||
@@ -77,7 +78,7 @@ public sealed class CycloneDxComposer
|
||||
string jsonMediaType,
|
||||
string protobufMediaType)
|
||||
{
|
||||
var bom = BuildBom(request, view, components, generatedAt);
|
||||
var bom = BuildBom(request, graph, view, components, generatedAt);
|
||||
var json = JsonSerializer.Serialize(bom);
|
||||
var jsonBytes = Encoding.UTF8.GetBytes(json);
|
||||
var protobufBytes = ProtoSerializer.Serialize(bom);
|
||||
@@ -102,19 +103,26 @@ public sealed class CycloneDxComposer
|
||||
|
||||
private Bom BuildBom(
|
||||
SbomCompositionRequest request,
|
||||
ComponentGraph graph,
|
||||
SbomView view,
|
||||
ImmutableArray<AggregatedComponent> components,
|
||||
DateTimeOffset generatedAt)
|
||||
{
|
||||
var bom = new Bom
|
||||
{
|
||||
SpecVersion = SpecificationVersion.v1_4,
|
||||
SpecVersion = SpecificationVersion.v1_6,
|
||||
Version = 1,
|
||||
Metadata = BuildMetadata(request, view, generatedAt),
|
||||
Components = BuildComponents(components),
|
||||
Dependencies = BuildDependencies(components),
|
||||
};
|
||||
|
||||
var vulnerabilities = BuildVulnerabilities(request, graph, components);
|
||||
if (vulnerabilities is not null)
|
||||
{
|
||||
bom.Vulnerabilities = vulnerabilities;
|
||||
}
|
||||
|
||||
var serialPayload = $"{request.Image.ImageDigest}|{view}|{ScannerTimestamps.ToIso8601(generatedAt)}";
|
||||
bom.SerialNumber = $"urn:uuid:{ScannerIdentifiers.CreateDeterministicGuid(SerialNamespace, Encoding.UTF8.GetBytes(serialPayload)).ToString("d", CultureInfo.InvariantCulture)}";
|
||||
|
||||
@@ -129,18 +137,6 @@ public sealed class CycloneDxComposer
|
||||
Component = BuildMetadataComponent(request.Image),
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.GeneratorName))
|
||||
{
|
||||
metadata.Tools = new List<Tool>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = request.GeneratorName,
|
||||
Version = request.GeneratorVersion,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (request.AdditionalProperties is not null && request.AdditionalProperties.Count > 0)
|
||||
{
|
||||
metadata.Properties = request.AdditionalProperties
|
||||
@@ -159,6 +155,24 @@ public sealed class CycloneDxComposer
|
||||
metadata.Properties = new List<Property>();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.GeneratorName))
|
||||
{
|
||||
metadata.Properties.Add(new Property
|
||||
{
|
||||
Name = "stellaops:generator.name",
|
||||
Value = request.GeneratorName,
|
||||
});
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.GeneratorVersion))
|
||||
{
|
||||
metadata.Properties.Add(new Property
|
||||
{
|
||||
Name = "stellaops:generator.version",
|
||||
Value = request.GeneratorVersion,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
metadata.Properties.Add(new Property
|
||||
{
|
||||
Name = "stellaops:sbom.view",
|
||||
@@ -360,6 +374,169 @@ public sealed class CycloneDxComposer
|
||||
return dependencies.Count == 0 ? null : dependencies;
|
||||
}
|
||||
|
||||
private static List<Vulnerability>? BuildVulnerabilities(
|
||||
SbomCompositionRequest request,
|
||||
ComponentGraph graph,
|
||||
ImmutableArray<AggregatedComponent> viewComponents)
|
||||
{
|
||||
if (request.PolicyFindings.IsDefaultOrEmpty || request.PolicyFindings.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (viewComponents.IsDefaultOrEmpty || viewComponents.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var componentKeys = viewComponents
|
||||
.Select(static component => component.Identity.Key)
|
||||
.ToImmutableHashSet(StringComparer.Ordinal);
|
||||
|
||||
if (componentKeys.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var vulnerabilities = new List<Vulnerability>(request.PolicyFindings.Length);
|
||||
foreach (var finding in request.PolicyFindings)
|
||||
{
|
||||
if (!graph.ComponentMap.TryGetValue(finding.ComponentKey, out var component))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!componentKeys.Contains(component.Identity.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var ratings = BuildRatings(finding.Score);
|
||||
var properties = BuildVulnerabilityProperties(finding);
|
||||
|
||||
var vulnerability = new Vulnerability
|
||||
{
|
||||
BomRef = finding.FindingId,
|
||||
Id = finding.VulnerabilityId ?? finding.FindingId,
|
||||
Source = new Source { Name = "StellaOps.Policy" },
|
||||
Affects = new List<Affects>
|
||||
{
|
||||
new() { Ref = component.Identity.Key }
|
||||
},
|
||||
Ratings = ratings,
|
||||
Properties = properties,
|
||||
};
|
||||
|
||||
vulnerabilities.Add(vulnerability);
|
||||
}
|
||||
|
||||
return vulnerabilities.Count == 0 ? null : vulnerabilities;
|
||||
}
|
||||
|
||||
private static List<Rating>? BuildRatings(double score)
|
||||
{
|
||||
if (double.IsNaN(score) || double.IsInfinity(score))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new List<Rating>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Method = ScoreMethod.Other,
|
||||
Justification = "StellaOps Policy score",
|
||||
Score = score,
|
||||
Severity = Severity.Unknown,
|
||||
Source = new Source { Name = "StellaOps.Policy" },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static List<Property>? BuildVulnerabilityProperties(SbomPolicyFinding finding)
|
||||
{
|
||||
var properties = new List<Property>();
|
||||
|
||||
AddStringProperty(properties, "stellaops:policy.status", finding.Status);
|
||||
AddStringProperty(properties, "stellaops:policy.configVersion", finding.ConfigVersion);
|
||||
AddBooleanProperty(properties, "stellaops:policy.quiet", finding.Quiet);
|
||||
AddStringProperty(properties, "stellaops:policy.quietedBy", finding.QuietedBy);
|
||||
AddStringProperty(properties, "stellaops:policy.confidenceBand", finding.ConfidenceBand);
|
||||
AddStringProperty(properties, "stellaops:policy.sourceTrust", finding.SourceTrust);
|
||||
AddStringProperty(properties, "stellaops:policy.reachability", finding.Reachability);
|
||||
AddDoubleProperty(properties, "stellaops:policy.score", finding.Score);
|
||||
AddNullableDoubleProperty(properties, "stellaops:policy.unknownConfidence", finding.UnknownConfidence);
|
||||
AddNullableDoubleProperty(properties, "stellaops:policy.unknownAgeDays", finding.UnknownAgeDays);
|
||||
|
||||
if (!finding.Inputs.IsDefaultOrEmpty && finding.Inputs.Length > 0)
|
||||
{
|
||||
foreach (var (key, value) in finding.Inputs)
|
||||
{
|
||||
AddDoubleProperty(properties, $"stellaops:policy.input.{key}", value);
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
properties.Sort(static (left, right) => StringComparer.Ordinal.Compare(left.Name, right.Name));
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static void AddStringProperty(ICollection<Property> properties, string name, string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties.Add(new Property
|
||||
{
|
||||
Name = name,
|
||||
Value = value.Trim(),
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddBooleanProperty(ICollection<Property> properties, string name, bool value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties.Add(new Property
|
||||
{
|
||||
Name = name,
|
||||
Value = value ? "true" : "false",
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddDoubleProperty(ICollection<Property> properties, string name, double value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name) || double.IsNaN(value) || double.IsInfinity(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
properties.Add(new Property
|
||||
{
|
||||
Name = name,
|
||||
Value = FormatDouble(value),
|
||||
});
|
||||
}
|
||||
|
||||
private static void AddNullableDoubleProperty(ICollection<Property> properties, string name, double? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AddDoubleProperty(properties, name, value.Value);
|
||||
}
|
||||
|
||||
private static Component.Classification MapClassification(string? type)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(type))
|
||||
@@ -372,7 +549,7 @@ public sealed class CycloneDxComposer
|
||||
"application" => Component.Classification.Application,
|
||||
"framework" => Component.Classification.Framework,
|
||||
"container" => Component.Classification.Container,
|
||||
"operating-system" or "os" => Component.Classification.OperationSystem,
|
||||
"operating-system" or "os" => Component.Classification.Operating_System,
|
||||
"device" => Component.Classification.Device,
|
||||
"firmware" => Component.Classification.Firmware,
|
||||
"file" => Component.Classification.File,
|
||||
@@ -396,6 +573,9 @@ public sealed class CycloneDxComposer
|
||||
};
|
||||
}
|
||||
|
||||
private static string FormatDouble(double value)
|
||||
=> value.ToString("0.############################", CultureInfo.InvariantCulture);
|
||||
|
||||
private static string ComputeSha256(byte[] bytes)
|
||||
{
|
||||
using var sha256 = SHA256.Create();
|
||||
|
||||
@@ -42,13 +42,17 @@ public sealed record SbomCompositionRequest
|
||||
public IReadOnlyDictionary<string, string>? AdditionalProperties { get; init; }
|
||||
= null;
|
||||
|
||||
public ImmutableArray<SbomPolicyFinding> PolicyFindings { get; init; }
|
||||
= ImmutableArray<SbomPolicyFinding>.Empty;
|
||||
|
||||
public static SbomCompositionRequest Create(
|
||||
ImageArtifactDescriptor image,
|
||||
IEnumerable<LayerComponentFragment> fragments,
|
||||
DateTimeOffset generatedAt,
|
||||
string? generatorName = null,
|
||||
string? generatorVersion = null,
|
||||
IReadOnlyDictionary<string, string>? properties = null)
|
||||
IReadOnlyDictionary<string, string>? properties = null,
|
||||
IEnumerable<SbomPolicyFinding>? policyFindings = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
ArgumentNullException.ThrowIfNull(fragments);
|
||||
@@ -70,6 +74,7 @@ public sealed record SbomCompositionRequest
|
||||
GeneratorName = Normalize(generatorName),
|
||||
GeneratorVersion = Normalize(generatorVersion),
|
||||
AdditionalProperties = properties,
|
||||
PolicyFindings = NormalizePolicyFindings(policyFindings),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -82,4 +87,50 @@ public sealed record SbomCompositionRequest
|
||||
|
||||
return value.Trim();
|
||||
}
|
||||
|
||||
private static ImmutableArray<SbomPolicyFinding> NormalizePolicyFindings(IEnumerable<SbomPolicyFinding>? policyFindings)
|
||||
{
|
||||
if (policyFindings is null)
|
||||
{
|
||||
return ImmutableArray<SbomPolicyFinding>.Empty;
|
||||
}
|
||||
|
||||
var builder = ImmutableArray.CreateBuilder<SbomPolicyFinding>();
|
||||
foreach (var finding in policyFindings)
|
||||
{
|
||||
if (finding is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SbomPolicyFinding normalized;
|
||||
try
|
||||
{
|
||||
normalized = finding.Normalize();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(normalized.FindingId) || string.IsNullOrWhiteSpace(normalized.ComponentKey))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Add(normalized);
|
||||
}
|
||||
|
||||
if (builder.Count == 0)
|
||||
{
|
||||
return ImmutableArray<SbomPolicyFinding>.Empty;
|
||||
}
|
||||
|
||||
return builder
|
||||
.ToImmutable()
|
||||
.OrderBy(static finding => finding.FindingId, StringComparer.Ordinal)
|
||||
.ThenBy(static finding => finding.ComponentKey, StringComparer.Ordinal)
|
||||
.ThenBy(static finding => finding.VulnerabilityId ?? string.Empty, StringComparer.Ordinal)
|
||||
.ToImmutableArray();
|
||||
}
|
||||
}
|
||||
|
||||
65
src/StellaOps.Scanner.Emit/Composition/SbomPolicyFinding.cs
Normal file
65
src/StellaOps.Scanner.Emit/Composition/SbomPolicyFinding.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
||||
namespace StellaOps.Scanner.Emit.Composition;
|
||||
|
||||
public sealed record SbomPolicyFinding
|
||||
{
|
||||
public required string FindingId { get; init; }
|
||||
|
||||
public required string ComponentKey { get; init; }
|
||||
|
||||
public string? VulnerabilityId { get; init; }
|
||||
|
||||
public string Status { get; init; } = string.Empty;
|
||||
|
||||
public double Score { get; init; }
|
||||
|
||||
public string ConfigVersion { get; init; } = string.Empty;
|
||||
|
||||
public ImmutableArray<KeyValuePair<string, double>> Inputs { get; init; } = ImmutableArray<KeyValuePair<string, double>>.Empty;
|
||||
|
||||
public string? QuietedBy { get; init; }
|
||||
|
||||
public bool Quiet { get; init; }
|
||||
|
||||
public double? UnknownConfidence { get; init; }
|
||||
|
||||
public string? ConfidenceBand { get; init; }
|
||||
|
||||
public double? UnknownAgeDays { get; init; }
|
||||
|
||||
public string? SourceTrust { get; init; }
|
||||
|
||||
public string? Reachability { get; init; }
|
||||
|
||||
internal SbomPolicyFinding Normalize()
|
||||
{
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(FindingId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(ComponentKey);
|
||||
|
||||
var normalizedInputs = Inputs.IsDefaultOrEmpty
|
||||
? ImmutableArray<KeyValuePair<string, double>>.Empty
|
||||
: Inputs
|
||||
.Where(static pair => !string.IsNullOrWhiteSpace(pair.Key))
|
||||
.Select(static pair => new KeyValuePair<string, double>(pair.Key.Trim(), pair.Value))
|
||||
.OrderBy(static pair => pair.Key, StringComparer.Ordinal)
|
||||
.ToImmutableArray();
|
||||
|
||||
return this with
|
||||
{
|
||||
FindingId = FindingId.Trim(),
|
||||
ComponentKey = ComponentKey.Trim(),
|
||||
VulnerabilityId = string.IsNullOrWhiteSpace(VulnerabilityId) ? null : VulnerabilityId.Trim(),
|
||||
Status = string.IsNullOrWhiteSpace(Status) ? string.Empty : Status.Trim(),
|
||||
ConfigVersion = string.IsNullOrWhiteSpace(ConfigVersion) ? string.Empty : ConfigVersion.Trim(),
|
||||
QuietedBy = string.IsNullOrWhiteSpace(QuietedBy) ? null : QuietedBy.Trim(),
|
||||
ConfidenceBand = string.IsNullOrWhiteSpace(ConfidenceBand) ? null : ConfidenceBand.Trim(),
|
||||
SourceTrust = string.IsNullOrWhiteSpace(SourceTrust) ? null : SourceTrust.Trim(),
|
||||
Reachability = string.IsNullOrWhiteSpace(Reachability) ? null : Reachability.Trim(),
|
||||
Inputs = normalizedInputs
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CycloneDX.Core" Version="5.1.0" />
|
||||
<PackageReference Include="CycloneDX.Core" Version="10.0.1" />
|
||||
<PackageReference Include="RoaringBitmap" Version="0.0.9" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
||||
|----|--------|----------|------------|-------------|---------------|
|
||||
| SCANNER-EMIT-10-601 | DOING (2025-10-19) | Emit Guild | SCANNER-CACHE-10-101 | Compose inventory SBOM (CycloneDX JSON/Protobuf) from layer fragments with deterministic ordering. | Inventory SBOM validated against schema; fixtures confirm deterministic output. |
|
||||
| SCANNER-EMIT-10-602 | DOING (2025-10-19) | Emit Guild | SCANNER-EMIT-10-601 | Compose usage SBOM leveraging EntryTrace to flag actual usage; ensure separate view toggles. | Usage SBOM tests confirm correct subset; API contract documented. |
|
||||
| SCANNER-EMIT-10-603 | DOING (2025-10-19) | Emit Guild | SCANNER-EMIT-10-601 | Generate BOM index sidecar (purl table + roaring bitmap + usedByEntrypoint flag). | Index format validated; query helpers proven; stored artifacts hashed deterministically. |
|
||||
| SCANNER-EMIT-10-604 | DOING (2025-10-19) | Emit Guild | SCANNER-EMIT-10-602 | Package artifacts for export + attestation (naming, compression, manifests). | Export pipeline produces deterministic file paths/hashes; integration test with storage passes. |
|
||||
| SCANNER-EMIT-10-605 | DOING (2025-10-19) | Emit Guild | SCANNER-EMIT-10-603 | Emit BOM-Index sidecar schema/fixtures (`bom-index@1`) and note CRITICAL PATH for Scheduler. | Schema + fixtures in docs/artifacts/bom-index; tests `BOMIndexGoldenIsStable` green. |
|
||||
| SCANNER-EMIT-10-606 | DOING (2025-10-19) | Emit Guild | SCANNER-EMIT-10-605 | Integrate EntryTrace usage flags into BOM-Index; document semantics. | Usage bits present in sidecar; integration tests with EntryTrace fixtures pass. |
|
||||
| SCANNER-EMIT-10-601 | DONE (2025-10-22) | Emit Guild | SCANNER-CACHE-10-101 | Compose inventory SBOM (CycloneDX JSON/Protobuf) from layer fragments with deterministic ordering. | Inventory SBOM validated against schema; fixtures confirm deterministic output. |
|
||||
| SCANNER-EMIT-10-602 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-601 | Compose usage SBOM leveraging EntryTrace to flag actual usage; ensure separate view toggles. | Usage SBOM tests confirm correct subset; API contract documented. |
|
||||
| SCANNER-EMIT-10-603 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-601 | Generate BOM index sidecar (purl table + roaring bitmap + usedByEntrypoint flag). | Index format validated; query helpers proven; stored artifacts hashed deterministically. |
|
||||
| SCANNER-EMIT-10-604 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-602 | Package artifacts for export + attestation (naming, compression, manifests). | Export pipeline produces deterministic file paths/hashes; integration test with storage passes. |
|
||||
| SCANNER-EMIT-10-605 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-603 | Emit BOM-Index sidecar schema/fixtures (`bom-index@1`) and note CRITICAL PATH for Scheduler. | Schema + fixtures in docs/artifacts/bom-index; tests `BOMIndexGoldenIsStable` green. |
|
||||
| SCANNER-EMIT-10-606 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-605 | Integrate EntryTrace usage flags into BOM-Index; document semantics. | Usage bits present in sidecar; integration tests with EntryTrace fixtures pass. |
|
||||
| SCANNER-EMIT-17-701 | TODO | Emit Guild, Native Analyzer Guild | SCANNER-EMIT-10-602 | Record GNU build-id for ELF components and surface it in inventory/usage SBOM plus diff payloads with deterministic ordering. | Native analyzer emits buildId for every ELF executable/library, SBOM/diff fixtures updated with canonical `buildId` field, regression tests prove stability, docs call out debug-symbol lookup flow. |
|
||||
| SCANNER-EMIT-10-607 | TODO | Emit Guild | SCANNER-EMIT-10-604, POLICY-CORE-09-005 | Embed scoring inputs, confidence band, and `quietedBy` provenance into CycloneDX 1.6 and DSSE predicates; verify deterministic serialization. | SBOM/attestation fixtures include score, inputs, configVersion, quiet metadata; golden tests confirm canonical output. |
|
||||
| SCANNER-EMIT-10-607 | DONE (2025-10-22) | Emit Guild | SCANNER-EMIT-10-604, POLICY-CORE-09-005 | Embed scoring inputs, confidence band, and `quietedBy` provenance into CycloneDX 1.6 and DSSE predicates; verify deterministic serialization. | SBOM/attestation fixtures include score, inputs, configVersion, quiet metadata; golden tests confirm canonical output. |
|
||||
|
||||
@@ -61,7 +61,8 @@ public static class TelemetryExtensions
|
||||
metrics
|
||||
.AddMeter(
|
||||
ScannerWorkerInstrumentation.MeterName,
|
||||
"StellaOps.Scanner.Analyzers.Lang.Node")
|
||||
"StellaOps.Scanner.Analyzers.Lang.Node",
|
||||
"StellaOps.Scanner.Analyzers.Lang.Go")
|
||||
.AddRuntimeInstrumentation()
|
||||
.AddProcessInstrumentation();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user