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 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 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 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
|
### 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 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
|
### 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.
|
- 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
|
- **Sprint 10** · Backlog
|
||||||
- Team: TBD
|
- Team: TBD
|
||||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
|
- 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)
|
• 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`
|
- Path: `src/StellaOps.Scanner.Analyzers.Lang.Go/TASKS.md`
|
||||||
1. [TODO] SCANNER-ANALYZERS-LANG-10-308G — Determinism fixtures + benchmark harness (Vs competitor).
|
1. [TODO] SCANNER-ANALYZERS-LANG-10-308G — Determinism fixtures + benchmark harness (Vs competitor).
|
||||||
• Prereqs: SCANNER-ANALYZERS-LANG-10-307G (Wave 4)
|
• 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
|
- **Sprint 10** · Backlog
|
||||||
- Team: TBD
|
- Team: TBD
|
||||||
- Path: `src/StellaOps.Scanner.Analyzers.Lang.DotNet/TASKS.md`
|
- 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)
|
• 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`
|
- 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.
|
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)
|
• 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-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-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/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-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-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. |
|
| 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-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-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.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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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-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 | 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 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. |
|
| 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 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/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 | 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.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using StellaOps.Scanner.Analyzers.Lang;
|
using StellaOps.Scanner.Analyzers.Lang;
|
||||||
|
using StellaOps.Scanner.Analyzers.Lang.Go;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.Java;
|
using StellaOps.Scanner.Analyzers.Lang.Java;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.Node;
|
using StellaOps.Scanner.Analyzers.Lang.Node;
|
||||||
|
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||||
|
|
||||||
namespace StellaOps.Bench.ScannerAnalyzers.Scenarios;
|
namespace StellaOps.Bench.ScannerAnalyzers.Scenarios;
|
||||||
|
|
||||||
@@ -104,7 +106,9 @@ internal sealed class LanguageAnalyzerScenarioRunner : IScenarioRunner
|
|||||||
return id switch
|
return id switch
|
||||||
{
|
{
|
||||||
"java" => static () => new JavaLanguageAnalyzer(),
|
"java" => static () => new JavaLanguageAnalyzer(),
|
||||||
|
"go" => static () => new GoLanguageAnalyzer(),
|
||||||
"node" => static () => new NodeLanguageAnalyzer(),
|
"node" => static () => new NodeLanguageAnalyzer(),
|
||||||
|
"dotnet" => static () => new DotNetLanguageAnalyzer(),
|
||||||
_ => throw new InvalidOperationException($"Unsupported analyzer '{analyzerId}'."),
|
_ => throw new InvalidOperationException($"Unsupported analyzer '{analyzerId}'."),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang\StellaOps.Scanner.Analyzers.Lang.csproj" />
|
<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.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.Java\StellaOps.Scanner.Analyzers.Lang.Java.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\src\StellaOps.Scanner.Analyzers.Lang.DotNet\StellaOps.Scanner.Analyzers.Lang.DotNet.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
|
scenario,iterations,sample_count,mean_ms,p95_ms,max_ms
|
||||||
node_monorepo_walk,5,4,4.2314,15.3277,18.9984
|
node_monorepo_walk,5,4,9.4303,36.1354,45.0012
|
||||||
java_demo_archive,5,1,4.5572,17.3489,21.5472
|
java_demo_archive,5,1,20.6964,81.5592,101.7846
|
||||||
python_site_packages_walk,5,3,2.0049,6.4230,7.8832
|
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,11 +18,27 @@
|
|||||||
"java"
|
"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",
|
"id": "python_site_packages_walk",
|
||||||
"label": "Python site-packages dist-info crawl",
|
"label": "Python site-packages dist-info crawl",
|
||||||
"root": "samples/runtime/python-venv/lib/python3.11/site-packages",
|
"root": "samples/runtime/python-venv/lib/python3.11/site-packages",
|
||||||
"matcher": "**/*.dist-info/METADATA",
|
"matcher": "**/*.dist-info/METADATA",
|
||||||
"parser": "python"
|
"parser": "python"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -3,10 +3,23 @@
|
|||||||
This directory will capture benchmark results for language analyzers (Node, Python, Go, .NET, Rust).
|
This directory will capture benchmark results for language analyzers (Node, Python, Go, .NET, Rust).
|
||||||
|
|
||||||
Pending tasks:
|
Pending tasks:
|
||||||
- LA1: Node analyzer microbench CSV + flamegraph.
|
- LA1: Node analyzer microbench CSV + flamegraph.
|
||||||
- LA2: Python hash throughput CSV.
|
- LA2: Python hash throughput CSV.
|
||||||
- LA3: Go build info extraction benchmarks.
|
- LA3: Go build info extraction benchmarks.
|
||||||
- LA4: .NET RID dedupe performance matrix.
|
- LA4: .NET RID dedupe performance matrix.
|
||||||
- LA5: Rust heuristic coverage comparisons.
|
- LA5: Rust heuristic coverage comparisons.
|
||||||
|
|
||||||
Results should be committed as deterministic CSV/JSON outputs with accompanying methodology notes.
|
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 |
|
| **Provenance** | Cosign signature, SPDX 2.3 SBOM, in‑toto SLSA attestation |
|
||||||
| **Attested manifest** | `offline-manifest.json` + detached JWS covering bundle metadata, signed during export. |
|
| **Attested manifest** | `offline-manifest.json` + detached JWS covering bundle metadata, signed during export. |
|
||||||
| **Delta patches** | Daily diff bundles keep size \< 350 MB |
|
| **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.
|
**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 }}**.
|
*Scanner core:* C# 12 on **.NET {{ dotnet }}**.
|
||||||
*Imports are idempotent and atomic — no service downtime.*
|
*Imports are idempotent and atomic — no service downtime.*
|
||||||
@@ -59,12 +59,53 @@ 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.
|
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:
|
||||||
|
|
||||||
## 2 · Import on the air‑gapped host
|
```json
|
||||||
|
{
|
||||||
```bash
|
"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
|
||||||
|
|
||||||
|
```bash
|
||||||
docker compose --env-file .env \
|
docker compose --env-file .env \
|
||||||
-f docker-compose.stella-ops.yml \
|
-f docker-compose.stella-ops.yml \
|
||||||
exec stella-ops \
|
exec stella-ops \
|
||||||
@@ -81,14 +122,22 @@ stellaops-cli offline kit import stella-ops-offline-kit-<DATE>.tgz \
|
|||||||
```
|
```
|
||||||
|
|
||||||
The CLI validates recorded digests (when `.metadata.json` is present) before streaming the multipart payload to `/api/offline-kit/import`.
|
The CLI validates recorded digests (when `.metadata.json` is present) before streaming the multipart payload to `/api/offline-kit/import`.
|
||||||
|
|
||||||
* The CLI validates the Cosign signature **before** activation.
|
* The CLI validates the Cosign signature **before** activation.
|
||||||
* Old feeds are kept until the new bundle is fully verified.
|
* Old feeds are kept until the new bundle is fully verified.
|
||||||
* Import time on a SATA SSD: ≈ 25 s for a 300 MB kit.
|
* 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:
|
||||||
|
|
||||||
## 3 · Delta patch workflow
|
```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
|
||||||
|
|
||||||
1. **Connected site** fetches `stella-ouk-YYYY‑MM‑DD.delta.tgz`.
|
1. **Connected site** fetches `stella-ouk-YYYY‑MM‑DD.delta.tgz`.
|
||||||
2. Transfer via any medium (USB, portable disk).
|
2. Transfer via any medium (USB, portable disk).
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ scanner:
|
|||||||
* `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total`
|
* `scanner.layer_cache_hits_total`, `scanner.file_cas_hits_total`
|
||||||
* `scanner.artifact_bytes_total{format}`
|
* `scanner.artifact_bytes_total{format}`
|
||||||
* `scanner.attestation_latency_seconds`, `scanner.rekor_failures_total`
|
* `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.
|
* **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.
|
* **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.
|
- 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.
|
- 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.
|
- 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:
|
Next steps:
|
||||||
- Concelier WebService owners to link this update in the next deployment bulletin once FEEDWEB-DOCS-01-001 clears review.
|
- 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-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-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 System;
|
||||||
using CycloneDX.Models;
|
using CycloneDX.Models;
|
||||||
|
|
||||||
var dependenciesProperty = typeof(Dependency).GetProperty("Dependencies")!;
|
Console.WriteLine(string.Join(", ", Enum.GetNames(typeof(Component.Classification))));
|
||||||
Console.WriteLine(dependenciesProperty.PropertyType);
|
|
||||||
Console.WriteLine(dependenciesProperty.PropertyType.GenericTypeArguments[0]);
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CycloneDX.Core" Version="5.1.0" />
|
<PackageReference Include="CycloneDX.Core" Version="10.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,39 +8,45 @@
|
|||||||
"revisionId": "rev-1",
|
"revisionId": "rev-1",
|
||||||
"digest": "27d2ec2b34feedc304fc564d252ecee1c8fa14ea581a5ff5c1ea8963313d5c8d"
|
"digest": "27d2ec2b34feedc304fc564d252ecee1c8fa14ea581a5ff5c1ea8963313d5c8d"
|
||||||
},
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"total": 1,
|
"total": 1,
|
||||||
"blocked": 1,
|
"blocked": 1,
|
||||||
"warned": 0,
|
"warned": 0,
|
||||||
"ignored": 0,
|
"ignored": 0,
|
||||||
"quieted": 0
|
"quieted": 1
|
||||||
},
|
},
|
||||||
"verdicts": [
|
"verdicts": [
|
||||||
{
|
{
|
||||||
"findingId": "finding-1",
|
"findingId": "finding-1",
|
||||||
"status": "Blocked",
|
"status": "Blocked",
|
||||||
"ruleName": "Block Critical",
|
"ruleName": "Block Critical",
|
||||||
"ruleAction": "Block",
|
"ruleAction": "Block",
|
||||||
"score": 40.5,
|
"score": 40.5,
|
||||||
"configVersion": "1.0",
|
"configVersion": "1.0",
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"reachabilityWeight": 0.45,
|
"reachabilityWeight": 0.45,
|
||||||
"baseScore": 40.5,
|
"baseScore": 40.5,
|
||||||
"severityWeight": 90,
|
"severityWeight": 90,
|
||||||
"trustWeight": 1,
|
"trustWeight": 1,
|
||||||
"trustWeight.NVD": 1,
|
"trustWeight.NVD": 1,
|
||||||
"reachability.runtime": 0.45
|
"reachability.runtime": 0.45,
|
||||||
},
|
"unknownConfidence": 0.52,
|
||||||
"quiet": false,
|
"unknownAgeDays": 4
|
||||||
"sourceTrust": "NVD",
|
},
|
||||||
"reachability": "runtime"
|
"quietedBy": "policy/quiet-critical-runtime",
|
||||||
}
|
"quiet": true,
|
||||||
|
"unknownConfidence": 0.52,
|
||||||
|
"confidenceBand": "medium",
|
||||||
|
"unknownAgeDays": 4,
|
||||||
|
"sourceTrust": "NVD",
|
||||||
|
"reachability": "runtime"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"issues": []
|
"issues": []
|
||||||
},
|
},
|
||||||
"dsse": {
|
"dsse": {
|
||||||
"payloadType": "application/vnd.stellaops.report+json",
|
"payloadType": "application/vnd.stellaops.report+json",
|
||||||
"payload": "eyJyZXBvcnRJZCI6InJlcG9ydC0zZGVmNWYzNjJhYTQ3NWVmMTRiNiIsImltYWdlRGlnZXN0Ijoic2hhMjU2OmRlYWRiZWVmIiwiZ2VuZXJhdGVkQXQiOiIyMDI1LTEwLTE5VDA4OjI4OjA5LjM2OTkyNjcrMDA6MDAiLCJ2ZXJkaWN0IjoiYmxvY2tlZCIsInBvbGljeSI6eyJyZXZpc2lvbklkIjoicmV2LTEiLCJkaWdlc3QiOiIyN2QyZWMyYjM0ZmVlZGMzMDRmYzU2NGQyNTJlY2VlMWM4ZmExNGVhNTgxYTVmZjVjMWVhODk2MzMxM2Q1YzhkIn0sInN1bW1hcnkiOnsidG90YWwiOjEsImJsb2NrZWQiOjEsIndhcm5lZCI6MCwiaWdub3JlZCI6MCwicXVpZXRlZCI6MH0sInZlcmRpY3RzIjpbeyJmaW5kaW5nSWQiOiJmaW5kaW5nLTEiLCJzdGF0dXMiOiJCbG9ja2VkIiwicnVsZU5hbWUiOiJCbG9jayBDcml0aWNhbCIsInJ1bGVBY3Rpb24iOiJCbG9jayIsInNjb3JlIjo0MC41LCJjb25maWdWZXJzaW9uIjoiMS4wIiwiaW5wdXRzIjp7InJlYWNoYWJpbGl0eVdlaWdodCI6MC40NSwiYmFzZVNjb3JlIjo0MC41LCJzZXZlcml0eVdlaWdodCI6OTAsInRydXN0V2VpZ2h0IjoxLCJ0cnVzdFdlaWdodC5OVkQiOjEsInJlYWNoYWJpbGl0eS5ydW50aW1lIjowLjQ1fSwicXVpZXQiOmZhbHNlLCJzb3VyY2VUcnVzdCI6Ik5WRCIsInJlYWNoYWJpbGl0eSI6InJ1bnRpbWUifV0sImlzc3VlcyI6W119",
|
"payload": "eyJyZXBvcnRJZCI6InJlcG9ydC0zZGVmNWYzNjJhYTQ3NWVmMTRiNiIsImltYWdlRGlnZXN0Ijoic2hhMjU2OmRlYWRiZWVmIiwiZ2VuZXJhdGVkQXQiOiIyMDI1LTEwLTE5VDA4OjI4OjA5LjM2OTkyNjcrMDA6MDAiLCJ2ZXJkaWN0IjoiYmxvY2tlZCIsInBvbGljeSI6eyJyZXZpc2lvbklkIjoicmV2LTEiLCJkaWdlc3QiOiIyN2QyZWMyYjM0ZmVlZGMzMDRmYzU2NGQyNTJlY2VlMWM4ZmExNGVhNTgxYTVmZjVjMWVhODk2MzMxM2Q1YzhkIn0sInN1bW1hcnkiOnsidG90YWwiOjEsImJsb2NrZWQiOjEsIndhcm5lZCI6MCwiaWdub3JlZCI6MCwicXVpZXRlZCI6MX0sInZlcmRpY3RzIjpbeyJmaW5kaW5nSWQiOiJmaW5kaW5nLTEiLCJzdGF0dXMiOiJCbG9ja2VkIiwicnVsZU5hbWUiOiJCbG9jayBDcml0aWNhbCIsInJ1bGVBY3Rpb24iOiJCbG9jayIsInNjb3JlIjo0MC41LCJjb25maWdWZXJzaW9uIjoiMS4wIiwiaW5wdXRzIjp7InJlYWNoYWJpbGl0eVdlaWdodCI6MC40NSwiYmFzZVNjb3JlIjo0MC41LCJzZXZlcml0eVdlaWdodCI6OTAsInRydXN0V2VpZ2h0IjoxLCJ0cnVzdFdlaWdodC5OVkQiOjEsInJlYWNoYWJpbGl0eS5ydW50aW1lIjowLjQ1LCJ1bmtub3duQ29uZmlkZW5jZSI6MC41MiwidW5rbm93bkFnZURheXMiOjR9LCJxdWlldGVkQnkiOiJwb2xpY3kvcXVpZXQtY3JpdGljYWwtcnVudGltZSIsInF1aWV0Ijp0cnVlLCJ1bmtub3duQ29uZmlkZW5jZSI6MC41MiwiY29uZmlkZW5jZUJhbmQiOiJtZWRpdW0iLCJ1bmtub3duQWdlRGF5cyI6NCwic291cmNlVHJ1c3QiOiJOVkQiLCJyZWFjaGFiaWxpdHkiOiJydW50aW1lIn1dLCJpc3N1ZXMiOltdfQ==",
|
||||||
"signatures": [
|
"signatures": [
|
||||||
{
|
{
|
||||||
"keyId": "scanner-report-signing",
|
"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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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 | 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. |
|
| 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",
|
"analyzerId": "golang",
|
||||||
"componentKey": "golang::bin::sha256:80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3",
|
"componentKey": "golang::bin::sha256:7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"type": "bin",
|
"type": "bin",
|
||||||
"usedByEntrypoint": false,
|
"usedByEntrypoint": false,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"binary.sha256": "80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3",
|
"binary.sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99",
|
||||||
"binaryPath": "app",
|
"binaryPath": "app",
|
||||||
"go.version.hint": "go1.22.8",
|
"go.version.hint": "go1.22.8",
|
||||||
"languageHint": "golang",
|
"languageHint": "golang",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"kind": "file",
|
"kind": "file",
|
||||||
"source": "binary",
|
"source": "binary",
|
||||||
"locator": "app",
|
"locator": "app",
|
||||||
"sha256": "80f528c90b72a4c4cc3fa078501154e4f2a3f49faea3ec380112d61740bde4c3"
|
"sha256": "7125d65230b913faa744a33acd884899c81a1dbc6d88cbf251a74b19621cde99"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kind": "metadata",
|
"kind": "metadata",
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.Metrics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.Go;
|
using StellaOps.Scanner.Analyzers.Lang.Go;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
|
||||||
@@ -63,4 +66,69 @@ public sealed class GoLanguageAnalyzerTests
|
|||||||
analyzers,
|
analyzers,
|
||||||
cancellationToken);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,17 @@ Build the Go analyzer plug-in that reads Go build info, module metadata, and DWA
|
|||||||
- Policy decisions or vulnerability joins.
|
- Policy decisions or vulnerability joins.
|
||||||
|
|
||||||
## Expectations
|
## Expectations
|
||||||
- Latency targets: ≤400 µs (hot) / ≤2 ms (cold) per binary; minimal allocations via buffer pooling.
|
- Latency targets: ≤400 µs (hot) / ≤2 ms (cold) per binary; minimal allocations via buffer pooling.
|
||||||
- Deterministic fallback to `bin:{sha256}` when metadata absent; heuristics clearly identified.
|
- Shared buffer pooling via `ArrayPool<byte>` for build-info/DWARF reads; safe for concurrent scans.
|
||||||
- Offline-first: rely solely on embedded metadata.
|
- Deterministic fallback to `bin:{sha256}` when metadata absent; heuristics clearly identified.
|
||||||
- Telemetry for binaries processed, metadata coverage, heuristics usage.
|
- Offline-first: rely solely on embedded metadata.
|
||||||
|
- Telemetry for binaries processed, metadata coverage, heuristics usage.
|
||||||
## Dependencies
|
- Heuristic fallback metrics: `scanner_analyzer_golang_heuristic_total{indicator,version_hint}` increments whenever stripped binaries are classified via fallbacks.
|
||||||
- Shared language analyzer core; Worker dispatcher; caching infrastructure (layer cache + file CAS).
|
|
||||||
|
## Dependencies
|
||||||
|
- Shared language analyzer core; Worker dispatcher; caching infrastructure (layer cache + file CAS).
|
||||||
|
|
||||||
## Testing & Artifacts
|
## Testing & Artifacts
|
||||||
- Golden fixtures for modules with/without VCS info, stripped binaries, cross-compiled variants.
|
- 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.
|
- ADR documenting heuristics and risk mitigation.
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ public sealed class GoLanguageAnalyzer : ILanguageAnalyzer
|
|||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
evidence: evidence,
|
evidence: evidence,
|
||||||
usedByEntrypoint: usedByEntrypoint);
|
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)
|
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;
|
goVersion = null;
|
||||||
moduleData = null;
|
moduleData = null;
|
||||||
|
|
||||||
|
FileInfo info;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var info = new FileInfo(filePath);
|
info = new FileInfo(filePath);
|
||||||
if (!info.Exists || info.Length < 64 || info.Length > 128 * 1024 * 1024)
|
if (!info.Exists || info.Length < 64 || info.Length > 128 * 1024 * 1024)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (System.Security.SecurityException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var data = File.ReadAllBytes(filePath);
|
var length = info.Length;
|
||||||
var span = new ReadOnlySpan<byte>(data);
|
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);
|
var offset = span.IndexOf(BuildInfoMagic.Span);
|
||||||
if (offset < 0)
|
if (offset < 0)
|
||||||
{
|
{
|
||||||
@@ -65,6 +108,11 @@ internal static class GoBinaryScanner
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Array.Clear(buffer, 0, inspectLength);
|
||||||
|
ArrayPool<byte>.Shared.Return(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryClassifyStrippedBinary(string filePath, out GoStrippedBinaryClassification classification)
|
public static bool TryClassifyStrippedBinary(string filePath, out GoStrippedBinaryClassification classification)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -15,16 +16,10 @@ internal static class GoDwarfReader
|
|||||||
{
|
{
|
||||||
metadata = null;
|
metadata = null;
|
||||||
|
|
||||||
ReadOnlySpan<byte> data;
|
FileInfo fileInfo;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fileInfo = new FileInfo(path);
|
fileInfo = new FileInfo(path);
|
||||||
if (!fileInfo.Exists || fileInfo.Length == 0 || fileInfo.Length > 256 * 1024 * 1024)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = File.ReadAllBytes(path);
|
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
@@ -35,27 +30,62 @@ internal static class GoDwarfReader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var revision = ExtractValue(data, VcsRevisionToken);
|
if (!fileInfo.Exists || fileInfo.Length == 0 || fileInfo.Length > 256 * 1024 * 1024)
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata = new GoDwarfMetadata(system, revision, modified, timestamp);
|
var length = fileInfo.Length;
|
||||||
return true;
|
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)
|
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. |
|
| 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 %. |
|
| 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. |
|
| 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. |
|
| 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 | 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. |
|
| 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 | 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. |
|
| 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 | 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
using StellaOps.Scanner.Analyzers.Lang.DotNet;
|
||||||
@@ -102,6 +103,54 @@ public sealed class DotNetLanguageAnalyzerTests
|
|||||||
Assert.Equal(first, result);
|
Assert.Equal(first, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[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
|
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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.
|
- Tests verifying dual runtimeconfig merge logic.
|
||||||
- Guidance for Policy on license propagation from NuGet metadata.
|
- 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-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)
|
## 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.
|
- **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-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-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-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-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-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. |
|
| 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.NotNull(result.Inventory);
|
||||||
Assert.StartsWith("urn:uuid:", result.Inventory.SerialNumber, StringComparison.Ordinal);
|
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+json; version=1.6", result.Inventory.JsonMediaType);
|
||||||
Assert.Equal("application/vnd.cyclonedx+protobuf; version=1.5", result.Inventory.ProtobufMediaType);
|
Assert.Equal("application/vnd.cyclonedx+protobuf; version=1.6", result.Inventory.ProtobufMediaType);
|
||||||
Assert.Equal(2, result.Inventory.Components.Length);
|
Assert.Equal(2, result.Inventory.Components.Length);
|
||||||
|
|
||||||
Assert.NotNull(result.Usage);
|
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.Single(result.Usage.Components);
|
||||||
Assert.Equal("pkg:npm/a", result.Usage.Components[0].Identity.Key);
|
Assert.Equal("pkg:npm/a", result.Usage.Components[0].Identity.Key);
|
||||||
|
|
||||||
ValidateJson(result.Inventory.JsonBytes, expectedComponentCount: 2, expectedView: "inventory");
|
ValidateJson(result.Inventory.JsonBytes, expectedComponentCount: 2, expectedView: "inventory");
|
||||||
ValidateJson(result.Usage.JsonBytes, expectedComponentCount: 1, expectedView: "usage");
|
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]
|
[Fact]
|
||||||
@@ -109,16 +138,54 @@ public sealed class CycloneDxComposerTests
|
|||||||
Architecture = "amd64",
|
Architecture = "amd64",
|
||||||
};
|
};
|
||||||
|
|
||||||
return SbomCompositionRequest.Create(
|
return SbomCompositionRequest.Create(
|
||||||
image,
|
image,
|
||||||
fragments,
|
fragments,
|
||||||
new DateTimeOffset(2025, 10, 19, 12, 0, 0, TimeSpan.Zero),
|
new DateTimeOffset(2025, 10, 19, 12, 0, 0, TimeSpan.Zero),
|
||||||
generatorName: "StellaOps.Scanner",
|
generatorName: "StellaOps.Scanner",
|
||||||
generatorVersion: "0.10.0",
|
generatorVersion: "0.10.0",
|
||||||
properties: new Dictionary<string, string>
|
properties: new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["stellaops:scanId"] = "scan-1234",
|
["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))
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateJson(byte[] data, int expectedComponentCount, string expectedView)
|
private static void ValidateJson(byte[] data, int expectedComponentCount, string expectedView)
|
||||||
@@ -128,19 +195,20 @@ public sealed class CycloneDxComposerTests
|
|||||||
|
|
||||||
Assert.True(root.TryGetProperty("metadata", out var metadata), "metadata property missing");
|
Assert.True(root.TryGetProperty("metadata", out var metadata), "metadata property missing");
|
||||||
var properties = metadata.GetProperty("properties");
|
var properties = metadata.GetProperty("properties");
|
||||||
var viewProperty = properties.EnumerateArray()
|
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());
|
Assert.Equal(expectedView, viewProperty.GetProperty("value").GetString());
|
||||||
|
|
||||||
var components = root.GetProperty("components").EnumerateArray().ToArray();
|
var components = root.GetProperty("components").EnumerateArray().ToArray();
|
||||||
Assert.Equal(expectedComponentCount, components.Length);
|
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());
|
Assert.Equal(names, names.OrderBy(n => n, StringComparer.Ordinal).ToArray());
|
||||||
|
|
||||||
var firstComponentProperties = components[0].GetProperty("properties").EnumerateArray().ToDictionary(
|
var firstComponentProperties = components[0].GetProperty("properties").EnumerateArray().ToDictionary(
|
||||||
element => element.GetProperty("name").GetString(),
|
element => element.GetProperty("name").GetString()!,
|
||||||
element => element.GetProperty("value").GetString());
|
element => element.GetProperty("value").GetString()!,
|
||||||
|
StringComparer.Ordinal);
|
||||||
|
|
||||||
Assert.Equal("apk", firstComponentProperties["stellaops.os.analyzer"]);
|
Assert.Equal("apk", firstComponentProperties["stellaops.os.analyzer"]);
|
||||||
Assert.Equal("x86_64", firstComponentProperties["stellaops.os.architecture"]);
|
Assert.Equal("x86_64", firstComponentProperties["stellaops.os.architecture"]);
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public sealed class ScannerArtifactPackageBuilderTests
|
|||||||
Assert.Equal(5, root.GetProperty("artifacts").GetArrayLength());
|
Assert.Equal(5, root.GetProperty("artifacts").GetArrayLength());
|
||||||
|
|
||||||
var usageEntry = root.GetProperty("artifacts").EnumerateArray().First(element => element.GetProperty("kind").GetString() == "sbom-usage");
|
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)
|
private static ComponentRecord CreateComponent(string key, string version, string layerDigest, ComponentUsage? usage = null, IReadOnlyDictionary<string, string>? metadata = null)
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ using System.Linq;
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using CycloneDX;
|
using CycloneDX;
|
||||||
using CycloneDX.Models;
|
using CycloneDX.Models;
|
||||||
|
using CycloneDX.Models.Vulnerabilities;
|
||||||
using JsonSerializer = CycloneDX.Json.Serializer;
|
using JsonSerializer = CycloneDX.Json.Serializer;
|
||||||
using ProtoSerializer = CycloneDX.Protobuf.Serializer;
|
using ProtoSerializer = CycloneDX.Protobuf.Serializer;
|
||||||
using StellaOps.Scanner.Core.Contracts;
|
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 static readonly Guid SerialNamespace = new("0d3a422b-6e1b-4d9b-9c35-654b706c97e8");
|
||||||
|
|
||||||
private const string InventoryMediaTypeJson = "application/vnd.cyclonedx+json; version=1.5";
|
private const string InventoryMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6";
|
||||||
private const string UsageMediaTypeJson = "application/vnd.cyclonedx+json; version=1.5; view=usage";
|
private const string UsageMediaTypeJson = "application/vnd.cyclonedx+json; version=1.6; view=usage";
|
||||||
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.5";
|
private const string InventoryMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6";
|
||||||
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.5; view=usage";
|
private const string UsageMediaTypeProtobuf = "application/vnd.cyclonedx+protobuf; version=1.6; view=usage";
|
||||||
|
|
||||||
public SbomCompositionResult Compose(SbomCompositionRequest request)
|
public SbomCompositionResult Compose(SbomCompositionRequest request)
|
||||||
{
|
{
|
||||||
@@ -77,7 +78,7 @@ public sealed class CycloneDxComposer
|
|||||||
string jsonMediaType,
|
string jsonMediaType,
|
||||||
string protobufMediaType)
|
string protobufMediaType)
|
||||||
{
|
{
|
||||||
var bom = BuildBom(request, view, components, generatedAt);
|
var bom = BuildBom(request, graph, view, components, generatedAt);
|
||||||
var json = JsonSerializer.Serialize(bom);
|
var json = JsonSerializer.Serialize(bom);
|
||||||
var jsonBytes = Encoding.UTF8.GetBytes(json);
|
var jsonBytes = Encoding.UTF8.GetBytes(json);
|
||||||
var protobufBytes = ProtoSerializer.Serialize(bom);
|
var protobufBytes = ProtoSerializer.Serialize(bom);
|
||||||
@@ -100,25 +101,32 @@ public sealed class CycloneDxComposer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bom BuildBom(
|
private Bom BuildBom(
|
||||||
SbomCompositionRequest request,
|
SbomCompositionRequest request,
|
||||||
SbomView view,
|
ComponentGraph graph,
|
||||||
ImmutableArray<AggregatedComponent> components,
|
SbomView view,
|
||||||
DateTimeOffset generatedAt)
|
ImmutableArray<AggregatedComponent> components,
|
||||||
{
|
DateTimeOffset generatedAt)
|
||||||
var bom = new Bom
|
{
|
||||||
{
|
var bom = new Bom
|
||||||
SpecVersion = SpecificationVersion.v1_4,
|
{
|
||||||
Version = 1,
|
SpecVersion = SpecificationVersion.v1_6,
|
||||||
Metadata = BuildMetadata(request, view, generatedAt),
|
Version = 1,
|
||||||
Components = BuildComponents(components),
|
Metadata = BuildMetadata(request, view, generatedAt),
|
||||||
Dependencies = BuildDependencies(components),
|
Components = BuildComponents(components),
|
||||||
};
|
Dependencies = BuildDependencies(components),
|
||||||
|
};
|
||||||
var serialPayload = $"{request.Image.ImageDigest}|{view}|{ScannerTimestamps.ToIso8601(generatedAt)}";
|
|
||||||
bom.SerialNumber = $"urn:uuid:{ScannerIdentifiers.CreateDeterministicGuid(SerialNamespace, Encoding.UTF8.GetBytes(serialPayload)).ToString("d", CultureInfo.InvariantCulture)}";
|
var vulnerabilities = BuildVulnerabilities(request, graph, components);
|
||||||
|
if (vulnerabilities is not null)
|
||||||
return bom;
|
{
|
||||||
|
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)}";
|
||||||
|
|
||||||
|
return bom;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Metadata BuildMetadata(SbomCompositionRequest request, SbomView view, DateTimeOffset generatedAt)
|
private static Metadata BuildMetadata(SbomCompositionRequest request, SbomView view, DateTimeOffset generatedAt)
|
||||||
@@ -129,23 +137,11 @@ public sealed class CycloneDxComposer
|
|||||||
Component = BuildMetadataComponent(request.Image),
|
Component = BuildMetadataComponent(request.Image),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(request.GeneratorName))
|
if (request.AdditionalProperties is not null && request.AdditionalProperties.Count > 0)
|
||||||
{
|
{
|
||||||
metadata.Tools = new List<Tool>
|
metadata.Properties = request.AdditionalProperties
|
||||||
{
|
.Where(static pair => !string.IsNullOrWhiteSpace(pair.Key) && pair.Value is not null)
|
||||||
new()
|
.OrderBy(static pair => pair.Key, StringComparer.Ordinal)
|
||||||
{
|
|
||||||
Name = request.GeneratorName,
|
|
||||||
Version = request.GeneratorVersion,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.AdditionalProperties is not null && request.AdditionalProperties.Count > 0)
|
|
||||||
{
|
|
||||||
metadata.Properties = request.AdditionalProperties
|
|
||||||
.Where(static pair => !string.IsNullOrWhiteSpace(pair.Key) && pair.Value is not null)
|
|
||||||
.OrderBy(static pair => pair.Key, StringComparer.Ordinal)
|
|
||||||
.Select(pair => new Property
|
.Select(pair => new Property
|
||||||
{
|
{
|
||||||
Name = pair.Key,
|
Name = pair.Key,
|
||||||
@@ -154,15 +150,33 @@ public sealed class CycloneDxComposer
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.Properties is null)
|
if (metadata.Properties is null)
|
||||||
{
|
{
|
||||||
metadata.Properties = new List<Property>();
|
metadata.Properties = new List<Property>();
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.Properties.Add(new Property
|
if (!string.IsNullOrWhiteSpace(request.GeneratorName))
|
||||||
{
|
{
|
||||||
Name = "stellaops:sbom.view",
|
metadata.Properties.Add(new Property
|
||||||
Value = view.ToString().ToLowerInvariant(),
|
{
|
||||||
|
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",
|
||||||
|
Value = view.ToString().ToLowerInvariant(),
|
||||||
});
|
});
|
||||||
|
|
||||||
return metadata;
|
return metadata;
|
||||||
@@ -357,8 +371,171 @@ public sealed class CycloneDxComposer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return dependencies.Count == 0 ? null : dependencies;
|
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)
|
private static Component.Classification MapClassification(string? type)
|
||||||
{
|
{
|
||||||
@@ -372,7 +549,7 @@ public sealed class CycloneDxComposer
|
|||||||
"application" => Component.Classification.Application,
|
"application" => Component.Classification.Application,
|
||||||
"framework" => Component.Classification.Framework,
|
"framework" => Component.Classification.Framework,
|
||||||
"container" => Component.Classification.Container,
|
"container" => Component.Classification.Container,
|
||||||
"operating-system" or "os" => Component.Classification.OperationSystem,
|
"operating-system" or "os" => Component.Classification.Operating_System,
|
||||||
"device" => Component.Classification.Device,
|
"device" => Component.Classification.Device,
|
||||||
"firmware" => Component.Classification.Firmware,
|
"firmware" => Component.Classification.Firmware,
|
||||||
"file" => Component.Classification.File,
|
"file" => Component.Classification.File,
|
||||||
@@ -396,7 +573,10 @@ public sealed class CycloneDxComposer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ComputeSha256(byte[] bytes)
|
private static string FormatDouble(double value)
|
||||||
|
=> value.ToString("0.############################", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
|
private static string ComputeSha256(byte[] bytes)
|
||||||
{
|
{
|
||||||
using var sha256 = SHA256.Create();
|
using var sha256 = SHA256.Create();
|
||||||
var hash = sha256.ComputeHash(bytes);
|
var hash = sha256.ComputeHash(bytes);
|
||||||
|
|||||||
@@ -39,21 +39,25 @@ public sealed record SbomCompositionRequest
|
|||||||
public string? GeneratorVersion { get; init; }
|
public string? GeneratorVersion { get; init; }
|
||||||
= null;
|
= null;
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, string>? AdditionalProperties { get; init; }
|
public IReadOnlyDictionary<string, string>? AdditionalProperties { get; init; }
|
||||||
= null;
|
= null;
|
||||||
|
|
||||||
public static SbomCompositionRequest Create(
|
public ImmutableArray<SbomPolicyFinding> PolicyFindings { get; init; }
|
||||||
ImageArtifactDescriptor image,
|
= ImmutableArray<SbomPolicyFinding>.Empty;
|
||||||
IEnumerable<LayerComponentFragment> fragments,
|
|
||||||
DateTimeOffset generatedAt,
|
public static SbomCompositionRequest Create(
|
||||||
string? generatorName = null,
|
ImageArtifactDescriptor image,
|
||||||
string? generatorVersion = null,
|
IEnumerable<LayerComponentFragment> fragments,
|
||||||
IReadOnlyDictionary<string, string>? properties = null)
|
DateTimeOffset generatedAt,
|
||||||
{
|
string? generatorName = null,
|
||||||
ArgumentNullException.ThrowIfNull(image);
|
string? generatorVersion = null,
|
||||||
ArgumentNullException.ThrowIfNull(fragments);
|
IReadOnlyDictionary<string, string>? properties = null,
|
||||||
|
IEnumerable<SbomPolicyFinding>? policyFindings = null)
|
||||||
var normalizedImage = new ImageArtifactDescriptor
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(image);
|
||||||
|
ArgumentNullException.ThrowIfNull(fragments);
|
||||||
|
|
||||||
|
var normalizedImage = new ImageArtifactDescriptor
|
||||||
{
|
{
|
||||||
ImageDigest = ScannerIdentifiers.NormalizeDigest(image.ImageDigest) ?? throw new ArgumentException("Image digest is required.", nameof(image)),
|
ImageDigest = ScannerIdentifiers.NormalizeDigest(image.ImageDigest) ?? throw new ArgumentException("Image digest is required.", nameof(image)),
|
||||||
ImageReference = Normalize(image.ImageReference),
|
ImageReference = Normalize(image.ImageReference),
|
||||||
@@ -65,21 +69,68 @@ public sealed record SbomCompositionRequest
|
|||||||
return new SbomCompositionRequest
|
return new SbomCompositionRequest
|
||||||
{
|
{
|
||||||
Image = normalizedImage,
|
Image = normalizedImage,
|
||||||
LayerFragments = fragments.ToImmutableArray(),
|
LayerFragments = fragments.ToImmutableArray(),
|
||||||
GeneratedAt = ScannerTimestamps.Normalize(generatedAt),
|
GeneratedAt = ScannerTimestamps.Normalize(generatedAt),
|
||||||
GeneratorName = Normalize(generatorName),
|
GeneratorName = Normalize(generatorName),
|
||||||
GeneratorVersion = Normalize(generatorVersion),
|
GeneratorVersion = Normalize(generatorVersion),
|
||||||
AdditionalProperties = properties,
|
AdditionalProperties = properties,
|
||||||
};
|
PolicyFindings = NormalizePolicyFindings(policyFindings),
|
||||||
}
|
};
|
||||||
|
}
|
||||||
private static string? Normalize(string? value)
|
|
||||||
{
|
private static string? Normalize(string? value)
|
||||||
|
{
|
||||||
if (string.IsNullOrWhiteSpace(value))
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.Trim();
|
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>
|
||||||
|
|
||||||
<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" />
|
<PackageReference Include="RoaringBitmap" Version="0.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|
| 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-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 | 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-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 | 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-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 | 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-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 | 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-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 | 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-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-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
|
metrics
|
||||||
.AddMeter(
|
.AddMeter(
|
||||||
ScannerWorkerInstrumentation.MeterName,
|
ScannerWorkerInstrumentation.MeterName,
|
||||||
"StellaOps.Scanner.Analyzers.Lang.Node")
|
"StellaOps.Scanner.Analyzers.Lang.Node",
|
||||||
|
"StellaOps.Scanner.Analyzers.Lang.Go")
|
||||||
.AddRuntimeInstrumentation()
|
.AddRuntimeInstrumentation()
|
||||||
.AddProcessInstrumentation();
|
.AddProcessInstrumentation();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user