feat: Add PathViewer and RiskDriftCard components with templates and styles
- Implemented PathViewerComponent for visualizing reachability call paths. - Added RiskDriftCardComponent to display reachability drift results. - Created corresponding HTML templates and SCSS styles for both components. - Introduced test fixtures for reachability analysis in JSON format. - Enhanced user interaction with collapsible and expandable features in PathViewer. - Included risk trend visualization and summary metrics in RiskDriftCard.
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// AttestingRichGraphWriter.cs
|
||||
// Sprint: SPRINT_3620_0001_0001_reachability_witness_dsse
|
||||
// Description: RichGraphWriter wrapper that produces DSSE attestation alongside graph.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace StellaOps.Scanner.Reachability.Attestation;
|
||||
|
||||
/// <summary>
|
||||
/// Result of writing a rich graph with attestation.
|
||||
/// </summary>
|
||||
/// <param name="GraphPath">Path to the richgraph-v1.json file.</param>
|
||||
/// <param name="MetaPath">Path to the meta.json file.</param>
|
||||
/// <param name="GraphHash">Content-addressed hash of the graph.</param>
|
||||
/// <param name="NodeCount">Number of nodes in the graph.</param>
|
||||
/// <param name="EdgeCount">Number of edges in the graph.</param>
|
||||
/// <param name="AttestationPath">Path to the attestation DSSE envelope (if produced).</param>
|
||||
/// <param name="WitnessResult">Detailed witness publication result (if attestation enabled).</param>
|
||||
public sealed record AttestingRichGraphWriteResult(
|
||||
string GraphPath,
|
||||
string MetaPath,
|
||||
string GraphHash,
|
||||
int NodeCount,
|
||||
int EdgeCount,
|
||||
string? AttestationPath,
|
||||
ReachabilityWitnessPublishResult? WitnessResult);
|
||||
|
||||
/// <summary>
|
||||
/// Writes richgraph-v1 documents with optional DSSE attestation.
|
||||
/// Wraps <see cref="RichGraphWriter"/> and integrates with <see cref="IReachabilityWitnessPublisher"/>.
|
||||
/// </summary>
|
||||
public sealed class AttestingRichGraphWriter
|
||||
{
|
||||
private readonly RichGraphWriter _graphWriter;
|
||||
private readonly IReachabilityWitnessPublisher _witnessPublisher;
|
||||
private readonly ReachabilityWitnessOptions _options;
|
||||
private readonly ILogger<AttestingRichGraphWriter> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new attesting rich graph writer.
|
||||
/// </summary>
|
||||
public AttestingRichGraphWriter(
|
||||
RichGraphWriter graphWriter,
|
||||
IReachabilityWitnessPublisher witnessPublisher,
|
||||
IOptions<ReachabilityWitnessOptions> options,
|
||||
ILogger<AttestingRichGraphWriter> logger)
|
||||
{
|
||||
_graphWriter = graphWriter ?? throw new ArgumentNullException(nameof(graphWriter));
|
||||
_witnessPublisher = witnessPublisher ?? throw new ArgumentNullException(nameof(witnessPublisher));
|
||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the rich graph and produces attestation if enabled.
|
||||
/// </summary>
|
||||
/// <param name="graph">The rich graph to write.</param>
|
||||
/// <param name="outputRoot">Root output directory.</param>
|
||||
/// <param name="analysisId">Analysis identifier.</param>
|
||||
/// <param name="subjectDigest">Subject artifact digest for attestation.</param>
|
||||
/// <param name="policyHash">Optional policy hash for attestation.</param>
|
||||
/// <param name="sourceCommit">Optional source commit for attestation.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Write result including attestation details.</returns>
|
||||
public async Task<AttestingRichGraphWriteResult> WriteWithAttestationAsync(
|
||||
RichGraph graph,
|
||||
string outputRoot,
|
||||
string analysisId,
|
||||
string subjectDigest,
|
||||
string? policyHash = null,
|
||||
string? sourceCommit = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(graph);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(outputRoot);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(analysisId);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(subjectDigest);
|
||||
|
||||
// Step 1: Write the graph using the standard writer
|
||||
var writeResult = await _graphWriter.WriteAsync(graph, outputRoot, analysisId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_logger.LogDebug(
|
||||
"Wrote rich graph: {GraphPath}, hash={GraphHash}, nodes={NodeCount}, edges={EdgeCount}",
|
||||
writeResult.GraphPath,
|
||||
writeResult.GraphHash,
|
||||
writeResult.NodeCount,
|
||||
writeResult.EdgeCount);
|
||||
|
||||
// Step 2: Produce attestation if enabled
|
||||
string? attestationPath = null;
|
||||
ReachabilityWitnessPublishResult? witnessResult = null;
|
||||
|
||||
if (_options.Enabled)
|
||||
{
|
||||
// Read the graph bytes for attestation
|
||||
var graphBytes = await File.ReadAllBytesAsync(writeResult.GraphPath, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Publish witness attestation
|
||||
witnessResult = await _witnessPublisher.PublishAsync(
|
||||
graph,
|
||||
graphBytes,
|
||||
writeResult.GraphHash,
|
||||
subjectDigest,
|
||||
policyHash,
|
||||
sourceCommit,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Write DSSE envelope to disk alongside the graph
|
||||
if (witnessResult.DsseEnvelopeBytes.Length > 0)
|
||||
{
|
||||
var graphDir = Path.GetDirectoryName(writeResult.GraphPath)!;
|
||||
attestationPath = Path.Combine(graphDir, "richgraph-v1.dsse.json");
|
||||
|
||||
await File.WriteAllBytesAsync(attestationPath, witnessResult.DsseEnvelopeBytes, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Wrote reachability witness attestation: {AttestationPath}, statementHash={StatementHash}",
|
||||
attestationPath,
|
||||
witnessResult.StatementHash);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Reachability witness attestation is disabled");
|
||||
}
|
||||
|
||||
return new AttestingRichGraphWriteResult(
|
||||
GraphPath: writeResult.GraphPath,
|
||||
MetaPath: writeResult.MetaPath,
|
||||
GraphHash: writeResult.GraphHash,
|
||||
NodeCount: writeResult.NodeCount,
|
||||
EdgeCount: writeResult.EdgeCount,
|
||||
AttestationPath: attestationPath,
|
||||
WitnessResult: witnessResult);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user