old sprints work, new sprints for exposing functionality via cli, improve code_of_conduct and other agents instructions
This commit is contained in:
@@ -2,6 +2,38 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.Scanner.Reachability.Witnesses;
|
||||
|
||||
/// <summary>
|
||||
/// Well-known predicate types for path witness attestations.
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
public static class WitnessPredicateTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Canonical path witness predicate type URI.
|
||||
/// </summary>
|
||||
public const string PathWitnessCanonical = "https://stella.ops/predicates/path-witness/v1";
|
||||
|
||||
/// <summary>
|
||||
/// Alias 1: stella.ops format for backward compatibility.
|
||||
/// </summary>
|
||||
public const string PathWitnessAlias1 = "stella.ops/pathWitness@v1";
|
||||
|
||||
/// <summary>
|
||||
/// Alias 2: HTTPS URL format variant.
|
||||
/// </summary>
|
||||
public const string PathWitnessAlias2 = "https://stella.ops/pathWitness/v1";
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the predicate type is a recognized path witness type.
|
||||
/// </summary>
|
||||
public static bool IsPathWitnessType(string predicateType)
|
||||
{
|
||||
return predicateType == PathWitnessCanonical
|
||||
|| predicateType == PathWitnessAlias1
|
||||
|| predicateType == PathWitnessAlias2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A DSSE-signable path witness documenting the call path from entrypoint to vulnerable sink.
|
||||
/// Conforms to stellaops.witness.v1 schema.
|
||||
@@ -67,6 +99,34 @@ public sealed record PathWitness
|
||||
/// </summary>
|
||||
[JsonPropertyName("observed_at")]
|
||||
public required DateTimeOffset ObservedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Canonical path hash computed from node hashes along the path.
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
[JsonPropertyName("path_hash")]
|
||||
public string? PathHash { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Top-K node hashes along the path (deterministically ordered).
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
[JsonPropertyName("node_hashes")]
|
||||
public IReadOnlyList<string>? NodeHashes { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Evidence URIs for tracing back to source artifacts.
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
[JsonPropertyName("evidence_uris")]
|
||||
public IReadOnlyList<string>? EvidenceUris { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Canonical predicate type URI for this witness.
|
||||
/// Default: https://stella.ops/predicates/path-witness/v1
|
||||
/// </summary>
|
||||
[JsonPropertyName("predicate_type")]
|
||||
public string PredicateType { get; init; } = WitnessPredicateTypes.PathWitnessCanonical;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -62,6 +62,13 @@ public sealed class PathWitnessBuilder : IPathWitnessBuilder
|
||||
var sinkNode = request.CallGraph.Nodes?.FirstOrDefault(n => n.SymbolId == request.SinkSymbolId);
|
||||
var sinkSymbol = sinkNode?.Display ?? sinkNode?.Symbol?.Demangled ?? request.SinkSymbolId;
|
||||
|
||||
// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
// Compute node hashes and path hash for deterministic joining with runtime evidence
|
||||
var (nodeHashes, pathHash) = ComputePathHashes(request.ComponentPurl, path);
|
||||
|
||||
// Build evidence URIs for traceability
|
||||
var evidenceUris = BuildEvidenceUris(request);
|
||||
|
||||
// Build the witness
|
||||
var witness = new PathWitness
|
||||
{
|
||||
@@ -98,7 +105,12 @@ public sealed class PathWitnessBuilder : IPathWitnessBuilder
|
||||
AnalysisConfigDigest = request.AnalysisConfigDigest,
|
||||
BuildId = request.BuildId
|
||||
},
|
||||
ObservedAt = _timeProvider.GetUtcNow()
|
||||
ObservedAt = _timeProvider.GetUtcNow(),
|
||||
// PW-SCN-003: Add node hashes and path hash
|
||||
NodeHashes = nodeHashes,
|
||||
PathHash = pathHash,
|
||||
EvidenceUris = evidenceUris,
|
||||
PredicateType = WitnessPredicateTypes.PathWitnessCanonical
|
||||
};
|
||||
|
||||
// Compute witness ID from canonical content
|
||||
@@ -480,4 +492,108 @@ public sealed class PathWitnessBuilder : IPathWitnessBuilder
|
||||
|
||||
return $"{WitnessSchema.WitnessIdPrefix}{hash}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes node hashes and combined path hash for the witness path.
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
/// <param name="componentPurl">Component PURL for hash computation.</param>
|
||||
/// <param name="path">Path steps from entrypoint to sink.</param>
|
||||
/// <returns>Tuple of (top-K node hashes, combined path hash).</returns>
|
||||
private static (IReadOnlyList<string> nodeHashes, string pathHash) ComputePathHashes(
|
||||
string componentPurl,
|
||||
IReadOnlyList<PathStep> path)
|
||||
{
|
||||
const int TopK = 10; // Return top-K node hashes
|
||||
|
||||
// Compute node hash for each step in the path
|
||||
var allNodeHashes = new List<string>();
|
||||
foreach (var step in path)
|
||||
{
|
||||
// Use SymbolId as the FQN for hash computation
|
||||
var nodeHash = ComputeNodeHash(componentPurl, step.SymbolId);
|
||||
allNodeHashes.Add(nodeHash);
|
||||
}
|
||||
|
||||
// Deduplicate and sort for deterministic ordering
|
||||
var uniqueHashes = allNodeHashes
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.Order(StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
// Select top-K hashes
|
||||
var topKHashes = uniqueHashes.Take(TopK).ToList();
|
||||
|
||||
// Compute combined path hash from all node hashes
|
||||
var pathHash = ComputeCombinedPathHash(allNodeHashes);
|
||||
|
||||
return (topKHashes, pathHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a canonical node hash from PURL and symbol FQN.
|
||||
/// Uses SHA-256 for compatibility with NodeHashRecipe in StellaOps.Reachability.Core.
|
||||
/// </summary>
|
||||
private static string ComputeNodeHash(string purl, string symbolFqn)
|
||||
{
|
||||
// Normalize inputs
|
||||
var normalizedPurl = purl?.Trim().ToLowerInvariant() ?? string.Empty;
|
||||
var normalizedSymbol = symbolFqn?.Trim() ?? string.Empty;
|
||||
|
||||
var input = $"{normalizedPurl}:{normalizedSymbol}";
|
||||
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(input));
|
||||
|
||||
return "sha256:" + Convert.ToHexStringLower(hashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes a combined path hash from ordered node hashes.
|
||||
/// </summary>
|
||||
private static string ComputeCombinedPathHash(IReadOnlyList<string> nodeHashes)
|
||||
{
|
||||
// Extract hex parts and concatenate in order
|
||||
var hexParts = nodeHashes
|
||||
.Select(h => h.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase) ? h[7..] : h)
|
||||
.ToList();
|
||||
|
||||
var combined = string.Join(":", hexParts);
|
||||
var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(combined));
|
||||
|
||||
return "path:sha256:" + Convert.ToHexStringLower(hashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds evidence URIs for traceability.
|
||||
/// Sprint: SPRINT_20260112_004_SCANNER_path_witness_nodehash (PW-SCN-003)
|
||||
/// </summary>
|
||||
private static IReadOnlyList<string> BuildEvidenceUris(PathWitnessRequest request)
|
||||
{
|
||||
var uris = new List<string>();
|
||||
|
||||
// Add callgraph evidence URI
|
||||
if (!string.IsNullOrWhiteSpace(request.CallgraphDigest))
|
||||
{
|
||||
uris.Add($"evidence:callgraph:{request.CallgraphDigest}");
|
||||
}
|
||||
|
||||
// Add SBOM evidence URI
|
||||
if (!string.IsNullOrWhiteSpace(request.SbomDigest))
|
||||
{
|
||||
uris.Add($"evidence:sbom:{request.SbomDigest}");
|
||||
}
|
||||
|
||||
// Add surface evidence URI
|
||||
if (!string.IsNullOrWhiteSpace(request.SurfaceDigest))
|
||||
{
|
||||
uris.Add($"evidence:surface:{request.SurfaceDigest}");
|
||||
}
|
||||
|
||||
// Add build evidence URI
|
||||
if (!string.IsNullOrWhiteSpace(request.BuildId))
|
||||
{
|
||||
uris.Add($"evidence:build:{request.BuildId}");
|
||||
}
|
||||
|
||||
return uris;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user