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

@@ -269,6 +269,9 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
sb.AppendLine(" │ ├── manifest.json");
sb.AppendLine(" │ ├── sbom.cdx.json");
sb.AppendLine(" │ ├── reachability.json");
sb.AppendLine(" │ ├── binary-diff.json # Binary diff evidence");
sb.AppendLine(" │ ├── binary-diff.dsse.json # Signed binary diff (if attested)");
sb.AppendLine(" │ ├── delta-proof.json # Semantic diff summary");
sb.AppendLine(" │ ├── vex/");
sb.AppendLine(" │ ├── attestations/");
sb.AppendLine(" │ ├── policy/");
@@ -359,6 +362,42 @@ public sealed class EvidenceBundleExporter : IEvidenceBundleExporter
.ConfigureAwait(false);
}
// Binary diff evidence - Sprint: SPRINT_20260112_009_SCANNER_binary_diff_bundle_export (BINDIFF-SCAN-002)
if (evidence.BinaryDiff is not null)
{
await AddJsonFileAsync("binary-diff.json", evidence.BinaryDiff, streams, entries, ct)
.ConfigureAwait(false);
// Add DSSE-signed binary diff if attestation refs are present
if (evidence.BinaryDiff.AttestationRef is not null)
{
var dsseWrapper = new
{
payloadType = "application/vnd.stellaops.binary-diff+json",
payload = evidence.BinaryDiff,
attestationRef = evidence.BinaryDiff.AttestationRef
};
await AddJsonFileAsync("binary-diff.dsse.json", dsseWrapper, streams, entries, ct)
.ConfigureAwait(false);
}
// Add delta proof summary for semantic fingerprint changes
if (evidence.BinaryDiff.SemanticDiff is not null)
{
var deltaProof = new
{
previousFingerprint = evidence.BinaryDiff.SemanticDiff.PreviousFingerprint,
currentFingerprint = evidence.BinaryDiff.SemanticDiff.CurrentFingerprint,
similarityScore = evidence.BinaryDiff.SemanticDiff.SimilarityScore,
semanticChanges = evidence.BinaryDiff.SemanticDiff.SemanticChanges,
functionChangeCount = evidence.BinaryDiff.FunctionChangeCount,
securityChangeCount = evidence.BinaryDiff.SecurityChangeCount
};
await AddJsonFileAsync("delta-proof.json", deltaProof, streams, entries, ct)
.ConfigureAwait(false);
}
}
// Policy evidence
if (evidence.Policy is not null)
{

View File

@@ -1,8 +1,10 @@
// -----------------------------------------------------------------------------
// PrAnnotationService.cs
// Sprint: SPRINT_3700_0005_0001_witness_ui_cli
// Tasks: PR-001, PR-002
// Sprint: SPRINT_20260112_007_SCANNER_pr_mr_annotations (SCANNER-PR-002)
// Tasks: PR-001, PR-002, SCANNER-PR-002
// Description: Service for generating PR annotations with reachability state flips.
// Updated: ASCII-only output, evidence anchors (attestation digest, witness id, policy verdict)
// -----------------------------------------------------------------------------
using StellaOps.Scanner.Reachability;
@@ -114,6 +116,47 @@ public sealed record StateFlipSummary
/// Individual state flips.
/// </summary>
public required IReadOnlyList<StateFlip> Flips { get; init; }
// Sprint: SPRINT_20260112_007_SCANNER_pr_mr_annotations (SCANNER-PR-002)
// Evidence anchor fields
/// <summary>
/// DSSE attestation digest for the head scan.
/// </summary>
public string? AttestationDigest { get; init; }
/// <summary>
/// Policy verdict for the PR (pass/fail/warn).
/// </summary>
public string? PolicyVerdict { get; init; }
/// <summary>
/// Policy verdict reason code.
/// </summary>
public string? PolicyReasonCode { get; init; }
/// <summary>
/// Verify command for reproducibility.
/// </summary>
public string? VerifyCommand { get; init; }
}
/// </summary>
public required int NetChange { get; init; }
/// <summary>
/// Whether this PR should be blocked based on policy.
/// </summary>
public required bool ShouldBlockPr { get; init; }
/// <summary>
/// Human-readable summary.
/// </summary>
public required string Summary { get; init; }
/// <summary>
/// Individual state flips.
/// </summary>
public required IReadOnlyList<StateFlip> Flips { get; init; }
}
/// <summary>
@@ -321,29 +364,57 @@ public sealed class PrAnnotationService : IPrAnnotationService
{
var sb = new System.Text.StringBuilder();
// Header
sb.AppendLine("## 🔍 Reachability Analysis");
// Sprint: SPRINT_20260112_007_SCANNER_pr_mr_annotations (SCANNER-PR-002)
// ASCII-only output with evidence anchors
// Header (ASCII-only)
sb.AppendLine("## Reachability Analysis");
sb.AppendLine();
// Status badge
// Status badge (ASCII-only)
if (summary.ShouldBlockPr)
{
sb.AppendLine(" **Status: BLOCKING** - New reachable vulnerabilities detected");
sb.AppendLine("[BLOCKING] **Status: BLOCKING** - New reachable vulnerabilities detected");
}
else if (summary.NewRiskCount > 0)
{
sb.AppendLine("⚠️ **Status: WARNING** - Reachability changes detected");
sb.AppendLine("[WARNING] **Status: WARNING** - Reachability changes detected");
}
else if (summary.MitigatedCount > 0)
{
sb.AppendLine(" **Status: IMPROVED** - Vulnerabilities became unreachable");
sb.AppendLine("[OK] **Status: IMPROVED** - Vulnerabilities became unreachable");
}
else
{
sb.AppendLine(" **Status: NO CHANGE** - No reachability changes");
sb.AppendLine("[OK] **Status: NO CHANGE** - No reachability changes");
}
sb.AppendLine();
// Evidence anchors section (SCANNER-PR-002)
if (!string.IsNullOrEmpty(summary.AttestationDigest) ||
!string.IsNullOrEmpty(summary.PolicyVerdict) ||
!string.IsNullOrEmpty(summary.VerifyCommand))
{
sb.AppendLine("### Evidence");
sb.AppendLine();
if (!string.IsNullOrEmpty(summary.AttestationDigest))
{
sb.AppendLine($"- **Attestation**: `{summary.AttestationDigest}`");
}
if (!string.IsNullOrEmpty(summary.PolicyVerdict))
{
var reasonPart = !string.IsNullOrEmpty(summary.PolicyReasonCode)
? $" ({summary.PolicyReasonCode})"
: "";
sb.AppendLine($"- **Policy Verdict**: {summary.PolicyVerdict}{reasonPart}");
}
if (!string.IsNullOrEmpty(summary.VerifyCommand))
{
sb.AppendLine($"- **Verify**: `{summary.VerifyCommand}`");
}
sb.AppendLine();
}
// Summary stats
sb.AppendLine("### Summary");
sb.AppendLine($"| Metric | Count |");
@@ -353,7 +424,7 @@ public sealed class PrAnnotationService : IPrAnnotationService
sb.AppendLine($"| Net Change | {(summary.NetChange >= 0 ? "+" : "")}{summary.NetChange} |");
sb.AppendLine();
// Flips table
// Flips table (ASCII-only, deterministic ordering)
if (summary.Flips.Count > 0)
{
sb.AppendLine("### State Flips");
@@ -361,22 +432,29 @@ public sealed class PrAnnotationService : IPrAnnotationService
sb.AppendLine("| CVE | Package | Change | Confidence | Witness |");
sb.AppendLine("|-----|---------|--------|------------|---------|");
foreach (var flip in summary.Flips.Take(20)) // Limit to 20 entries
// Deterministic ordering: became reachable first, then by CVE ID
var orderedFlips = summary.Flips
.OrderByDescending(f => f.FlipType == StateFlipType.BecameReachable)
.ThenBy(f => f.CveId, StringComparer.Ordinal)
.Take(20);
foreach (var flip in orderedFlips)
{
var changeIcon = flip.FlipType switch
// ASCII-only change indicators
var changeText = flip.FlipType switch
{
StateFlipType.BecameReachable => "🔴 Became Reachable",
StateFlipType.BecameUnreachable => "🟢 Became Unreachable",
StateFlipType.TierIncreased => "🟡 Tier ",
StateFlipType.TierDecreased => "🟢 Tier ",
_ => "?"
StateFlipType.BecameReachable => "[+] Became Reachable",
StateFlipType.BecameUnreachable => "[-] Became Unreachable",
StateFlipType.TierIncreased => "[^] Tier Increased",
StateFlipType.TierDecreased => "[v] Tier Decreased",
_ => "[?]"
};
var witnessLink = !string.IsNullOrEmpty(flip.WitnessId)
? $"[View](?witness={flip.WitnessId})"
: "-";
sb.AppendLine($"| {flip.CveId} | `{TruncatePurl(flip.Purl)}` | {changeIcon} | {flip.NewTier} | {witnessLink} |");
sb.AppendLine($"| {flip.CveId} | `{TruncatePurl(flip.Purl)}` | {changeText} | {flip.NewTier} | {witnessLink} |");
}
if (summary.Flips.Count > 20)
@@ -454,7 +532,15 @@ public sealed class PrAnnotationService : IPrAnnotationService
{
var annotations = new List<InlineAnnotation>();
foreach (var flip in flips.Where(f => !string.IsNullOrEmpty(f.FilePath) && f.LineNumber > 0))
// Sprint: SPRINT_20260112_007_SCANNER_pr_mr_annotations (SCANNER-PR-002)
// Deterministic ordering and ASCII-only output
var orderedFlips = flips
.Where(f => !string.IsNullOrEmpty(f.FilePath) && f.LineNumber > 0)
.OrderByDescending(f => f.FlipType == StateFlipType.BecameReachable)
.ThenBy(f => f.FilePath, StringComparer.Ordinal)
.ThenBy(f => f.LineNumber);
foreach (var flip in orderedFlips)
{
var level = flip.FlipType switch
{
@@ -465,17 +551,18 @@ public sealed class PrAnnotationService : IPrAnnotationService
_ => AnnotationLevel.Notice
};
// ASCII-only titles (no emoji)
var title = flip.FlipType switch
{
StateFlipType.BecameReachable => $"🔴 {flip.CveId} is now reachable",
StateFlipType.BecameUnreachable => $"🟢 {flip.CveId} is no longer reachable",
StateFlipType.TierIncreased => $"🟡 {flip.CveId} reachability increased",
StateFlipType.TierDecreased => $"🟢 {flip.CveId} reachability decreased",
StateFlipType.BecameReachable => $"[+] {flip.CveId} is now reachable",
StateFlipType.BecameUnreachable => $"[-] {flip.CveId} is no longer reachable",
StateFlipType.TierIncreased => $"[^] {flip.CveId} reachability increased",
StateFlipType.TierDecreased => $"[v] {flip.CveId} reachability decreased",
_ => flip.CveId
};
var message = $"Package: {flip.Purl}\n" +
$"Confidence: {flip.PreviousTier ?? "N/A"} {flip.NewTier}\n" +
$"Confidence: {flip.PreviousTier ?? "N/A"} -> {flip.NewTier}\n" +
(flip.Entrypoint != null ? $"Entrypoint: {flip.Entrypoint}\n" : "") +
(flip.WitnessId != null ? $"Witness: {flip.WitnessId}" : "");