feat: Add initial implementation of Vulnerability Resolver Jobs
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Created project for StellaOps.Scanner.Analyzers.Native.Tests with necessary dependencies. - Documented roles and guidelines in AGENTS.md for Scheduler module. - Implemented IResolverJobService interface and InMemoryResolverJobService for handling resolver jobs. - Added ResolverBacklogNotifier and ResolverBacklogService for monitoring job metrics. - Developed API endpoints for managing resolver jobs and retrieving metrics. - Defined models for resolver job requests and responses. - Integrated dependency injection for resolver job services. - Implemented ImpactIndexSnapshot for persisting impact index data. - Introduced SignalsScoringOptions for configurable scoring weights in reachability scoring. - Added unit tests for ReachabilityScoringService and RuntimeFactsIngestionService. - Created dotnet-filter.sh script to handle command-line arguments for dotnet. - Established nuget-prime project for managing package downloads.
This commit is contained in:
46
src/SbomService/AGENTS.md
Normal file
46
src/SbomService/AGENTS.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# SBOM Service · AGENTS
|
||||
|
||||
## Roles & Scope
|
||||
- Backend engineer (.NET 10, C# preview) for `src/SbomService/StellaOps.SbomService` and related workers/tests.
|
||||
- Docs/contract maintainer for SBOM service APIs and events under `docs/modules/sbomservice`.
|
||||
- QA automation for `src/SbomService/__Tests` (unit/integration/golden/property).
|
||||
- Working directory: `src/SbomService/**`; docs under `docs/modules/sbomservice/**` when touched by sprint tasks.
|
||||
|
||||
## Required Reading (treat as read before DOING)
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/sbomservice/architecture.md`
|
||||
- Current sprint doc: `docs/implplan/SPRINT_0142_0001_0001_sbomservice.md`
|
||||
|
||||
## Working Agreements
|
||||
- Determinism: stable ordering, seeded randomness, UTC ISO-8601, deterministic pagination cursors; no wall-clock in logic/tests.
|
||||
- Offline-friendly: no hardcoded external endpoints; support air-gap bundles and BYO trust roots.
|
||||
- Observability: structured logs with event ids; counters + OTEL traces guarded by config; include tenant/context ids.
|
||||
- Security: least privilege, validated options, input validation; avoid secrets in code/tests.
|
||||
- Configuration: DI + `IOptions` with validation; env var mappings documented; defaults safe/conservative.
|
||||
- Data: enforce tenant scoping on all queries/APIs; deterministic projections with LNM v1 schema.
|
||||
|
||||
## Testing
|
||||
- Run targeted tests before DONE: `dotnet test src/SbomService/__Tests/StellaOps.SbomService.Tests/StellaOps.SbomService.Tests.csproj -v q` (or filtered) once build churn allows.
|
||||
- Keep fixtures deterministic; avoid live network; prefer in-memory or local test servers.
|
||||
- Add/extend golden/property tests for new endpoints, metrics, and event envelopes.
|
||||
|
||||
## Documentation & Contracts
|
||||
- Update `docs/modules/sbomservice/architecture.md` and linked schema/event docs when APIs/events change.
|
||||
- Keep Link-Not-Merge (LNM) schema alignment; consume fixtures once provided.
|
||||
- Surface decisions/risks in sprint doc and mirror in module docs when behavior changes.
|
||||
|
||||
## Dependencies / Interlocks
|
||||
- LNM v1 fixtures (Cartographer/Core) gate schema freeze and SBOM-SERVICE-21-001.
|
||||
- Orchestrator control signals (pause/throttle/backfill) must be defined before SBOM-ORCH-33/34.
|
||||
- AdvisoryAI/Console consumers rely on stable `/sbom/paths`, `/sbom/versions`, `/console/sboms` contracts.
|
||||
|
||||
## Ready-to-Start Checklist (per task)
|
||||
- Confirm sprint status reflects reality and dependencies are satisfied.
|
||||
- Ensure pagination/ordering is deterministic; add tests when adding/altering queries.
|
||||
- Update sprint Decisions & Risks when contracts shift; add to Execution Log.
|
||||
|
||||
## Allowed Shared Libraries
|
||||
- Only shared libs already referenced by SbomService projects; do not add new cross-module deps without sprint approval.
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using StellaOps.SbomService.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.SbomService.Tests;
|
||||
|
||||
public class SbomEndpointsTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
|
||||
public SbomEndpointsTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory.WithWebHostBuilder(_ => { });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Paths_requires_purl()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/sbom/paths");
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Paths_returns_seeded_paths_with_cursor()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/sbom/paths?purl=pkg:npm/lodash@4.17.21&limit=1");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomPathResult>();
|
||||
payload.Should().NotBeNull();
|
||||
payload!.Paths.Should().HaveCount(1);
|
||||
payload.Purl.Should().Be("pkg:npm/lodash@4.17.21");
|
||||
payload.NextCursor.Should().Be("1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Versions_returns_descending_timeline()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/sbom/versions?artifact=ghcr.io/stellaops/sample-api");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomTimelineResult>();
|
||||
payload.Should().NotBeNull();
|
||||
payload!.Versions.Should().HaveCountGreaterThan(0);
|
||||
payload.Versions.Should().BeInDescendingOrder(v => v.CreatedAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Console_sboms_supports_filters_and_cursor()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var response = await client.GetAsync("/console/sboms?artifact=sample-api&limit=1");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<SbomCatalogResult>();
|
||||
payload.Should().NotBeNull();
|
||||
payload!.Items.Should().HaveCount(1);
|
||||
payload.Items[0].Artifact.Should().Contain("sample-api");
|
||||
payload.NextCursor.Should().Be("1");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Components_lookup_requires_purl_and_paginates()
|
||||
{
|
||||
var client = _factory.CreateClient();
|
||||
|
||||
var bad = await client.GetAsync("/components/lookup");
|
||||
bad.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var response = await client.GetAsync("/components/lookup?purl=pkg:npm/lodash@4.17.21&limit=1");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var payload = await response.Content.ReadFromJsonAsync<ComponentLookupResult>();
|
||||
payload.Should().NotBeNull();
|
||||
payload!.Neighbors.Should().HaveCount(1);
|
||||
payload.Neighbors[0].Purl.Should().Contain("express");
|
||||
payload.NextCursor.Should().Be("1");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Update="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Update="xunit" Version="2.9.2" />
|
||||
<PackageReference Update="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Update="coverlet.collector" Version="6.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../StellaOps.SbomService/StellaOps.SbomService.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,104 +1,272 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.SbomService", "StellaOps.SbomService\StellaOps.SbomService.csproj", "{0D9049C8-1667-4F98-9295-579AD9F3631C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "..\__Libraries\StellaOps.Configuration\StellaOps.Configuration.csproj", "{AF00CFB3-C548-4272-AE91-21720CCA0F51}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions", "..\Authority\StellaOps.Authority\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj", "{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{0D5F8F7D-D66D-4415-956F-F4822AB72D31}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions", "..\Authority\StellaOps.Authority\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj", "{CF8D1B05-BB50-45B9-B956-56380D5B4616}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "..\__Libraries\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{A7F565B4-F79B-471A-BD17-AE6314591345}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.SbomService", "StellaOps.SbomService\StellaOps.SbomService.csproj", "{0D9049C8-1667-4F98-9295-579AD9F3631C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "..\__Libraries\StellaOps.Configuration\StellaOps.Configuration.csproj", "{AF00CFB3-C548-4272-AE91-21720CCA0F51}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Authority.Plugins.Abstractions", "..\Authority\StellaOps.Authority\StellaOps.Authority.Plugins.Abstractions\StellaOps.Authority.Plugins.Abstractions.csproj", "{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{0D5F8F7D-D66D-4415-956F-F4822AB72D31}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Auth.Abstractions", "..\Authority\StellaOps.Authority\StellaOps.Auth.Abstractions\StellaOps.Auth.Abstractions.csproj", "{CF8D1B05-BB50-45B9-B956-56380D5B4616}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "..\__Libraries\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{A7F565B4-F79B-471A-BD17-AE6314591345}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.SbomService.Tests", "StellaOps.SbomService.Tests\StellaOps.SbomService.Tests.csproj", "{5F0FA73A-B13B-4B53-B154-5396F077A3E1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Testing", "..\Concelier\__Libraries\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj", "{9FD5687F-1627-4051-87C7-C6F5FA3C1341}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Connector.Common", "..\Concelier\__Libraries\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj", "{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Storage.Mongo", "..\Concelier\__Libraries\StellaOps.Concelier.Storage.Mongo\StellaOps.Concelier.Storage.Mongo.csproj", "{A9817182-8118-4865-ACBB-B53AA010F64F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Core", "..\Concelier\__Libraries\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj", "{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Models", "..\Concelier\__Libraries\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj", "{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.RawModels", "..\Concelier\__Libraries\StellaOps.Concelier.RawModels\StellaOps.Concelier.RawModels.csproj", "{9969E571-2F75-428F-822D-154A816C8D0F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Concelier.Normalization", "..\Concelier\__Libraries\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj", "{14762A37-48BA-42E8-B6CF-FA1967C58F13}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Ingestion.Telemetry", "..\__Libraries\StellaOps.Ingestion.Telemetry\StellaOps.Ingestion.Telemetry.csproj", "{F921862B-2057-4E57-9765-2C34764BC226}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Provenance.Mongo", "..\__Libraries\StellaOps.Provenance.Mongo\StellaOps.Provenance.Mongo.csproj", "{055EDD0B-F513-40C8-BAC0-80815BCE45E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Plugin", "..\__Libraries\StellaOps.Plugin\StellaOps.Plugin.csproj", "{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Aoc", "..\Aoc\__Libraries\StellaOps.Aoc\StellaOps.Aoc.csproj", "{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0D9049C8-1667-4F98-9295-579AD9F3631C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x64.Build.0 = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{AF00CFB3-C548-4272-AE91-21720CCA0F51}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1D1D07F0-86EE-45FB-B9FA-6D9F7E49770C}.Release|x86.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x64.Build.0 = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{0D5F8F7D-D66D-4415-956F-F4822AB72D31}.Release|x86.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x64.Build.0 = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CF8D1B05-BB50-45B9-B956-56380D5B4616}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7F565B4-F79B-471A-BD17-AE6314591345}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5F0FA73A-B13B-4B53-B154-5396F077A3E1}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9FD5687F-1627-4051-87C7-C6F5FA3C1341}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1383D9F7-10A6-47E3-84CE-8AC9E5E59E25}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A9817182-8118-4865-ACBB-B53AA010F64F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{6684AA9D-3FDA-42ED-A60F-8B10DAD3394B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DA225445-FC3D-429C-A1EE-7B14EB16AE0F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9969E571-2F75-428F-822D-154A816C8D0F}.Release|x86.Build.0 = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|x64.Build.0 = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{14762A37-48BA-42E8-B6CF-FA1967C58F13}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F921862B-2057-4E57-9765-2C34764BC226}.Release|x86.Build.0 = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{055EDD0B-F513-40C8-BAC0-80815BCE45E3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|x64.Build.0 = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{872BE10D-03C8-4F6A-9D4C-F56FFDCC6B16}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DA1297B3-5B0A-4B4F-A213-9D0E633233EE}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -16,6 +16,8 @@ Expose normalized SBOM projections (components, relationships, scopes, entrypoin
|
||||
|
||||
## Required Reading
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- `docs/modules/sbomservice/architecture.md`
|
||||
- `docs/implplan/SPRINT_0142_0001_0001_sbomservice.md`
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` in both correspoding sprint file `/docs/implplan/SPRINT_*.md` and the local `TASKS.md` when you start or finish work.
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
namespace StellaOps.SbomService.Models;
|
||||
|
||||
public sealed record SbomPathQuery(
|
||||
string Purl,
|
||||
string? Artifact,
|
||||
string? Scope,
|
||||
string? Environment,
|
||||
int Limit = 50,
|
||||
int Offset = 0);
|
||||
|
||||
public sealed record SbomPathNode(string Name, string Kind);
|
||||
|
||||
public sealed record SbomPath(
|
||||
IReadOnlyList<SbomPathNode> Nodes,
|
||||
bool RuntimeFlag,
|
||||
string? BlastRadius,
|
||||
string? NearestSafeVersion);
|
||||
|
||||
public sealed record SbomPathResult(
|
||||
string Purl,
|
||||
string? Artifact,
|
||||
string? Scope,
|
||||
string? Environment,
|
||||
IReadOnlyList<SbomPath> Paths,
|
||||
string? NextCursor);
|
||||
|
||||
public sealed record SbomTimelineQuery(
|
||||
string Artifact,
|
||||
int Limit = 50,
|
||||
int Offset = 0);
|
||||
|
||||
public sealed record SbomVersion(
|
||||
string Version,
|
||||
string Digest,
|
||||
DateTimeOffset CreatedAt,
|
||||
string SourceBundleHash,
|
||||
string? Provenance);
|
||||
|
||||
public sealed record SbomTimelineResult(
|
||||
string Artifact,
|
||||
IReadOnlyList<SbomVersion> Versions,
|
||||
string? NextCursor);
|
||||
|
||||
public sealed record SbomCatalogQuery(
|
||||
string? Artifact,
|
||||
string? License,
|
||||
string? Scope,
|
||||
string? AssetTag,
|
||||
int Limit = 50,
|
||||
int Offset = 0);
|
||||
|
||||
public sealed record SbomCatalogItem(
|
||||
string Artifact,
|
||||
string SbomVersion,
|
||||
string Digest,
|
||||
string? License,
|
||||
string Scope,
|
||||
IReadOnlyDictionary<string, string> AssetTags,
|
||||
DateTimeOffset CreatedAt,
|
||||
string ProjectionHash,
|
||||
string EvaluationMetadata);
|
||||
|
||||
public sealed record SbomCatalogResult(
|
||||
IReadOnlyList<SbomCatalogItem> Items,
|
||||
string? NextCursor);
|
||||
|
||||
public sealed record QueryResult<T>(T Result, bool CacheHit);
|
||||
|
||||
public sealed record ComponentLookupQuery(
|
||||
string Purl,
|
||||
string? Artifact,
|
||||
int Limit = 50,
|
||||
int Offset = 0);
|
||||
|
||||
public sealed record ComponentNeighbor(
|
||||
string Purl,
|
||||
string Relationship,
|
||||
string? License,
|
||||
string Scope,
|
||||
bool RuntimeFlag);
|
||||
|
||||
public sealed record ComponentLookupResult(
|
||||
string Purl,
|
||||
string? Artifact,
|
||||
IReadOnlyList<ComponentNeighbor> Neighbors,
|
||||
string? NextCursor,
|
||||
string CacheHint);
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace StellaOps.SbomService.Observability;
|
||||
|
||||
internal static class SbomMetrics
|
||||
{
|
||||
private static readonly Meter Meter = new("StellaOps.SbomService");
|
||||
|
||||
public static readonly Histogram<double> PathsLatencySeconds =
|
||||
Meter.CreateHistogram<double>("sbom_paths_latency_seconds", unit: "s",
|
||||
description: "Latency for SBOM path queries by tenant/scenario");
|
||||
|
||||
public static readonly Counter<long> PathsQueryTotal =
|
||||
Meter.CreateCounter<long>("sbom_paths_queries_total",
|
||||
description: "Total SBOM path queries, tagged by cache hit and scope");
|
||||
|
||||
public static readonly Histogram<double> TimelineLatencySeconds =
|
||||
Meter.CreateHistogram<double>("sbom_timeline_latency_seconds", unit: "s",
|
||||
description: "Latency for SBOM version timeline queries");
|
||||
|
||||
public static readonly Counter<long> TimelineQueryTotal =
|
||||
Meter.CreateCounter<long>("sbom_timeline_queries_total",
|
||||
description: "Total SBOM timeline queries");
|
||||
}
|
||||
@@ -1,17 +1,202 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Configuration
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables("SBOM_");
|
||||
|
||||
builder.Services.AddOptions();
|
||||
builder.Services.AddLogging();
|
||||
|
||||
// TODO: register SBOM projection services, repositories, and Authority integration.
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapGet("/healthz", () => Results.Ok(new { status = "ok" }));
|
||||
app.MapGet("/readyz", () => Results.Ok(new { status = "warming" }));
|
||||
|
||||
app.Run();
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics.Metrics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StellaOps.SbomService.Models;
|
||||
using StellaOps.SbomService.Services;
|
||||
using StellaOps.SbomService.Observability;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Configuration
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddEnvironmentVariables("SBOM_");
|
||||
|
||||
builder.Services.AddOptions();
|
||||
builder.Services.AddLogging();
|
||||
|
||||
// Register SBOM query services (InMemory seed; replace with Mongo-backed repository later).
|
||||
builder.Services.AddSingleton<ISbomQueryService, InMemorySbomQueryService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.MapGet("/healthz", () => Results.Ok(new { status = "ok" }));
|
||||
app.MapGet("/readyz", () => Results.Ok(new { status = "warming" }));
|
||||
|
||||
app.MapGet("/console/sboms", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? license,
|
||||
[FromQuery] string? scope,
|
||||
[FromQuery(Name = "assetTag")] string? assetTag,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (limit is { } requestedLimit && (requestedLimit <= 0 || requestedLimit > 200))
|
||||
{
|
||||
return Results.BadRequest(new { error = "limit must be between 1 and 200" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = limit ?? 50;
|
||||
|
||||
var start = Stopwatch.GetTimestamp();
|
||||
var result = await service.GetConsoleCatalogAsync(
|
||||
new SbomCatalogQuery(artifact?.Trim(), license?.Trim(), scope?.Trim(), assetTag?.Trim(), pageSize, offset),
|
||||
cancellationToken);
|
||||
|
||||
var elapsedSeconds = Stopwatch.GetElapsedTime(start).TotalSeconds;
|
||||
SbomMetrics.PathsLatencySeconds.Record(elapsedSeconds, new TagList
|
||||
{
|
||||
{ "scope", scope ?? string.Empty },
|
||||
{ "env", string.Empty }
|
||||
});
|
||||
SbomMetrics.PathsQueryTotal.Add(1, new TagList
|
||||
{
|
||||
{ "cache_hit", result.CacheHit },
|
||||
{ "scope", scope ?? string.Empty }
|
||||
});
|
||||
|
||||
return Results.Ok(result.Result);
|
||||
});
|
||||
|
||||
app.MapGet("/components/lookup", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromQuery] string? purl,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(purl))
|
||||
{
|
||||
return Results.BadRequest(new { error = "purl is required" });
|
||||
}
|
||||
|
||||
if (limit is { } requestedLimit && (requestedLimit <= 0 || requestedLimit > 200))
|
||||
{
|
||||
return Results.BadRequest(new { error = "limit must be between 1 and 200" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = limit ?? 50;
|
||||
|
||||
var start = Stopwatch.GetTimestamp();
|
||||
var result = await service.GetComponentLookupAsync(
|
||||
new ComponentLookupQuery(purl.Trim(), artifact?.Trim(), pageSize, offset),
|
||||
cancellationToken);
|
||||
|
||||
var elapsedSeconds = Stopwatch.GetElapsedTime(start).TotalSeconds;
|
||||
SbomMetrics.PathsLatencySeconds.Record(elapsedSeconds, new TagList
|
||||
{
|
||||
{ "scope", string.Empty },
|
||||
{ "env", string.Empty }
|
||||
});
|
||||
SbomMetrics.PathsQueryTotal.Add(1, new TagList
|
||||
{
|
||||
{ "cache_hit", result.CacheHit },
|
||||
{ "scope", string.Empty }
|
||||
});
|
||||
|
||||
return Results.Ok(result.Result);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/paths", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromQuery] string? purl,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? scope,
|
||||
[FromQuery(Name = "env")] string? environment,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(purl))
|
||||
{
|
||||
return Results.BadRequest(new { error = "purl is required" });
|
||||
}
|
||||
|
||||
if (limit is { } requestedLimit && (requestedLimit <= 0 || requestedLimit > 200))
|
||||
{
|
||||
return Results.BadRequest(new { error = "limit must be between 1 and 200" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = limit ?? 50;
|
||||
|
||||
var start = Stopwatch.GetTimestamp();
|
||||
var result = await service.GetPathsAsync(
|
||||
new SbomPathQuery(purl.Trim(), artifact?.Trim(), scope?.Trim(), environment?.Trim(), pageSize, offset),
|
||||
cancellationToken);
|
||||
|
||||
var elapsedSeconds = Stopwatch.GetElapsedTime(start).TotalSeconds;
|
||||
SbomMetrics.PathsLatencySeconds.Record(elapsedSeconds, new TagList
|
||||
{
|
||||
{ "scope", scope ?? string.Empty },
|
||||
{ "env", environment ?? string.Empty }
|
||||
});
|
||||
SbomMetrics.PathsQueryTotal.Add(1, new TagList
|
||||
{
|
||||
{ "cache_hit", result.CacheHit },
|
||||
{ "scope", scope ?? string.Empty }
|
||||
});
|
||||
|
||||
return Results.Ok(result.Result);
|
||||
});
|
||||
|
||||
app.MapGet("/sbom/versions", async Task<IResult> (
|
||||
[FromServices] ISbomQueryService service,
|
||||
[FromQuery] string? artifact,
|
||||
[FromQuery] string? cursor,
|
||||
[FromQuery] int? limit,
|
||||
CancellationToken cancellationToken) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(artifact))
|
||||
{
|
||||
return Results.BadRequest(new { error = "artifact is required" });
|
||||
}
|
||||
|
||||
if (limit is { } requestedLimit && (requestedLimit <= 0 || requestedLimit > 200))
|
||||
{
|
||||
return Results.BadRequest(new { error = "limit must be between 1 and 200" });
|
||||
}
|
||||
|
||||
if (cursor is { Length: > 0 } && !int.TryParse(cursor, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||
{
|
||||
return Results.BadRequest(new { error = "cursor must be an integer offset" });
|
||||
}
|
||||
|
||||
var offset = cursor is null ? 0 : int.Parse(cursor, CultureInfo.InvariantCulture);
|
||||
var pageSize = limit ?? 50;
|
||||
|
||||
var start = Stopwatch.GetTimestamp();
|
||||
var result = await service.GetTimelineAsync(
|
||||
new SbomTimelineQuery(artifact.Trim(), pageSize, offset),
|
||||
cancellationToken);
|
||||
|
||||
var elapsedSeconds = Stopwatch.GetElapsedTime(start).TotalSeconds;
|
||||
SbomMetrics.TimelineLatencySeconds.Record(elapsedSeconds, new TagList { { "artifact", artifact } });
|
||||
SbomMetrics.TimelineQueryTotal.Add(1, new TagList { { "artifact", artifact }, { "cache_hit", result.CacheHit } });
|
||||
|
||||
return Results.Ok(result.Result);
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
public partial class Program;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using StellaOps.SbomService.Models;
|
||||
|
||||
namespace StellaOps.SbomService.Services;
|
||||
|
||||
public interface ISbomQueryService
|
||||
{
|
||||
Task<QueryResult<SbomPathResult>> GetPathsAsync(SbomPathQuery query, CancellationToken cancellationToken);
|
||||
|
||||
Task<QueryResult<SbomTimelineResult>> GetTimelineAsync(SbomTimelineQuery query, CancellationToken cancellationToken);
|
||||
|
||||
Task<QueryResult<SbomCatalogResult>> GetConsoleCatalogAsync(SbomCatalogQuery query, CancellationToken cancellationToken);
|
||||
|
||||
Task<QueryResult<ComponentLookupResult>> GetComponentLookupAsync(ComponentLookupQuery query, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
using System.Globalization;
|
||||
using StellaOps.SbomService.Models;
|
||||
|
||||
namespace StellaOps.SbomService.Services;
|
||||
|
||||
internal sealed class InMemorySbomQueryService : ISbomQueryService
|
||||
{
|
||||
private readonly IReadOnlyList<PathRecord> _paths;
|
||||
private readonly IReadOnlyList<TimelineRecord> _timelines;
|
||||
private readonly IReadOnlyList<CatalogRecord> _catalog;
|
||||
private readonly IReadOnlyList<ComponentLookupRecord> _components;
|
||||
private readonly ConcurrentDictionary<string, object> _cache = new();
|
||||
|
||||
public InMemorySbomQueryService()
|
||||
{
|
||||
// Deterministic seed data for early contract testing; replace with Mongo-backed implementation later.
|
||||
_paths = SeedPaths();
|
||||
_timelines = SeedTimelines();
|
||||
_catalog = SeedCatalog();
|
||||
_components = SeedComponents();
|
||||
}
|
||||
|
||||
public Task<QueryResult<SbomPathResult>> GetPathsAsync(SbomPathQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var cacheKey = $"paths|{query.Purl}|{query.Artifact}|{query.Scope}|{query.Environment}|{query.Offset}|{query.Limit}";
|
||||
if (_cache.TryGetValue(cacheKey, out var cached) && cached is SbomPathResult cachedResult)
|
||||
{
|
||||
return Task.FromResult(new QueryResult<SbomPathResult>(cachedResult, true));
|
||||
}
|
||||
|
||||
var filtered = _paths
|
||||
.Where(p => p.Purl.Equals(query.Purl, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(p => query.Artifact is null || p.Artifact.Equals(query.Artifact, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(p => query.Scope is null || string.Equals(p.Scope, query.Scope, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(p => query.Environment is null || string.Equals(p.Environment, query.Environment, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(p => p.Artifact)
|
||||
.ThenBy(p => p.Environment)
|
||||
.ThenBy(p => p.Scope)
|
||||
.ThenBy(p => string.Join("->", p.Nodes.Select(n => n.Name)))
|
||||
.ToList();
|
||||
|
||||
var page = filtered
|
||||
.Skip(query.Offset)
|
||||
.Take(query.Limit)
|
||||
.Select(r => new SbomPath(r.Nodes, r.RuntimeFlag, r.BlastRadius, r.NearestSafeVersion))
|
||||
.ToList();
|
||||
|
||||
string? nextCursor = query.Offset + query.Limit < filtered.Count
|
||||
? (query.Offset + query.Limit).ToString(CultureInfo.InvariantCulture)
|
||||
: null;
|
||||
|
||||
var result = new SbomPathResult(
|
||||
Purl: query.Purl,
|
||||
Artifact: query.Artifact,
|
||||
Scope: query.Scope,
|
||||
Environment: query.Environment,
|
||||
Paths: page,
|
||||
NextCursor: nextCursor);
|
||||
|
||||
_cache[cacheKey] = result;
|
||||
return Task.FromResult(new QueryResult<SbomPathResult>(result, false));
|
||||
}
|
||||
|
||||
public Task<QueryResult<SbomTimelineResult>> GetTimelineAsync(SbomTimelineQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var cacheKey = $"timeline|{query.Artifact}|{query.Offset}|{query.Limit}";
|
||||
if (_cache.TryGetValue(cacheKey, out var cached) && cached is SbomTimelineResult cachedTimeline)
|
||||
{
|
||||
return Task.FromResult(new QueryResult<SbomTimelineResult>(cachedTimeline, true));
|
||||
}
|
||||
|
||||
var filtered = _timelines
|
||||
.Where(t => t.Artifact.Equals(query.Artifact, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(t => t.CreatedAt)
|
||||
.ThenByDescending(t => t.Version)
|
||||
.ToList();
|
||||
|
||||
var page = filtered
|
||||
.Skip(query.Offset)
|
||||
.Take(query.Limit)
|
||||
.Select(t => new SbomVersion(t.Version, t.Digest, t.CreatedAt, t.SourceBundleHash, t.Provenance))
|
||||
.ToList();
|
||||
|
||||
string? nextCursor = query.Offset + query.Limit < filtered.Count
|
||||
? (query.Offset + query.Limit).ToString(CultureInfo.InvariantCulture)
|
||||
: null;
|
||||
|
||||
var result = new SbomTimelineResult(query.Artifact, page, nextCursor);
|
||||
_cache[cacheKey] = result;
|
||||
return Task.FromResult(new QueryResult<SbomTimelineResult>(result, false));
|
||||
}
|
||||
|
||||
public Task<QueryResult<SbomCatalogResult>> GetConsoleCatalogAsync(SbomCatalogQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var cacheKey = $"catalog|{query.Artifact}|{query.License}|{query.Scope}|{query.AssetTag}|{query.Offset}|{query.Limit}";
|
||||
if (_cache.TryGetValue(cacheKey, out var cached) && cached is SbomCatalogResult cachedCatalog)
|
||||
{
|
||||
return Task.FromResult(new QueryResult<SbomCatalogResult>(cachedCatalog, true));
|
||||
}
|
||||
|
||||
var filtered = _catalog
|
||||
.Where(c => query.Artifact is null || c.Artifact.Contains(query.Artifact, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(c => query.License is null || string.Equals(c.License, query.License, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(c => query.Scope is null || string.Equals(c.Scope, query.Scope, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(c => query.AssetTag is null || c.AssetTags.ContainsKey(query.AssetTag))
|
||||
.OrderByDescending(c => c.CreatedAt)
|
||||
.ThenBy(c => c.Artifact)
|
||||
.ToList();
|
||||
|
||||
var page = filtered
|
||||
.Skip(query.Offset)
|
||||
.Take(query.Limit)
|
||||
.Select(c => new SbomCatalogItem(
|
||||
c.Artifact,
|
||||
c.SbomVersion,
|
||||
c.Digest,
|
||||
c.License,
|
||||
c.Scope,
|
||||
c.AssetTags,
|
||||
c.CreatedAt,
|
||||
c.ProjectionHash,
|
||||
c.EvaluationMetadata))
|
||||
.ToList();
|
||||
|
||||
string? nextCursor = query.Offset + query.Limit < filtered.Count
|
||||
? (query.Offset + query.Limit).ToString(CultureInfo.InvariantCulture)
|
||||
: null;
|
||||
|
||||
var result = new SbomCatalogResult(page, nextCursor);
|
||||
_cache[cacheKey] = result;
|
||||
return Task.FromResult(new QueryResult<SbomCatalogResult>(result, false));
|
||||
}
|
||||
|
||||
public Task<QueryResult<ComponentLookupResult>> GetComponentLookupAsync(ComponentLookupQuery query, CancellationToken cancellationToken)
|
||||
{
|
||||
var cacheKey = $"component|{query.Purl}|{query.Artifact}|{query.Offset}|{query.Limit}";
|
||||
if (_cache.TryGetValue(cacheKey, out var cached) && cached is ComponentLookupResult cachedResult)
|
||||
{
|
||||
return Task.FromResult(new QueryResult<ComponentLookupResult>(cachedResult, true));
|
||||
}
|
||||
|
||||
var filtered = _components
|
||||
.Where(c => c.Purl.Equals(query.Purl, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(c => query.Artifact is null || c.Artifact.Equals(query.Artifact, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderBy(c => c.Artifact)
|
||||
.ThenBy(c => c.Purl)
|
||||
.ToList();
|
||||
|
||||
var page = filtered
|
||||
.Skip(query.Offset)
|
||||
.Take(query.Limit)
|
||||
.Select(c => new ComponentNeighbor(c.NeighborPurl, c.Relationship, c.License, c.Scope, c.RuntimeFlag))
|
||||
.ToList();
|
||||
|
||||
string? nextCursor = query.Offset + query.Limit < filtered.Count
|
||||
? (query.Offset + query.Limit).ToString(CultureInfo.InvariantCulture)
|
||||
: null;
|
||||
|
||||
var result = new ComponentLookupResult(query.Purl, query.Artifact, page, nextCursor, CacheHint: "seeded");
|
||||
_cache[cacheKey] = result;
|
||||
return Task.FromResult(new QueryResult<ComponentLookupResult>(result, false));
|
||||
}
|
||||
|
||||
private static IReadOnlyList<PathRecord> SeedPaths()
|
||||
{
|
||||
return new List<PathRecord>
|
||||
{
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api@sha256:111",
|
||||
Purl: "pkg:npm/lodash@4.17.21",
|
||||
Scope: "runtime",
|
||||
Environment: "prod",
|
||||
RuntimeFlag: true,
|
||||
BlastRadius: "medium",
|
||||
NearestSafeVersion: "pkg:npm/lodash@4.17.22",
|
||||
Nodes: new[]
|
||||
{
|
||||
new SbomPathNode("sample-api", "artifact"),
|
||||
new SbomPathNode("express", "npm"),
|
||||
new SbomPathNode("lodash", "npm")
|
||||
}),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api@sha256:111",
|
||||
Purl: "pkg:npm/lodash@4.17.21",
|
||||
Scope: "build",
|
||||
Environment: "prod",
|
||||
RuntimeFlag: false,
|
||||
BlastRadius: "low",
|
||||
NearestSafeVersion: "pkg:npm/lodash@4.17.22",
|
||||
Nodes: new[]
|
||||
{
|
||||
new SbomPathNode("sample-api", "artifact"),
|
||||
new SbomPathNode("rollup", "npm"),
|
||||
new SbomPathNode("lodash", "npm")
|
||||
}),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api@sha256:222",
|
||||
Purl: "pkg:nuget/Newtonsoft.Json@13.0.2",
|
||||
Scope: "runtime",
|
||||
Environment: "staging",
|
||||
RuntimeFlag: true,
|
||||
BlastRadius: "high",
|
||||
NearestSafeVersion: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
Nodes: new[]
|
||||
{
|
||||
new SbomPathNode("sample-worker", "artifact"),
|
||||
new SbomPathNode("StellaOps.Core", "nuget"),
|
||||
new SbomPathNode("Newtonsoft.Json", "nuget")
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<TimelineRecord> SeedTimelines()
|
||||
{
|
||||
return new List<TimelineRecord>
|
||||
{
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
Version: "2025.11.15.1",
|
||||
Digest: "sha256:111",
|
||||
SourceBundleHash: "sha256:bundle111",
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 15, 12, 0, 0, TimeSpan.Zero),
|
||||
Provenance: "scanner:surface_bundle_mock_v1.tgz"),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
Version: "2025.11.16.1",
|
||||
Digest: "sha256:112",
|
||||
SourceBundleHash: "sha256:bundle112",
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 16, 12, 0, 0, TimeSpan.Zero),
|
||||
Provenance: "scanner:surface_bundle_mock_v1.tgz"),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-worker",
|
||||
Version: "2025.11.12.0",
|
||||
Digest: "sha256:222",
|
||||
SourceBundleHash: "sha256:bundle222",
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 12, 8, 0, 0, TimeSpan.Zero),
|
||||
Provenance: "upload:spdx:worker"),
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CatalogRecord> SeedCatalog()
|
||||
{
|
||||
return new List<CatalogRecord>
|
||||
{
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
SbomVersion: "2025.11.16.1",
|
||||
Digest: "sha256:112",
|
||||
License: "MIT",
|
||||
Scope: "runtime",
|
||||
AssetTags: new Dictionary<string, string>
|
||||
{
|
||||
["owner"] = "payments",
|
||||
["criticality"] = "high",
|
||||
["env"] = "prod"
|
||||
},
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 16, 12, 0, 0, TimeSpan.Zero),
|
||||
ProjectionHash: "sha256:proj112",
|
||||
EvaluationMetadata: "eval:passed:v1"),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
SbomVersion: "2025.11.15.1",
|
||||
Digest: "sha256:111",
|
||||
License: "MIT",
|
||||
Scope: "runtime",
|
||||
AssetTags: new Dictionary<string, string>
|
||||
{
|
||||
["owner"] = "payments",
|
||||
["criticality"] = "high",
|
||||
["env"] = "prod"
|
||||
},
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 15, 12, 0, 0, TimeSpan.Zero),
|
||||
ProjectionHash: "sha256:proj111",
|
||||
EvaluationMetadata: "eval:passed:v1"),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-worker",
|
||||
SbomVersion: "2025.11.12.0",
|
||||
Digest: "sha256:222",
|
||||
License: "Apache-2.0",
|
||||
Scope: "runtime",
|
||||
AssetTags: new Dictionary<string, string>
|
||||
{
|
||||
["owner"] = "platform",
|
||||
["criticality"] = "medium",
|
||||
["env"] = "staging"
|
||||
},
|
||||
CreatedAt: new DateTimeOffset(2025, 11, 12, 8, 0, 0, TimeSpan.Zero),
|
||||
ProjectionHash: "sha256:proj222",
|
||||
EvaluationMetadata: "eval:pending:v1"),
|
||||
};
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ComponentLookupRecord> SeedComponents()
|
||||
{
|
||||
return new List<ComponentLookupRecord>
|
||||
{
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
Purl: "pkg:npm/lodash@4.17.21",
|
||||
NeighborPurl: "pkg:npm/express@4.18.2",
|
||||
Relationship: "DEPENDS_ON",
|
||||
License: "MIT",
|
||||
Scope: "runtime",
|
||||
RuntimeFlag: true),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-api",
|
||||
Purl: "pkg:npm/lodash@4.17.21",
|
||||
NeighborPurl: "pkg:npm/rollup@3.0.0",
|
||||
Relationship: "DEPENDS_ON",
|
||||
License: "MIT",
|
||||
Scope: "build",
|
||||
RuntimeFlag: false),
|
||||
new(
|
||||
Artifact: "ghcr.io/stellaops/sample-worker",
|
||||
Purl: "pkg:nuget/Newtonsoft.Json@13.0.2",
|
||||
NeighborPurl: "pkg:nuget/StellaOps.Core@1.0.0",
|
||||
Relationship: "DEPENDS_ON",
|
||||
License: "Apache-2.0",
|
||||
Scope: "runtime",
|
||||
RuntimeFlag: true)
|
||||
};
|
||||
}
|
||||
|
||||
private sealed record PathRecord(
|
||||
string Artifact,
|
||||
string Purl,
|
||||
string? Scope,
|
||||
string? Environment,
|
||||
bool RuntimeFlag,
|
||||
string? BlastRadius,
|
||||
string? NearestSafeVersion,
|
||||
IReadOnlyList<SbomPathNode> Nodes);
|
||||
|
||||
private sealed record TimelineRecord(
|
||||
string Artifact,
|
||||
string Version,
|
||||
string Digest,
|
||||
string SourceBundleHash,
|
||||
DateTimeOffset CreatedAt,
|
||||
string? Provenance);
|
||||
|
||||
private sealed record CatalogRecord(
|
||||
string Artifact,
|
||||
string SbomVersion,
|
||||
string Digest,
|
||||
string? License,
|
||||
string Scope,
|
||||
IReadOnlyDictionary<string, string> AssetTags,
|
||||
DateTimeOffset CreatedAt,
|
||||
string ProjectionHash,
|
||||
string EvaluationMetadata);
|
||||
|
||||
private sealed record ComponentLookupRecord(
|
||||
string Artifact,
|
||||
string Purl,
|
||||
string NeighborPurl,
|
||||
string Relationship,
|
||||
string? License,
|
||||
string Scope,
|
||||
bool RuntimeFlag);
|
||||
}
|
||||
Reference in New Issue
Block a user