Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.

This commit is contained in:
StellaOps Bot
2025-12-26 21:54:17 +02:00
parent 335ff7da16
commit c2b9cd8d1f
3717 changed files with 264714 additions and 48202 deletions

View File

@@ -10,21 +10,21 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.10.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Moq" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Scanner\StellaOps.Scanner.WebService\StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="..\Attestor\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="..\Cli\StellaOps.Cli\StellaOps.Cli.csproj" />
<ProjectReference Include="..\..\..\Scanner\StellaOps.Scanner.WebService\StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="..\..\..\Attestor\__Libraries\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="..\..\..\Cli\StellaOps.Cli\StellaOps.Cli.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -92,7 +92,7 @@ public class BinaryEvidenceDeterminismTests
var identities = await Task.WhenAll(tasks);
// Assert - All outputs should be identical
identities.Should().AllBe(identities[0]);
identities.Should().OnlyContain(x => x == identities[0]);
}
#endregion
@@ -303,7 +303,7 @@ public class BinaryEvidenceDeterminismTests
var proofs = await Task.WhenAll(tasks);
// Assert
proofs.Should().AllBe(proofs[0]);
proofs.Should().OnlyContain(x => x == proofs[0]);
}
#endregion

View File

@@ -418,12 +418,13 @@ public class FullVerdictPipelineDeterminismTests
/// Validates input permutations produce consistent output ordering.
/// </summary>
[Theory]
[InlineData(new[] { "c", "a", "b" })]
[InlineData(new[] { "a", "b", "c" })]
[InlineData(new[] { "b", "c", "a" })]
public void InputPermutations_ProduceConsistentOrdering(string[] inputOrder)
[InlineData("c", "a", "b")]
[InlineData("a", "b", "c")]
[InlineData("b", "c", "a")]
public void InputPermutations_ProduceConsistentOrdering(string first, string second, string third)
{
// Arrange
var inputOrder = new[] { first, second, third };
var changes = inputOrder.Select((id, i) => CreateChange(
$"CVE-2024-{id}",
$"pkg:npm/{id}@1.0.0",

View File

@@ -166,7 +166,7 @@ public class ReachabilityEvidenceDeterminismTests
for (int i = 1; i < evidence1.Paths.Count; i++)
{
string.CompareOrdinal(evidence1.Paths[i - 1].PathId, evidence1.Paths[i].PathId)
.Should().BeLessOrEqualTo(0, "Paths should be sorted by PathId");
.Should().BeLessThanOrEqualTo(0, "Paths should be sorted by PathId");
}
}

View File

@@ -17,45 +17,45 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<!-- Policy scoring for determinism tests -->
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<ProjectReference Include="../../../Policy/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<!-- Policy for VerdictId content-addressing tests (SPRINT_8200_0001_0001) -->
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj" />
<ProjectReference Include="../../../Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj" />
<!-- Proof chain for hash verification -->
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="../../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<!-- Cryptography for hashing -->
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<!-- Canonical JSON -->
<ProjectReference Include="../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<!-- Determinism manifest writer/reader (NEW for SPRINT_5100_0007_0003) -->
<ProjectReference Include="../__Libraries/StellaOps.Testing.Determinism/StellaOps.Testing.Determinism.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Testing.Determinism/StellaOps.Testing.Determinism.csproj" />
<!-- Scanner Emit for SBOM generation (SPRINT_5100_0009_0001 Task 7) -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Emit/StellaOps.Scanner.Emit.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Emit/StellaOps.Scanner.Emit.csproj" />
<!-- Scanner Core contracts for composition requests -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
<!-- Scanner Reachability for reachability evidence determinism (SPRINT_5100_0009_0001 Task 8) -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<!-- Scanner Evidence for reachability evidence models (SPRINT_5100_0009_0001 Task 8) -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Evidence/StellaOps.Scanner.Evidence.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Evidence/StellaOps.Scanner.Evidence.csproj" />
</ItemGroup>
<ItemGroup>
<!-- Determinism corpus -->

View File

@@ -159,7 +159,7 @@ public class TriageOutputDeterminismTests
for (int i = 1; i < output1.Findings.Count; i++)
{
string.CompareOrdinal(output1.Findings[i - 1].CveId, output1.Findings[i].CveId)
.Should().BeLessOrEqualTo(0, "Findings should be sorted by CVE ID");
.Should().BeLessThanOrEqualTo(0, "Findings should be sorted by CVE ID");
}
}

View File

@@ -165,7 +165,7 @@ public class VerdictArtifactDeterminismTests
{
cmp = string.CompareOrdinal(verdict1.Changes[i - 1].PackageUrl, verdict1.Changes[i].PackageUrl);
}
cmp.Should().BeLessOrEqualTo(0, "Changes should be sorted by CVE ID, then package URL");
cmp.Should().BeLessThanOrEqualTo(0, "Changes should be sorted by CVE ID, then package URL");
}
}
@@ -282,7 +282,7 @@ public class VerdictArtifactDeterminismTests
for (int i = 1; i < evidences.Count; i++)
{
string.CompareOrdinal(evidences[i - 1].EvidenceType, evidences[i].EvidenceType)
.Should().BeLessOrEqualTo(0, "Proof spine evidences should be sorted by type");
.Should().BeLessThanOrEqualTo(0, "Proof spine evidences should be sorted by type");
}
}

View File

@@ -13,10 +13,11 @@ using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using StellaOps.Attestor.Dsse;
using StellaOps.Attestor.Envelope;
using StellaOps.Canonical.Json;
using StellaOps.Policy.Deltas;
using Testcontainers.PostgreSql;
using Xunit;
namespace StellaOps.Integration.E2E;
@@ -460,7 +461,7 @@ public sealed class E2EReproducibilityTestFixture : IAsyncLifetime
"HIGH" => DeltaDriverSeverity.High,
"MEDIUM" => DeltaDriverSeverity.Medium,
"LOW" => DeltaDriverSeverity.Low,
_ => DeltaDriverSeverity.Unknown
_ => DeltaDriverSeverity.Low // Default to Low for unknown severities
};
private static bool IsBlockingSeverity(string severity) =>

View File

@@ -368,8 +368,8 @@ public static class ManifestComparer
var endOffset = Math.Min(Math.Max(expected.Length, actual.Length), diff.Offset + contextBytes);
sb.AppendLine($"Difference at offset 0x{diff.Offset:X8} ({diff.Offset}):");
sb.AppendLine($" Expected: 0x{diff.Expected:X2} ('{(char?)(diff.Expected >= 32 && diff.Expected < 127 ? diff.Expected : '.')}')" );
sb.AppendLine($" Actual: 0x{diff.Actual:X2} ('{(char?)(diff.Actual >= 32 && diff.Actual < 127 ? diff.Actual : '.')}')" );
sb.AppendLine($" Expected: 0x{diff.Expected:X2} ('{(diff.Expected is byte e && e >= 32 && e < 127 ? (char)e : '.')}')" );
sb.AppendLine($" Actual: 0x{diff.Actual:X2} ('{(diff.Actual is byte a && a >= 32 && a < 127 ? (char)a : '.')}')" );
sb.AppendLine();
}

View File

@@ -0,0 +1,434 @@
// Licensed to StellaOps under the AGPL-3.0-or-later license.
using System.Collections.Immutable;
using System.Net;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using StellaOps.ReachGraph.Schema;
using StellaOps.Scanner.CallGraph;
using StellaOps.Scanner.Reachability;
using Xunit;
namespace StellaOps.Integration.E2E;
/// <summary>
/// End-to-end tests for the ReachGraph pipeline.
/// Tests: scan -> extract call graph -> store -> slice query -> verify determinism.
/// Sprint: SPRINT_1227_0012_0003
/// Task: T12 - End-to-end test
/// </summary>
public class ReachGraphE2ETests : IClassFixture<WebApplicationFactory<StellaOps.ReachGraph.WebService.Program>>
{
private readonly HttpClient _client;
private const string TenantHeader = "X-Tenant-ID";
private const string TestTenant = "e2e-test-tenant";
public ReachGraphE2ETests(WebApplicationFactory<StellaOps.ReachGraph.WebService.Program> factory)
{
_client = factory.CreateClient();
_client.DefaultRequestHeaders.Add(TenantHeader, TestTenant);
}
[Fact]
public async Task FullPipeline_CallGraphToSliceQuery_Succeeds()
{
// 1. Create a call graph with edge explanations
var callGraph = CreateTestCallGraphWithExplanations();
// 2. Build a ReachGraph from the call graph
var reachGraph = BuildReachGraphFromCallGraph(callGraph);
// 3. Store the reachability graph
var upsertRequest = new { graph = reachGraph };
var upsertResponse = await _client.PostAsJsonAsync("/v1/reachgraphs", upsertRequest);
Assert.Equal(HttpStatusCode.Created, upsertResponse.StatusCode);
var upsertResult = await upsertResponse.Content.ReadFromJsonAsync<UpsertResult>();
Assert.NotNull(upsertResult);
Assert.True(upsertResult.Created);
Assert.StartsWith("blake3:", upsertResult.Digest);
// 4. Query slice by CVE
var sliceResponse = await _client.GetAsync(
$"/v1/reachgraphs/{upsertResult.Digest}/slice?cve=CVE-2024-1234");
Assert.Equal(HttpStatusCode.OK, sliceResponse.StatusCode);
var slice = await sliceResponse.Content.ReadFromJsonAsync<SliceResult>();
Assert.NotNull(slice);
Assert.Equal("cve", slice.SliceQuery?.Type);
Assert.Equal("CVE-2024-1234", slice.SliceQuery?.Cve);
// 5. Verify paths exist and have edge explanations
Assert.NotNull(slice.Paths);
Assert.NotEmpty(slice.Paths);
var path = slice.Paths.First();
Assert.True(path.Hops?.Count > 0);
// 6. Verify determinism via replay
var replayRequest = new
{
expectedDigest = upsertResult.Digest,
inputs = new
{
sbom = reachGraph.Provenance.Inputs.Sbom,
callgraph = reachGraph.Provenance.Inputs.Callgraph
}
};
var replayResponse = await _client.PostAsJsonAsync("/v1/reachgraphs/replay", replayRequest);
Assert.Equal(HttpStatusCode.OK, replayResponse.StatusCode);
var replayResult = await replayResponse.Content.ReadFromJsonAsync<ReplayResult>();
Assert.NotNull(replayResult);
Assert.True(replayResult.Match);
Assert.Equal(upsertResult.Digest, replayResult.ComputedDigest);
// 7. Verify the graph can be fetched
var getResponse = await _client.GetAsync($"/v1/reachgraphs/{upsertResult.Digest}");
Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
}
[Fact]
public async Task EdgeExplanations_AllTypes_Classified()
{
// Test that all edge explanation types are preserved through the pipeline
var reachGraph = CreateGraphWithAllExplanationTypes();
var upsertRequest = new { graph = reachGraph };
var upsertResponse = await _client.PostAsJsonAsync("/v1/reachgraphs", upsertRequest);
Assert.Equal(HttpStatusCode.Created, upsertResponse.StatusCode);
var upsertResult = await upsertResponse.Content.ReadFromJsonAsync<UpsertResult>();
Assert.NotNull(upsertResult);
// Fetch the graph back
var getResponse = await _client.GetAsync($"/v1/reachgraphs/{upsertResult.Digest}");
Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
var fetchedGraph = await getResponse.Content.ReadFromJsonAsync<ReachGraphMinimal>();
Assert.NotNull(fetchedGraph);
Assert.NotNull(fetchedGraph.Edges);
// Verify edge explanations are preserved
var edgeTypes = fetchedGraph.Edges.Select(e => e.Why.Type).Distinct().ToList();
Assert.Contains(EdgeExplanationType.Import, edgeTypes);
Assert.Contains(EdgeExplanationType.EnvGuard, edgeTypes);
}
[Fact]
public async Task SliceByPackage_ReturnsConnectedSubgraph()
{
var reachGraph = CreateMultiPackageGraph();
var upsertRequest = new { graph = reachGraph };
var upsertResponse = await _client.PostAsJsonAsync("/v1/reachgraphs", upsertRequest);
Assert.Equal(HttpStatusCode.Created, upsertResponse.StatusCode);
var upsertResult = await upsertResponse.Content.ReadFromJsonAsync<UpsertResult>();
Assert.NotNull(upsertResult);
// Slice by package pattern
var sliceResponse = await _client.GetAsync(
$"/v1/reachgraphs/{upsertResult.Digest}/slice?q=pkg:npm/lodash@*&depth=2");
Assert.Equal(HttpStatusCode.OK, sliceResponse.StatusCode);
var slice = await sliceResponse.Content.ReadFromJsonAsync<SliceResult>();
Assert.NotNull(slice);
Assert.Equal("package", slice.SliceQuery?.Type);
Assert.True(slice.NodeCount > 0);
}
[Fact]
public async Task DeterministicDigest_MultipleUpserts_SameDigest()
{
var reachGraph1 = CreateTestCallGraphWithExplanations();
var reachGraph2 = CreateTestCallGraphWithExplanations();
// Both graphs should produce the same digest
var graph1 = BuildReachGraphFromCallGraph(reachGraph1);
var graph2 = BuildReachGraphFromCallGraph(reachGraph2);
var response1 = await _client.PostAsJsonAsync("/v1/reachgraphs", new { graph = graph1 });
var response2 = await _client.PostAsJsonAsync("/v1/reachgraphs", new { graph = graph2 });
var result1 = await response1.Content.ReadFromJsonAsync<UpsertResult>();
var result2 = await response2.Content.ReadFromJsonAsync<UpsertResult>();
Assert.NotNull(result1);
Assert.NotNull(result2);
Assert.Equal(result1.Digest, result2.Digest);
// First upsert creates, second returns existing
Assert.True(result1.Created);
Assert.False(result2.Created);
}
#region Test Data Builders
private static CallGraphSnapshot CreateTestCallGraphWithExplanations()
{
var nodes = ImmutableArray.Create(
new CallGraphNode(
NodeId: "sha256:entry1",
Symbol: "main()",
File: "src/index.ts",
Line: 1,
Package: "test-app",
Visibility: Visibility.Public,
IsEntrypoint: true,
EntrypointType: EntrypointType.HttpHandler,
IsSink: false,
SinkCategory: null),
new CallGraphNode(
NodeId: "sha256:handler1",
Symbol: "processRequest()",
File: "src/handler.ts",
Line: 42,
Package: "test-app",
Visibility: Visibility.Public,
IsEntrypoint: false,
EntrypointType: null,
IsSink: false,
SinkCategory: null),
new CallGraphNode(
NodeId: "sha256:sink1",
Symbol: "lodash.template()",
File: "node_modules/lodash/template.js",
Line: 100,
Package: "lodash",
Visibility: Visibility.Public,
IsEntrypoint: false,
EntrypointType: null,
IsSink: true,
SinkCategory: SinkCategory.TemplateInjection)
);
var edges = ImmutableArray.Create(
new CallGraphEdge(
SourceId: "sha256:entry1",
TargetId: "sha256:handler1",
CallKind: CallKind.Direct,
CallSite: "src/index.ts:5",
Explanation: CallEdgeExplanation.Import()),
new CallGraphEdge(
SourceId: "sha256:handler1",
TargetId: "sha256:sink1",
CallKind: CallKind.Direct,
CallSite: "src/handler.ts:50",
Explanation: CallEdgeExplanation.EnvGuard("DEBUG=true"))
);
return new CallGraphSnapshot(
ScanId: "e2e-test-scan",
GraphDigest: "sha256:e2e-test-digest",
Language: "node",
ExtractedAt: DateTimeOffset.UtcNow,
Nodes: nodes,
Edges: edges,
EntrypointIds: ImmutableArray.Create("sha256:entry1"),
SinkIds: ImmutableArray.Create("sha256:sink1"));
}
private static ReachGraphMinimal BuildReachGraphFromCallGraph(CallGraphSnapshot callGraph)
{
var nodes = callGraph.Nodes.Select(n => new ReachGraphNode
{
Id = n.NodeId,
Kind = n.IsEntrypoint ? ReachGraphNodeKind.Function : n.IsSink ? ReachGraphNodeKind.Function : ReachGraphNodeKind.Package,
Ref = n.Symbol,
File = n.File,
Line = n.Line,
IsEntrypoint = n.IsEntrypoint,
IsSink = n.IsSink
}).ToImmutableArray();
var edges = callGraph.Edges.Select(e => new ReachGraphEdge
{
From = e.SourceId,
To = e.TargetId,
Why = new EdgeExplanation
{
Type = MapExplanationType(e.Explanation?.Type ?? CallEdgeExplanationType.DirectCall),
Loc = e.CallSite,
Guard = e.Explanation?.Guard,
Confidence = e.Explanation?.Confidence ?? 1.0
}
}).ToImmutableArray();
return new ReachGraphMinimal
{
SchemaVersion = "reachgraph.min@v1",
Artifact = new ReachGraphArtifact(
$"test-app:1.0.0",
$"sha256:{Guid.NewGuid():N}",
["linux/amd64"]),
Scope = new ReachGraphScope(
callGraph.EntrypointIds,
ImmutableArray.Create("prod"),
ImmutableArray.Create("CVE-2024-1234")),
Nodes = nodes,
Edges = edges,
Provenance = new ReachGraphProvenance
{
Inputs = new ReachGraphInputs
{
Sbom = $"sha256:sbom{Guid.NewGuid():N}",
Callgraph = $"sha256:cg{Guid.NewGuid():N}"
},
ComputedAt = DateTimeOffset.UtcNow,
Analyzer = new ReachGraphAnalyzer(
"stellaops-e2e-test",
"1.0.0",
$"sha256:tool{Guid.NewGuid():N}")
}
};
}
private static ReachGraphMinimal CreateGraphWithAllExplanationTypes()
{
var nodes = ImmutableArray.Create(
new ReachGraphNode { Id = "n1", Kind = ReachGraphNodeKind.Function, Ref = "entry()", IsEntrypoint = true },
new ReachGraphNode { Id = "n2", Kind = ReachGraphNodeKind.Function, Ref = "loadModule()" },
new ReachGraphNode { Id = "n3", Kind = ReachGraphNodeKind.Function, Ref = "reflectCall()" },
new ReachGraphNode { Id = "n4", Kind = ReachGraphNodeKind.Function, Ref = "guardedCall()" },
new ReachGraphNode { Id = "n5", Kind = ReachGraphNodeKind.Function, Ref = "sink()", IsSink = true }
);
var edges = ImmutableArray.Create(
new ReachGraphEdge
{
From = "n1",
To = "n2",
Why = new EdgeExplanation { Type = EdgeExplanationType.Import, Confidence = 1.0 }
},
new ReachGraphEdge
{
From = "n2",
To = "n3",
Why = new EdgeExplanation { Type = EdgeExplanationType.DynamicLoad, Confidence = 0.5 }
},
new ReachGraphEdge
{
From = "n3",
To = "n4",
Why = new EdgeExplanation { Type = EdgeExplanationType.Reflection, Confidence = 0.5 }
},
new ReachGraphEdge
{
From = "n4",
To = "n5",
Why = new EdgeExplanation
{
Type = EdgeExplanationType.EnvGuard,
Guard = "ENABLE_FEATURE=true",
Confidence = 0.9
}
}
);
return new ReachGraphMinimal
{
SchemaVersion = "reachgraph.min@v1",
Artifact = new ReachGraphArtifact("all-types:v1", "sha256:abc123", ["linux/amd64"]),
Scope = new ReachGraphScope(["n1"], ["test"]),
Nodes = nodes,
Edges = edges,
Provenance = new ReachGraphProvenance
{
Inputs = new ReachGraphInputs { Sbom = "sha256:sbom1" },
ComputedAt = DateTimeOffset.UtcNow,
Analyzer = new ReachGraphAnalyzer("test", "1.0.0", "sha256:tool1")
}
};
}
private static ReachGraphMinimal CreateMultiPackageGraph()
{
var nodes = ImmutableArray.Create(
new ReachGraphNode { Id = "app1", Kind = ReachGraphNodeKind.Function, Ref = "main()", IsEntrypoint = true },
new ReachGraphNode { Id = "lodash1", Kind = ReachGraphNodeKind.Package, Ref = "pkg:npm/lodash@4.17.21" },
new ReachGraphNode { Id = "axios1", Kind = ReachGraphNodeKind.Package, Ref = "pkg:npm/axios@1.6.0" },
new ReachGraphNode { Id = "sink1", Kind = ReachGraphNodeKind.Function, Ref = "template()", IsSink = true }
);
var edges = ImmutableArray.Create(
new ReachGraphEdge { From = "app1", To = "lodash1", Why = new EdgeExplanation { Type = EdgeExplanationType.Import, Confidence = 1.0 } },
new ReachGraphEdge { From = "app1", To = "axios1", Why = new EdgeExplanation { Type = EdgeExplanationType.Import, Confidence = 1.0 } },
new ReachGraphEdge { From = "lodash1", To = "sink1", Why = new EdgeExplanation { Type = EdgeExplanationType.Import, Confidence = 1.0 } }
);
return new ReachGraphMinimal
{
SchemaVersion = "reachgraph.min@v1",
Artifact = new ReachGraphArtifact("multi-pkg:v1", "sha256:multi123", ["linux/amd64"]),
Scope = new ReachGraphScope(["app1"], ["prod"]),
Nodes = nodes,
Edges = edges,
Provenance = new ReachGraphProvenance
{
Inputs = new ReachGraphInputs { Sbom = "sha256:sbommulti" },
ComputedAt = DateTimeOffset.UtcNow,
Analyzer = new ReachGraphAnalyzer("test", "1.0.0", "sha256:toolmulti")
}
};
}
private static EdgeExplanationType MapExplanationType(CallEdgeExplanationType callType) => callType switch
{
CallEdgeExplanationType.Import => EdgeExplanationType.Import,
CallEdgeExplanationType.DynamicLoad => EdgeExplanationType.DynamicLoad,
CallEdgeExplanationType.Reflection => EdgeExplanationType.Reflection,
CallEdgeExplanationType.Ffi => EdgeExplanationType.Ffi,
CallEdgeExplanationType.EnvGuard => EdgeExplanationType.EnvGuard,
CallEdgeExplanationType.FeatureFlag => EdgeExplanationType.FeatureFlag,
CallEdgeExplanationType.PlatformArch => EdgeExplanationType.PlatformArch,
CallEdgeExplanationType.TaintGate => EdgeExplanationType.TaintGate,
CallEdgeExplanationType.LoaderRule => EdgeExplanationType.LoaderRule,
_ => EdgeExplanationType.Import
};
#endregion
#region Response DTOs
private sealed record UpsertResult
{
public bool Created { get; init; }
public string Digest { get; init; } = string.Empty;
public int NodeCount { get; init; }
public int EdgeCount { get; init; }
}
private sealed record SliceResult
{
public SliceQueryInfo? SliceQuery { get; init; }
public string? ParentDigest { get; init; }
public int NodeCount { get; init; }
public int EdgeCount { get; init; }
public List<PathInfo>? Paths { get; init; }
}
private sealed record SliceQueryInfo
{
public string? Type { get; init; }
public string? Query { get; init; }
public string? Cve { get; init; }
}
private sealed record PathInfo
{
public string? Entrypoint { get; init; }
public string? Sink { get; init; }
public List<string>? Hops { get; init; }
}
private sealed record ReplayResult
{
public bool Match { get; init; }
public string? ComputedDigest { get; init; }
public string? ExpectedDigest { get; init; }
public int DurationMs { get; init; }
}
#endregion
}

View File

@@ -18,48 +18,56 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Testcontainers" Version="3.6.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<!-- ReachGraph WebService for reachability graph tests -->
<ProjectReference Include="../../../ReachGraph/StellaOps.ReachGraph.WebService/StellaOps.ReachGraph.WebService.csproj" />
<!-- Scanner WebService for integration testing -->
<ProjectReference Include="../../Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="../../../Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj" />
<!-- Scanner Core for contracts -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
<!-- Scanner CallGraph for call graph models -->
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.CallGraph/StellaOps.Scanner.CallGraph.csproj" />
<!-- ReachGraph Schema types -->
<ProjectReference Include="../../../__Libraries/StellaOps.ReachGraph/StellaOps.ReachGraph.csproj" />
<!-- Concelier for advisory ingestion and normalization -->
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj" />
<ProjectReference Include="../../../Concelier/__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
<ProjectReference Include="../../../Concelier/__Libraries/StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
<ProjectReference Include="../../../Concelier/__Libraries/StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
<ProjectReference Include="../../../Concelier/__Libraries/StellaOps.Concelier.Merge/StellaOps.Concelier.Merge.csproj" />
<!-- Policy for verdict computation -->
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj" />
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<ProjectReference Include="../../../Policy/__Libraries/StellaOps.Policy/StellaOps.Policy.csproj" />
<ProjectReference Include="../../../Policy/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<!-- Attestor for DSSE envelope and bundle creation -->
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.Dsse/StellaOps.Attestor.Dsse.csproj" />
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="../../Attestor/StellaOps.Attestor.Envelope/StellaOps.Attestor.Envelope.csproj" />
<!-- Attestor for bundle creation -->
<ProjectReference Include="../../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="../../../Attestor/StellaOps.Attestor.Envelope/StellaOps.Attestor.Envelope.csproj" />
<!-- Cryptography for hashing and content addressing -->
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<!-- Canonical JSON for deterministic serialization -->
<ProjectReference Include="../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Canonical.Json/StellaOps.Canonical.Json.csproj" />
<!-- Testing infrastructure -->
<ProjectReference Include="../__Libraries/StellaOps.Testing.Determinism/StellaOps.Testing.Determinism.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Testing.Determinism/StellaOps.Testing.Determinism.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -10,21 +10,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Scanner\StellaOps.Scanner.WebService\StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="..\Attestor\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="..\Concelier\__Libraries\StellaOps.Concelier.CallGraph\StellaOps.Concelier.CallGraph.csproj" />
<ProjectReference Include="..\Policy\__Libraries\StellaOps.Policy.Scoring\StellaOps.Policy.Scoring.csproj" />
<ProjectReference Include="..\..\..\Scanner\StellaOps.Scanner.WebService\StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="..\..\..\Attestor\__Libraries\StellaOps.Attestor.ProofChain\StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="..\..\..\Policy\StellaOps.Policy.Scoring\StellaOps.Policy.Scoring.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -17,20 +17,21 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Npgsql" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Testcontainers" Version="3.6.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<!-- Infrastructure testing library -->
<ProjectReference Include="../__Libraries/StellaOps.Infrastructure.Postgres.Testing/StellaOps.Infrastructure.Postgres.Testing.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Infrastructure.Postgres.Testing/StellaOps.Infrastructure.Postgres.Testing.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -12,6 +12,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Testcontainers.PostgreSql;
using Xunit;
namespace StellaOps.Integration.ProofChain;

View File

@@ -17,31 +17,30 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Testcontainers" Version="3.6.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<!-- Scanner WebService for integration testing -->
<ProjectReference Include="../../Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj" />
<ProjectReference Include="../../../Scanner/StellaOps.Scanner.WebService/StellaOps.Scanner.WebService.csproj" />
<!-- Proof chain and attestation libraries -->
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.Dsse/StellaOps.Attestor.Dsse.csproj" />
<ProjectReference Include="../../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
<!-- Policy scoring -->
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<ProjectReference Include="../../../Policy/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<!-- Cryptography -->
<ProjectReference Include="../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -63,10 +63,10 @@ public class ReachabilityIntegrationTests : IClassFixture<ReachabilityTestFixtur
// Assert
entrypoints.Should().NotBeEmpty("Kestrel apps should have HTTP entrypoints");
entrypoints.Should().Contain(e =>
e.Symbol?.Contains("Controller", StringComparison.OrdinalIgnoreCase) == true ||
e.Symbol?.Contains("Endpoint", StringComparison.OrdinalIgnoreCase) == true ||
e.Symbol?.Contains("Handler", StringComparison.OrdinalIgnoreCase) == true);
entrypoints.Should().Contain(e =>
(e.Symbol != null && e.Symbol.Contains("Controller", StringComparison.OrdinalIgnoreCase)) ||
(e.Symbol != null && e.Symbol.Contains("Endpoint", StringComparison.OrdinalIgnoreCase)) ||
(e.Symbol != null && e.Symbol.Contains("Handler", StringComparison.OrdinalIgnoreCase)));
}
#endregion

View File

@@ -17,27 +17,22 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Testcontainers" Version="3.6.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<!-- Scanner libraries for reachability -->
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.CallGraph/StellaOps.Scanner.CallGraph.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.CallGraph.DotNet/StellaOps.Scanner.CallGraph.DotNet.csproj" />
<ProjectReference Include="../../Scanner/__Libraries/StellaOps.Scanner.CallGraph.Java/StellaOps.Scanner.CallGraph.Java.csproj" />
<!-- Attestation for graph signing -->
<ProjectReference Include="../../Attestor/__Libraries/StellaOps.Attestor.Dsse/StellaOps.Attestor.Dsse.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.Reachability/StellaOps.Scanner.Reachability.csproj" />
<ProjectReference Include="../../../Scanner/__Libraries/StellaOps.Scanner.CallGraph/StellaOps.Scanner.CallGraph.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -17,25 +17,22 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" >
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageReference Include="Testcontainers" Version="3.6.0" />
<PackageReference Include="Testcontainers.PostgreSql" Version="3.6.0" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Testcontainers" />
<PackageReference Include="Testcontainers.PostgreSql" />
</ItemGroup>
<ItemGroup>
<!-- Policy libraries for unknowns -->
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy.Unknowns/StellaOps.Policy.Unknowns.csproj" />
<ProjectReference Include="../../Policy/__Libraries/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
<!-- Scheduler for rescan integration -->
<ProjectReference Include="../../Scheduler/__Libraries/StellaOps.Scheduler.Client/StellaOps.Scheduler.Client.csproj" />
<ProjectReference Include="../../../Policy/__Libraries/StellaOps.Policy.Unknowns/StellaOps.Policy.Unknowns.csproj" />
<ProjectReference Include="../../../Policy/StellaOps.Policy.Scoring/StellaOps.Policy.Scoring.csproj" />
</ItemGroup>
</Project>