release orchestration strengthening

This commit is contained in:
master
2026-01-17 21:32:03 +02:00
parent 195dff2457
commit da27b9faa9
256 changed files with 94634 additions and 2269 deletions

View File

@@ -319,10 +319,17 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
// Best single hypothesis
var bestHint = sorted[0];
var bestKey = GetAgreementKey(bestHint) ?? bestHint.Hypothesis;
// If we have multiple high-confidence hints that agree, boost confidence
var agreeing = sorted
.Where(h => h.Confidence >= 0.5)
.GroupBy(GetAgreementKey)
.Select(h => new
{
Hint = h,
Key = GetAgreementKey(h) ?? bestKey
})
.GroupBy(x => x.Key)
.OrderByDescending(g => g.Count())
.FirstOrDefault();
@@ -330,7 +337,7 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
{
// Multiple hints agree - combine confidence
var combinedConfidence = Math.Min(0.99,
agreeing.Max(h => h.Confidence) + (agreeing.Count() - 1) * 0.1);
agreeing.Max(x => x.Hint.Confidence) + (agreeing.Count() - 1) * 0.1);
return (
$"{agreeing.Key} (confirmed by {agreeing.Count()} evidence sources)",
@@ -360,7 +367,7 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
};
}
private static string GetAgreementKey(ProvenanceHint hint)
private static string? GetAgreementKey(ProvenanceHint hint)
{
var evidence = hint.Evidence;
var key = evidence.BuildId?.MatchedPackage
@@ -370,7 +377,7 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
?? ExtractPackageFromVersion(evidence.CorpusMatch?.MatchedEntry)
?? ExtractPackageFromHypothesis(hint.Hypothesis);
return string.IsNullOrWhiteSpace(key) ? hint.Hypothesis : key;
return string.IsNullOrWhiteSpace(key) ? null : key;
}
private static string? BestMatchPackage(IReadOnlyList<FingerprintMatch>? matches)
@@ -395,7 +402,12 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
}
var trimmed = value.Trim();
var token = trimmed.Split([' ', '/', '\t'], StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
if (!trimmed.Contains(' ') && !trimmed.Contains('/'))
{
return null;
}
var token = trimmed.Split([' ', '/', '\t', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
return string.IsNullOrWhiteSpace(token) ? null : token;
}
@@ -419,11 +431,11 @@ public sealed partial class ProvenanceHintBuilder : IProvenanceHintBuilder
};
}
private static string ExtractPackageFromHypothesis(string hypothesis)
private static string? ExtractPackageFromHypothesis(string hypothesis)
{
// Simple extraction - match "matches <package>" or "from <package>"
var match = PackageExtractionRegex().Match(hypothesis);
return match.Success ? match.Groups[1].Value : hypothesis;
return match.Success ? match.Groups[1].Value : null;
}
[GeneratedRegex(@"(?:matches?|from)\s+(\S+)")]

View File

@@ -19,6 +19,10 @@ public sealed class NativeUnknownClassifier
{
private readonly TimeProvider _timeProvider;
private readonly string _createdBy;
private static readonly JsonSerializerOptions ContextJsonOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public NativeUnknownClassifier(TimeProvider timeProvider, string createdBy = "unknowns")
{
@@ -243,16 +247,7 @@ public sealed class NativeUnknownClassifier
private static JsonDocument SerializeContext(NativeUnknownContext context)
{
var json = JsonSerializer.Serialize(context, NativeUnknownContextJsonContext.Default.NativeUnknownContext);
var json = JsonSerializer.Serialize(context, ContextJsonOptions);
return JsonDocument.Parse(json);
}
}
/// <summary>
/// Source-generated JSON context for NativeUnknownContext serialization.
/// </summary>
[System.Text.Json.Serialization.JsonSourceGenerationOptions(PropertyNamingPolicy = System.Text.Json.JsonKnownNamingPolicy.CamelCase)]
[System.Text.Json.Serialization.JsonSerializable(typeof(NativeUnknownContext))]
internal partial class NativeUnknownContextJsonContext : System.Text.Json.Serialization.JsonSerializerContext
{
}

View File

@@ -277,11 +277,10 @@ public sealed class ProvenanceHintSerializationTests
// Act
var json = JsonSerializer.Serialize(hint, JsonOptions);
// Assert - JSON is parseable
var parsed = JsonDocument.Parse(json);
parsed.RootElement.GetProperty("hint_id").GetString().Should().StartWith("hint:sha256:");
parsed.RootElement.GetProperty("type").GetString().Should().NotBeNullOrEmpty();
parsed.RootElement.GetProperty("type").GetInt32().Should().Be((int)ProvenanceHintType.BuildIdMatch);
parsed.RootElement.GetProperty("confidence").GetDouble().Should().BeInRange(0, 1);
parsed.RootElement.GetProperty("evidence").GetProperty("build_id").GetProperty("catalog_source")
.GetString().Should().Be("debian-security");

View File

@@ -8,6 +8,7 @@
using System.Net;
using System.Net.Http.Json;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Unknowns.Core.Models;
using StellaOps.Unknowns.Core.Repositories;
@@ -24,10 +25,24 @@ public sealed class UnknownsEndpointsTests : IClassFixture<WebApplicationFactory
public UnknownsEndpointsTests(WebApplicationFactory<Program> factory)
{
Environment.SetEnvironmentVariable(
"ConnectionStrings__UnknownsDb",
"Host=localhost;Database=unknowns_test;Username=test;Password=test");
_mockRepository = Substitute.For<IUnknownRepository>();
_factory = factory.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration((_, config) =>
{
var settings = new Dictionary<string, string?>
{
["ConnectionStrings:UnknownsDb"] =
"Host=localhost;Database=unknowns_test;Username=test;Password=test"
};
config.AddInMemoryCollection(settings);
});
builder.ConfigureServices(services =>
{
// Remove existing repository registration
@@ -336,20 +351,35 @@ public sealed class UnknownsEndpointsTests : IClassFixture<WebApplicationFactory
ProvenanceHints = [
new ProvenanceHint
{
Id = $"hint:{Guid.NewGuid():N}",
HintId = $"hint:sha256:{Guid.NewGuid():N}",
Type = ProvenanceHintType.BuildIdMatch,
Confidence = 0.85,
ConfidenceLevel = HintConfidence.High,
Summary = "Build-ID match from Debian",
Hypothesis = "Likely debian:bookworm backport",
Evidence = new ProvenanceEvidence
{
BuildId = new BuildIdEvidence
{
BuildId = "deadbeef0123456789abcdef",
BuildIdType = "sha256",
MatchedPackage = "openssl",
MatchedVersion = "3.0.0",
MatchedDistro = "debian",
CatalogSource = "debian-security"
}
},
SuggestedActions = [
new SuggestedAction
{
Action = "Verify debian package version",
Priority = 1,
Effort = "low",
Description = "Check against Debian security tracker"
}
],
GeneratedAt = now.AddDays(-3)
GeneratedAt = now.AddDays(-3),
Source = "BuildIdAnalyzer"
}
],
BestHypothesis = "Likely debian:bookworm backport",