old sprints work, new sprints for exposing functionality via cli, improve code_of_conduct and other agents instructions

This commit is contained in:
master
2026-01-15 18:37:59 +02:00
parent c631bacee2
commit 88a85cdd92
208 changed files with 32271 additions and 2287 deletions

View File

@@ -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>

View File

@@ -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;
}
}