Gaps fill up, fixes, ui restructuring

This commit is contained in:
master
2026-02-19 22:10:54 +02:00
parent b5829dce5c
commit 04cacdca8a
331 changed files with 42859 additions and 2174 deletions

View File

@@ -0,0 +1,7 @@
namespace StellaOps.Remediation.Core.Abstractions;
public interface IContributorTrustScorer
{
double CalculateTrustScore(int verifiedFixes, int totalSubmissions, int rejectedSubmissions);
string GetTrustTier(double score);
}

View File

@@ -0,0 +1,8 @@
using StellaOps.Remediation.Core.Models;
namespace StellaOps.Remediation.Core.Abstractions;
public interface IRemediationMatcher
{
Task<IReadOnlyList<FixTemplate>> FindMatchesAsync(string cveId, string? purl = null, string? version = null, CancellationToken ct = default);
}

View File

@@ -0,0 +1,14 @@
using StellaOps.Remediation.Core.Models;
namespace StellaOps.Remediation.Core.Abstractions;
public interface IRemediationRegistry
{
Task<IReadOnlyList<FixTemplate>> ListTemplatesAsync(string? cveId = null, string? purl = null, int limit = 50, int offset = 0, CancellationToken ct = default);
Task<FixTemplate?> GetTemplateAsync(Guid id, CancellationToken ct = default);
Task<FixTemplate> CreateTemplateAsync(FixTemplate template, CancellationToken ct = default);
Task<IReadOnlyList<PrSubmission>> ListSubmissionsAsync(string? cveId = null, string? status = null, int limit = 50, int offset = 0, CancellationToken ct = default);
Task<PrSubmission?> GetSubmissionAsync(Guid id, CancellationToken ct = default);
Task<PrSubmission> CreateSubmissionAsync(PrSubmission submission, CancellationToken ct = default);
Task UpdateSubmissionStatusAsync(Guid id, string status, string? verdict = null, CancellationToken ct = default);
}

View File

@@ -0,0 +1,14 @@
namespace StellaOps.Remediation.Core.Models;
public sealed record Contributor
{
public Guid Id { get; init; }
public string Username { get; init; } = string.Empty;
public string? DisplayName { get; init; }
public int VerifiedFixes { get; init; }
public int TotalSubmissions { get; init; }
public int RejectedSubmissions { get; init; }
public double TrustScore { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? LastActiveAt { get; init; }
}

View File

@@ -0,0 +1,18 @@
namespace StellaOps.Remediation.Core.Models;
public sealed record FixTemplate
{
public Guid Id { get; init; }
public string CveId { get; init; } = string.Empty;
public string Purl { get; init; } = string.Empty;
public string VersionRange { get; init; } = string.Empty;
public string PatchContent { get; init; } = string.Empty;
public string? Description { get; init; }
public Guid? ContributorId { get; init; }
public Guid? SourceId { get; init; }
public string Status { get; init; } = "pending";
public double TrustScore { get; init; }
public string? DsseDigest { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? VerifiedAt { get; init; }
}

View File

@@ -0,0 +1,14 @@
namespace StellaOps.Remediation.Core.Models;
public sealed record MarketplaceSource
{
public Guid Id { get; init; }
public string Key { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public string? Url { get; init; }
public string SourceType { get; init; } = "community";
public bool Enabled { get; init; } = true;
public double TrustScore { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? LastSyncAt { get; init; }
}

View File

@@ -0,0 +1,22 @@
namespace StellaOps.Remediation.Core.Models;
public sealed record PrSubmission
{
public Guid Id { get; init; }
public Guid? FixTemplateId { get; init; }
public string PrUrl { get; init; } = string.Empty;
public string RepositoryUrl { get; init; } = string.Empty;
public string SourceBranch { get; init; } = string.Empty;
public string TargetBranch { get; init; } = string.Empty;
public string CveId { get; init; } = string.Empty;
public string Status { get; init; } = "opened";
public string? PreScanDigest { get; init; }
public string? PostScanDigest { get; init; }
public string? ReachabilityDeltaDigest { get; init; }
public string? FixChainDsseDigest { get; init; }
public string? Verdict { get; init; }
public Guid? ContributorId { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? MergedAt { get; init; }
public DateTimeOffset? VerifiedAt { get; init; }
}

View File

@@ -0,0 +1,21 @@
using StellaOps.Remediation.Core.Abstractions;
namespace StellaOps.Remediation.Core.Services;
public sealed class ContributorTrustScorer : IContributorTrustScorer
{
public double CalculateTrustScore(int verifiedFixes, int totalSubmissions, int rejectedSubmissions)
{
var denominator = Math.Max(totalSubmissions, 1);
var raw = (verifiedFixes * 1.0 - rejectedSubmissions * 0.5) / denominator;
return Math.Clamp(raw, 0.0, 1.0);
}
public string GetTrustTier(double score) => score switch
{
> 0.8 => "trusted",
> 0.5 => "established",
> 0.2 => "new",
_ => "untrusted"
};
}

View File

@@ -0,0 +1,15 @@
using StellaOps.Remediation.Core.Models;
namespace StellaOps.Remediation.Core.Services;
public interface IRemediationVerifier
{
Task<VerificationResult> VerifyAsync(PrSubmission submission, CancellationToken ct = default);
}
public sealed record VerificationResult(
string Verdict,
string? ReachabilityDeltaDigest,
string? FixChainDsseDigest,
IReadOnlyList<string> AffectedPaths,
DateTimeOffset VerifiedAt);

View File

@@ -0,0 +1,42 @@
using StellaOps.Remediation.Core.Models;
namespace StellaOps.Remediation.Core.Services;
public sealed class RemediationVerifier : IRemediationVerifier
{
private readonly TimeProvider _timeProvider;
public RemediationVerifier(TimeProvider? timeProvider = null)
{
_timeProvider = timeProvider ?? TimeProvider.System;
}
public Task<VerificationResult> VerifyAsync(PrSubmission submission, CancellationToken ct = default)
{
// Stub: real implementation will integrate with scan service and reachability delta
var verdict = DetermineVerdict(submission);
var result = new VerificationResult(
Verdict: verdict,
ReachabilityDeltaDigest: submission.ReachabilityDeltaDigest,
FixChainDsseDigest: submission.FixChainDsseDigest,
AffectedPaths: Array.Empty<string>(),
VerifiedAt: _timeProvider.GetUtcNow());
return Task.FromResult(result);
}
private static string DetermineVerdict(PrSubmission submission)
{
if (string.IsNullOrEmpty(submission.PreScanDigest) || string.IsNullOrEmpty(submission.PostScanDigest))
{
return "inconclusive";
}
if (submission.PreScanDigest == submission.PostScanDigest)
{
return "not_fixed";
}
return "fixed";
}
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>