Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -13,14 +13,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="StackExchange.Redis" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -17,8 +17,7 @@ using StellaOps.Concelier.Connector.Common;
|
||||
using StellaOps.Concelier.Connector.Common.Fetch;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Plugin;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.CertCc;
|
||||
@@ -595,7 +594,7 @@ public sealed class CertCcConnector : IFeedConnector
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private async Task<IReadOnlyList<string>> ReadSummaryNotesAsync(DocumentRecord document, CancellationToken cancellationToken)
|
||||
private async Task<IReadOnlyList<string>> ReadSummaryNotesAsync(StorageDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!document.PayloadId.HasValue)
|
||||
{
|
||||
@@ -606,6 +605,16 @@ public sealed class CertCcConnector : IFeedConnector
|
||||
return CertCcSummaryParser.ParseNotes(payload);
|
||||
}
|
||||
|
||||
private async Task<byte[]> DownloadDocumentAsync(StorageDocument document, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!document.PayloadId.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Document {document.Id} has no GridFS payload.");
|
||||
}
|
||||
|
||||
return await _rawDocumentStorage.DownloadAsync(document.PayloadId.Value, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task<byte[]> DownloadDocumentAsync(DocumentRecord document, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!document.PayloadId.HasValue)
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig" Version="0.31.0" />
|
||||
<PackageReference Include="Markdig" />
|
||||
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -72,8 +72,8 @@ public sealed class JsonSchemaValidator : IJsonSchemaValidator
|
||||
foreach (var kvp in node.Errors)
|
||||
{
|
||||
errors.Add(new JsonSchemaValidationError(
|
||||
node.InstanceLocation?.ToString() ?? string.Empty,
|
||||
node.SchemaLocation?.ToString() ?? string.Empty,
|
||||
node.InstanceLocation.ToString() ?? string.Empty,
|
||||
node.SchemaLocation.ToString() ?? string.Empty,
|
||||
kvp.Value,
|
||||
kvp.Key));
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonSchema.Net" Version="7.3.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
|
||||
<PackageReference Include="AngleSharp" Version="1.2.0" />
|
||||
<PackageReference Include="PdfPig" Version="0.1.12" />
|
||||
<PackageReference Include="NuGet.Versioning" Version="6.13.2" />
|
||||
<PackageReference Include="JsonSchema.Net" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
|
||||
<PackageReference Include="AngleSharp" />
|
||||
<PackageReference Include="PdfPig" />
|
||||
<PackageReference Include="NuGet.Versioning" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj" />
|
||||
@@ -18,6 +18,6 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Storage.Postgres\StellaOps.Concelier.Storage.Postgres.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Persistence\StellaOps.Concelier.Persistence.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -79,6 +79,11 @@ public sealed class CannedHttpMessageHandler : HttpMessageHandler
|
||||
_fallback = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias for <see cref="Clear"/> to maintain backward compatibility.
|
||||
/// </summary>
|
||||
public void Reset() => Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Throws if any responses remain queued.
|
||||
/// </summary>
|
||||
@@ -207,4 +212,10 @@ public sealed class CannedHttpMessageHandler : HttpMessageHandler
|
||||
|
||||
public void AddTextResponse(Uri requestUri, string content, string contentType = "text/plain", HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
=> AddResponse(requestUri, () => BuildTextResponse(statusCode, content, contentType));
|
||||
|
||||
/// <summary>
|
||||
/// Adds an error response with the specified HTTP status code.
|
||||
/// </summary>
|
||||
public void AddErrorResponse(Uri requestUri, HttpStatusCode statusCode)
|
||||
=> AddResponse(requestUri, () => new HttpResponseMessage(statusCode));
|
||||
}
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build.Tasks.Core" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -26,4 +26,4 @@
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -23,4 +23,4 @@
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Schemas\kev-catalog.schema.json" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Connector.Common\StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Normalization\StellaOps.Concelier.Normalization.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -21,4 +21,4 @@
|
||||
<_Parameter1>StellaOps.Concelier.Connector.Osv.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.2.0" />
|
||||
<PackageReference Include="AngleSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -20,4 +20,4 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.2.0" />
|
||||
<PackageReference Include="AngleSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -21,4 +21,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="1.2.0" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
|
||||
<PackageReference Include="AngleSharp" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,4 +28,4 @@
|
||||
<_Parameter1>StellaOps.Concelier.Connector.Vndr.Chromium.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Contracts;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Vndr.Oracle.Internal;
|
||||
|
||||
@@ -203,6 +204,15 @@ internal sealed record OracleFetchCacheEntry(string? Sha256, string? ETag, DateT
|
||||
document.LastModified?.ToUniversalTime());
|
||||
}
|
||||
|
||||
public static OracleFetchCacheEntry FromDocument(StorageDocument document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
return new OracleFetchCacheEntry(
|
||||
document.Sha256 ?? string.Empty,
|
||||
document.Etag,
|
||||
document.LastModified?.ToUniversalTime());
|
||||
}
|
||||
|
||||
public bool Matches(DocumentRecord document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
@@ -224,4 +234,26 @@ internal sealed record OracleFetchCacheEntry(string? Sha256, string? ETag, DateT
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Matches(StorageDocument document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
if (!string.IsNullOrEmpty(Sha256) && !string.IsNullOrEmpty(document.Sha256))
|
||||
{
|
||||
return string.Equals(Sha256, document.Sha256, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ETag) && !string.IsNullOrEmpty(document.Etag))
|
||||
{
|
||||
return string.Equals(ETag, document.Etag, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
if (LastModified.HasValue && document.LastModified.HasValue)
|
||||
{
|
||||
return LastModified.Value.ToUniversalTime() == document.LastModified.Value.ToUniversalTime();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ using StellaOps.Concelier.Connector.Vndr.Oracle.Configuration;
|
||||
using StellaOps.Concelier.Connector.Vndr.Oracle.Internal;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||
using StellaOps.Plugin;
|
||||
|
||||
|
||||
@@ -13,4 +13,4 @@
|
||||
<ProjectReference Include="../StellaOps.Concelier.Connector.Common/StellaOps.Concelier.Connector.Common.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using StellaOps.Concelier.Documents;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Contracts;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
|
||||
|
||||
@@ -61,6 +62,16 @@ internal sealed record VmwareFetchCacheEntry(string? Sha256, string? ETag, DateT
|
||||
document.LastModified?.ToUniversalTime());
|
||||
}
|
||||
|
||||
public static VmwareFetchCacheEntry FromDocument(StorageDocument document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
return new VmwareFetchCacheEntry(
|
||||
document.Sha256,
|
||||
document.Etag,
|
||||
document.LastModified?.ToUniversalTime());
|
||||
}
|
||||
|
||||
public bool Matches(DocumentRecord document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
@@ -85,4 +96,29 @@ internal sealed record VmwareFetchCacheEntry(string? Sha256, string? ETag, DateT
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Matches(StorageDocument document)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
|
||||
if (!string.IsNullOrEmpty(Sha256) && !string.IsNullOrEmpty(document.Sha256)
|
||||
&& string.Equals(Sha256, document.Sha256, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ETag) && !string.IsNullOrEmpty(document.Etag)
|
||||
&& string.Equals(ETag, document.Etag, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastModified.HasValue && document.LastModified.HasValue
|
||||
&& LastModified.Value.ToUniversalTime() == document.LastModified.Value.ToUniversalTime())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
<_Parameter1>StellaOps.Concelier.Tests</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -16,8 +16,7 @@ using StellaOps.Concelier.Connector.Vndr.Vmware.Configuration;
|
||||
using StellaOps.Concelier.Connector.Vndr.Vmware.Internal;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Storage.PsirtFlags;
|
||||
using StellaOps.Plugin;
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Cronos" Version="0.9.0" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="Cronos" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Cryptography/StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Plugin/StellaOps.Plugin.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,7 @@ using StellaOps.Concelier.Federation.Compression;
|
||||
using StellaOps.Concelier.Federation.Models;
|
||||
using StellaOps.Concelier.Federation.Serialization;
|
||||
using StellaOps.Concelier.Federation.Signing;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Federation.Export;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Concelier.Core.Canonical;
|
||||
using StellaOps.Concelier.Federation.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Federation.Export;
|
||||
|
||||
|
||||
@@ -8,17 +8,17 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="ZstdSharp.Port" Version="0.8.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="ZstdSharp.Port" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Core\StellaOps.Concelier.Core.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Models\StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Storage.Postgres\StellaOps.Concelier.Storage.Postgres.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Persistence\StellaOps.Concelier.Persistence.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Concelier.Cache.Valkey\StellaOps.Concelier.Cache.Valkey.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Canonical.Json\StellaOps.Canonical.Json.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Provenance\StellaOps.Provenance.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="..\..\..\Router/__Libraries/StellaOps.Messaging\StellaOps.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.0" />
|
||||
<PackageReference Include="Semver" Version="2.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
|
||||
<PackageReference Include="Semver" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Models/StellaOps.Concelier.Models.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.Normalization/StellaOps.Concelier.Normalization.csproj" />
|
||||
<ProjectReference Include="../StellaOps.Concelier.ProofService/StellaOps.Concelier.ProofService.csproj" />
|
||||
<ProjectReference Include="../../../Attestor/__Libraries/StellaOps.Attestor.ProofChain/StellaOps.Attestor.ProofChain.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="../../../Router/__Libraries/StellaOps.Messaging/StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Provcache/StellaOps.Provcache.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.VersionComparison/StellaOps.VersionComparison.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Concelier.RawModels\StellaOps.Concelier.RawModels.csproj" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
@@ -17,6 +17,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NuGet.Versioning" Version="6.13.2" />
|
||||
<PackageReference Include="NuGet.Versioning" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace StellaOps.Concelier.Persistence.EfCore.Context;
|
||||
|
||||
/// <summary>
|
||||
/// EF Core DbContext for Concelier module.
|
||||
/// This is a stub that will be scaffolded from the PostgreSQL database.
|
||||
/// </summary>
|
||||
public class ConcelierDbContext : DbContext
|
||||
{
|
||||
public ConcelierDbContext(DbContextOptions<ConcelierDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.HasDefaultSchema("vuln");
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Advisories;
|
||||
using StellaOps.Concelier.Persistence.Postgres;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
using StellaOps.Concelier.Core.Linksets;
|
||||
using StorageContracts = StellaOps.Concelier.Storage;
|
||||
using AdvisoryContracts = StellaOps.Concelier.Storage.Advisories;
|
||||
using ExportingContracts = StellaOps.Concelier.Storage.Exporting;
|
||||
using JpFlagsContracts = StellaOps.Concelier.Storage.JpFlags;
|
||||
using PsirtContracts = StellaOps.Concelier.Storage.PsirtFlags;
|
||||
using HistoryContracts = StellaOps.Concelier.Storage.ChangeHistory;
|
||||
using StellaOps.Concelier.Merge.Backport;
|
||||
|
||||
namespace StellaOps.Concelier.Persistence.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for configuring Concelier persistence services.
|
||||
/// </summary>
|
||||
public static class ConcelierPersistenceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Concelier PostgreSQL persistence services.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="configuration">Configuration root.</param>
|
||||
/// <param name="sectionName">Configuration section name for PostgreSQL options.</param>
|
||||
/// <returns>Service collection for chaining.</returns>
|
||||
public static IServiceCollection AddConcelierPersistence(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration,
|
||||
string sectionName = "Postgres:Concelier")
|
||||
{
|
||||
services.Configure<PostgresOptions>(sectionName, configuration.GetSection(sectionName));
|
||||
services.AddSingleton<ConcelierDataSource>();
|
||||
|
||||
// Register repositories
|
||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||
services.AddScoped<IAdvisoryReferenceRepository, AdvisoryReferenceRepository>();
|
||||
services.AddScoped<IAdvisoryCreditRepository, AdvisoryCreditRepository>();
|
||||
services.AddScoped<IAdvisoryWeaknessRepository, AdvisoryWeaknessRepository>();
|
||||
services.AddScoped<IKevFlagRepository, KevFlagRepository>();
|
||||
services.AddScoped<StellaOps.Concelier.Persistence.Postgres.Repositories.ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<AdvisoryContracts.IAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<IDocumentRepository, DocumentRepository>();
|
||||
services.AddScoped<StorageContracts.ISourceStateRepository, PostgresSourceStateAdapter>();
|
||||
services.AddScoped<IFeedSnapshotRepository, FeedSnapshotRepository>();
|
||||
services.AddScoped<IAdvisorySnapshotRepository, AdvisorySnapshotRepository>();
|
||||
services.AddScoped<IMergeEventRepository, MergeEventRepository>();
|
||||
services.AddScoped<IAdvisoryLinksetStore, AdvisoryLinksetCacheRepository>();
|
||||
services.AddScoped<IAdvisoryLinksetLookup>(sp => sp.GetRequiredService<IAdvisoryLinksetStore>());
|
||||
services.AddScoped<StorageContracts.IDocumentStore, PostgresDocumentStore>();
|
||||
services.AddScoped<StorageContracts.IDtoStore, PostgresDtoStore>();
|
||||
services.AddScoped<ExportingContracts.IExportStateStore, PostgresExportStateStore>();
|
||||
services.AddScoped<PsirtContracts.IPsirtFlagStore, PostgresPsirtFlagStore>();
|
||||
services.AddScoped<JpFlagsContracts.IJpFlagStore, PostgresJpFlagStore>();
|
||||
services.AddScoped<HistoryContracts.IChangeHistoryStore, PostgresChangeHistoryStore>();
|
||||
|
||||
// Provenance scope services (backport integration)
|
||||
services.AddScoped<IProvenanceScopeRepository, ProvenanceScopeRepository>();
|
||||
services.AddScoped<IProvenanceScopeStore, PostgresProvenanceScopeStore>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds Concelier PostgreSQL persistence services with explicit options.
|
||||
/// </summary>
|
||||
/// <param name="services">Service collection.</param>
|
||||
/// <param name="configureOptions">Options configuration action.</param>
|
||||
/// <returns>Service collection for chaining.</returns>
|
||||
public static IServiceCollection AddConcelierPersistence(
|
||||
this IServiceCollection services,
|
||||
Action<PostgresOptions> configureOptions)
|
||||
{
|
||||
services.Configure(configureOptions);
|
||||
services.AddSingleton<ConcelierDataSource>();
|
||||
|
||||
// Register repositories
|
||||
services.AddScoped<IAdvisoryRepository, AdvisoryRepository>();
|
||||
services.AddScoped<IPostgresAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<ISourceRepository, SourceRepository>();
|
||||
services.AddScoped<IAdvisoryAliasRepository, AdvisoryAliasRepository>();
|
||||
services.AddScoped<IAdvisoryCvssRepository, AdvisoryCvssRepository>();
|
||||
services.AddScoped<IAdvisoryAffectedRepository, AdvisoryAffectedRepository>();
|
||||
services.AddScoped<IAdvisoryReferenceRepository, AdvisoryReferenceRepository>();
|
||||
services.AddScoped<IAdvisoryCreditRepository, AdvisoryCreditRepository>();
|
||||
services.AddScoped<IAdvisoryWeaknessRepository, AdvisoryWeaknessRepository>();
|
||||
services.AddScoped<IKevFlagRepository, KevFlagRepository>();
|
||||
services.AddScoped<StellaOps.Concelier.Persistence.Postgres.Repositories.ISourceStateRepository, SourceStateRepository>();
|
||||
services.AddScoped<AdvisoryContracts.IAdvisoryStore, PostgresAdvisoryStore>();
|
||||
services.AddScoped<IDocumentRepository, DocumentRepository>();
|
||||
services.AddScoped<StorageContracts.ISourceStateRepository, PostgresSourceStateAdapter>();
|
||||
services.AddScoped<IFeedSnapshotRepository, FeedSnapshotRepository>();
|
||||
services.AddScoped<IAdvisorySnapshotRepository, AdvisorySnapshotRepository>();
|
||||
services.AddScoped<IMergeEventRepository, MergeEventRepository>();
|
||||
services.AddScoped<IAdvisoryLinksetStore, AdvisoryLinksetCacheRepository>();
|
||||
services.AddScoped<IAdvisoryLinksetLookup>(sp => sp.GetRequiredService<IAdvisoryLinksetStore>());
|
||||
services.AddScoped<StorageContracts.IDocumentStore, PostgresDocumentStore>();
|
||||
services.AddScoped<StorageContracts.IDtoStore, PostgresDtoStore>();
|
||||
services.AddScoped<ExportingContracts.IExportStateStore, PostgresExportStateStore>();
|
||||
services.AddScoped<PsirtContracts.IPsirtFlagStore, PostgresPsirtFlagStore>();
|
||||
services.AddScoped<JpFlagsContracts.IJpFlagStore, PostgresJpFlagStore>();
|
||||
services.AddScoped<HistoryContracts.IChangeHistoryStore, PostgresChangeHistoryStore>();
|
||||
|
||||
// Provenance scope services (backport integration)
|
||||
services.AddScoped<IProvenanceScopeRepository, ProvenanceScopeRepository>();
|
||||
services.AddScoped<IProvenanceScopeStore, PostgresProvenanceScopeStore>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,728 @@
|
||||
-- Concelier/Vuln Schema: Consolidated Initial Schema
|
||||
-- Consolidated from migrations 001-017 (pre_1.0 archived)
|
||||
-- Creates the complete vuln and concelier schemas for vulnerability advisory management
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 1: Schema and Extension Creation
|
||||
-- ============================================================================
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS vuln;
|
||||
CREATE SCHEMA IF NOT EXISTS concelier;
|
||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 2: Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.update_updated_at()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.update_timestamp()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.update_advisory_search_vector()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.search_vector =
|
||||
setweight(to_tsvector('english', COALESCE(NEW.primary_vuln_id, '')), 'A') ||
|
||||
setweight(to_tsvector('english', COALESCE(NEW.title, '')), 'B') ||
|
||||
setweight(to_tsvector('english', COALESCE(NEW.summary, '')), 'C') ||
|
||||
setweight(to_tsvector('english', COALESCE(NEW.description, '')), 'D');
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 3: Core vuln Tables
|
||||
-- ============================================================================
|
||||
|
||||
-- Sources table (feed sources)
|
||||
CREATE TABLE IF NOT EXISTS vuln.sources (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
key TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL,
|
||||
url TEXT,
|
||||
priority INT NOT NULL DEFAULT 0,
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
config JSONB NOT NULL DEFAULT '{}',
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_sources_enabled ON vuln.sources(enabled, priority DESC);
|
||||
|
||||
CREATE TRIGGER trg_sources_updated_at
|
||||
BEFORE UPDATE ON vuln.sources
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_updated_at();
|
||||
|
||||
-- Feed snapshots table
|
||||
CREATE TABLE IF NOT EXISTS vuln.feed_snapshots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source_id UUID NOT NULL REFERENCES vuln.sources(id),
|
||||
snapshot_id TEXT NOT NULL,
|
||||
advisory_count INT NOT NULL DEFAULT 0,
|
||||
checksum TEXT,
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(source_id, snapshot_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_feed_snapshots_source ON vuln.feed_snapshots(source_id);
|
||||
CREATE INDEX idx_feed_snapshots_created ON vuln.feed_snapshots(created_at);
|
||||
|
||||
-- Advisory snapshots table
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_snapshots (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
feed_snapshot_id UUID NOT NULL REFERENCES vuln.feed_snapshots(id),
|
||||
advisory_key TEXT NOT NULL,
|
||||
content_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(feed_snapshot_id, advisory_key)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_snapshots_feed ON vuln.advisory_snapshots(feed_snapshot_id);
|
||||
CREATE INDEX idx_advisory_snapshots_key ON vuln.advisory_snapshots(advisory_key);
|
||||
|
||||
-- Advisories table with generated columns
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_key TEXT NOT NULL UNIQUE,
|
||||
primary_vuln_id TEXT NOT NULL,
|
||||
source_id UUID REFERENCES vuln.sources(id),
|
||||
title TEXT,
|
||||
summary TEXT,
|
||||
description TEXT,
|
||||
severity TEXT CHECK (severity IN ('critical', 'high', 'medium', 'low', 'unknown')),
|
||||
published_at TIMESTAMPTZ,
|
||||
modified_at TIMESTAMPTZ,
|
||||
withdrawn_at TIMESTAMPTZ,
|
||||
provenance JSONB NOT NULL DEFAULT '{}',
|
||||
raw_payload JSONB,
|
||||
search_vector TSVECTOR,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
-- Generated columns for provenance
|
||||
provenance_source_key TEXT GENERATED ALWAYS AS (provenance->>'source_key') STORED,
|
||||
provenance_feed_id TEXT GENERATED ALWAYS AS (provenance->>'feed_id') STORED,
|
||||
provenance_ingested_at TIMESTAMPTZ GENERATED ALWAYS AS ((provenance->>'ingested_at')::TIMESTAMPTZ) STORED
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisories_vuln_id ON vuln.advisories(primary_vuln_id);
|
||||
CREATE INDEX idx_advisories_source ON vuln.advisories(source_id);
|
||||
CREATE INDEX idx_advisories_severity ON vuln.advisories(severity);
|
||||
CREATE INDEX idx_advisories_published ON vuln.advisories(published_at);
|
||||
CREATE INDEX idx_advisories_modified ON vuln.advisories(modified_at);
|
||||
CREATE INDEX idx_advisories_search ON vuln.advisories USING GIN(search_vector);
|
||||
CREATE INDEX IF NOT EXISTS ix_advisories_provenance_source ON vuln.advisories(provenance_source_key) WHERE provenance_source_key IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_advisories_provenance_feed ON vuln.advisories(provenance_feed_id) WHERE provenance_feed_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_advisories_provenance_ingested ON vuln.advisories(provenance_ingested_at DESC) WHERE provenance_ingested_at IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_advisories_severity_ingested ON vuln.advisories(severity, provenance_ingested_at DESC) WHERE provenance_ingested_at IS NOT NULL;
|
||||
|
||||
CREATE TRIGGER trg_advisories_search_vector
|
||||
BEFORE INSERT OR UPDATE ON vuln.advisories
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_advisory_search_vector();
|
||||
|
||||
CREATE TRIGGER trg_advisories_updated_at
|
||||
BEFORE UPDATE ON vuln.advisories
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_updated_at();
|
||||
|
||||
-- Advisory aliases table
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_aliases (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
alias_type TEXT NOT NULL,
|
||||
alias_value TEXT NOT NULL,
|
||||
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(advisory_id, alias_type, alias_value)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_aliases_advisory ON vuln.advisory_aliases(advisory_id);
|
||||
CREATE INDEX idx_advisory_aliases_value ON vuln.advisory_aliases(alias_type, alias_value);
|
||||
CREATE INDEX idx_advisory_aliases_cve ON vuln.advisory_aliases(alias_value) WHERE alias_type = 'CVE';
|
||||
|
||||
-- Advisory CVSS scores table
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_cvss (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
cvss_version TEXT NOT NULL,
|
||||
vector_string TEXT NOT NULL,
|
||||
base_score NUMERIC(3,1) NOT NULL,
|
||||
base_severity TEXT,
|
||||
exploitability_score NUMERIC(3,1),
|
||||
impact_score NUMERIC(3,1),
|
||||
source TEXT,
|
||||
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(advisory_id, cvss_version, source)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_cvss_advisory ON vuln.advisory_cvss(advisory_id);
|
||||
CREATE INDEX idx_advisory_cvss_score ON vuln.advisory_cvss(base_score DESC);
|
||||
|
||||
-- Advisory affected packages with generated columns
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_affected (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
ecosystem TEXT NOT NULL,
|
||||
package_name TEXT NOT NULL,
|
||||
purl TEXT,
|
||||
version_range JSONB NOT NULL DEFAULT '{}',
|
||||
versions_affected TEXT[],
|
||||
versions_fixed TEXT[],
|
||||
database_specific JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
-- Generated columns for PURL parsing
|
||||
purl_type TEXT GENERATED ALWAYS AS (
|
||||
CASE WHEN purl IS NOT NULL AND purl LIKE 'pkg:%'
|
||||
THEN split_part(split_part(purl, ':', 2), '/', 1) ELSE NULL END
|
||||
) STORED,
|
||||
purl_name TEXT GENERATED ALWAYS AS (
|
||||
CASE WHEN purl IS NOT NULL AND purl LIKE 'pkg:%'
|
||||
THEN split_part(split_part(split_part(purl, ':', 2), '@', 1), '/', -1) ELSE NULL END
|
||||
) STORED
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_affected_advisory ON vuln.advisory_affected(advisory_id);
|
||||
CREATE INDEX idx_advisory_affected_ecosystem ON vuln.advisory_affected(ecosystem, package_name);
|
||||
CREATE INDEX idx_advisory_affected_purl ON vuln.advisory_affected(purl);
|
||||
CREATE INDEX idx_advisory_affected_purl_trgm ON vuln.advisory_affected USING GIN(purl gin_trgm_ops);
|
||||
CREATE INDEX IF NOT EXISTS ix_advisory_affected_purl_type ON vuln.advisory_affected(purl_type) WHERE purl_type IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_advisory_affected_purl_name ON vuln.advisory_affected(purl_name) WHERE purl_name IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_advisory_affected_ecosystem_type ON vuln.advisory_affected(ecosystem, purl_type) WHERE purl_type IS NOT NULL;
|
||||
|
||||
-- Advisory references table
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_references (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
ref_type TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_references_advisory ON vuln.advisory_references(advisory_id);
|
||||
|
||||
-- Advisory credits table
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_credits (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
name TEXT NOT NULL,
|
||||
contact TEXT,
|
||||
credit_type TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_credits_advisory ON vuln.advisory_credits(advisory_id);
|
||||
|
||||
-- Advisory weaknesses table (CWE)
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_weaknesses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
cwe_id TEXT NOT NULL,
|
||||
description TEXT,
|
||||
source TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(advisory_id, cwe_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_advisory_weaknesses_advisory ON vuln.advisory_weaknesses(advisory_id);
|
||||
CREATE INDEX idx_advisory_weaknesses_cwe ON vuln.advisory_weaknesses(cwe_id);
|
||||
|
||||
-- KEV flags table
|
||||
CREATE TABLE IF NOT EXISTS vuln.kev_flags (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
advisory_id UUID NOT NULL REFERENCES vuln.advisories(id) ON DELETE CASCADE,
|
||||
cve_id TEXT NOT NULL,
|
||||
vendor_project TEXT,
|
||||
product TEXT,
|
||||
vulnerability_name TEXT,
|
||||
date_added DATE NOT NULL,
|
||||
due_date DATE,
|
||||
known_ransomware_use BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(advisory_id, cve_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_kev_flags_advisory ON vuln.kev_flags(advisory_id);
|
||||
CREATE INDEX idx_kev_flags_cve ON vuln.kev_flags(cve_id);
|
||||
CREATE INDEX idx_kev_flags_date ON vuln.kev_flags(date_added);
|
||||
|
||||
-- Source states table
|
||||
CREATE TABLE IF NOT EXISTS vuln.source_states (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
source_id UUID NOT NULL REFERENCES vuln.sources(id) UNIQUE,
|
||||
cursor TEXT,
|
||||
last_sync_at TIMESTAMPTZ,
|
||||
last_success_at TIMESTAMPTZ,
|
||||
last_error TEXT,
|
||||
sync_count BIGINT NOT NULL DEFAULT 0,
|
||||
error_count INT NOT NULL DEFAULT 0,
|
||||
metadata JSONB NOT NULL DEFAULT '{}',
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_source_states_source ON vuln.source_states(source_id);
|
||||
|
||||
CREATE TRIGGER trg_source_states_updated_at
|
||||
BEFORE UPDATE ON vuln.source_states
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_updated_at();
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 4: Partitioned Merge Events Table
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.merge_events (
|
||||
id BIGSERIAL,
|
||||
advisory_id UUID NOT NULL,
|
||||
source_id UUID,
|
||||
event_type TEXT NOT NULL,
|
||||
old_value JSONB,
|
||||
new_value JSONB,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
PRIMARY KEY (id, created_at)
|
||||
) PARTITION BY RANGE (created_at);
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
v_start DATE;
|
||||
v_end DATE;
|
||||
v_partition_name TEXT;
|
||||
BEGIN
|
||||
v_start := date_trunc('month', NOW() - INTERVAL '12 months')::DATE;
|
||||
WHILE v_start <= date_trunc('month', NOW() + INTERVAL '3 months')::DATE LOOP
|
||||
v_end := (v_start + INTERVAL '1 month')::DATE;
|
||||
v_partition_name := 'merge_events_' || to_char(v_start, 'YYYY_MM');
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = 'vuln' AND c.relname = v_partition_name
|
||||
) THEN
|
||||
EXECUTE format(
|
||||
'CREATE TABLE vuln.%I PARTITION OF vuln.merge_events FOR VALUES FROM (%L) TO (%L)',
|
||||
v_partition_name, v_start, v_end
|
||||
);
|
||||
END IF;
|
||||
v_start := v_end;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.merge_events_default PARTITION OF vuln.merge_events DEFAULT;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_merge_events_part_advisory ON vuln.merge_events(advisory_id);
|
||||
CREATE INDEX IF NOT EXISTS ix_merge_events_part_source ON vuln.merge_events(source_id) WHERE source_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS ix_merge_events_part_event_type ON vuln.merge_events(event_type);
|
||||
CREATE INDEX IF NOT EXISTS brin_merge_events_part_created ON vuln.merge_events USING BRIN(created_at) WITH (pages_per_range = 128);
|
||||
|
||||
COMMENT ON TABLE vuln.merge_events IS 'Advisory merge event log. Partitioned monthly by created_at.';
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 5: LNM Linkset Cache
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.lnm_linkset_cache (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id TEXT NOT NULL,
|
||||
source TEXT NOT NULL,
|
||||
advisory_id TEXT NOT NULL,
|
||||
observations TEXT[] NOT NULL DEFAULT '{}',
|
||||
normalized JSONB,
|
||||
conflicts JSONB,
|
||||
provenance JSONB,
|
||||
confidence DOUBLE PRECISION,
|
||||
built_by_job_id TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT uq_lnm_linkset_cache UNIQUE (tenant_id, advisory_id, source)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_lnm_linkset_cache_order ON vuln.lnm_linkset_cache(tenant_id, created_at DESC, advisory_id, source);
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 6: Sync Ledger and Site Policy
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.sync_ledger (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id TEXT NOT NULL,
|
||||
cursor TEXT NOT NULL,
|
||||
bundle_hash TEXT NOT NULL,
|
||||
items_count INT NOT NULL DEFAULT 0,
|
||||
signed_at TIMESTAMPTZ NOT NULL,
|
||||
imported_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_sync_ledger_site_cursor UNIQUE (site_id, cursor),
|
||||
CONSTRAINT uq_sync_ledger_bundle UNIQUE (bundle_hash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sync_ledger_site ON vuln.sync_ledger(site_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sync_ledger_site_time ON vuln.sync_ledger(site_id, signed_at DESC);
|
||||
|
||||
COMMENT ON TABLE vuln.sync_ledger IS 'Federation sync cursor tracking per remote site';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.site_policy (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
site_id TEXT NOT NULL UNIQUE,
|
||||
display_name TEXT,
|
||||
allowed_sources TEXT[] NOT NULL DEFAULT '{}',
|
||||
denied_sources TEXT[] NOT NULL DEFAULT '{}',
|
||||
max_bundle_size_mb INT NOT NULL DEFAULT 100,
|
||||
max_items_per_bundle INT NOT NULL DEFAULT 10000,
|
||||
require_signature BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
allowed_signers TEXT[] NOT NULL DEFAULT '{}',
|
||||
enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_site_policy_enabled ON vuln.site_policy(enabled) WHERE enabled = TRUE;
|
||||
|
||||
CREATE TRIGGER trg_site_policy_updated
|
||||
BEFORE UPDATE ON vuln.site_policy
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_timestamp();
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 7: Advisory Canonical and Source Edge
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_canonical (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
cve TEXT NOT NULL,
|
||||
affects_key TEXT NOT NULL,
|
||||
version_range JSONB,
|
||||
weakness TEXT[] NOT NULL DEFAULT '{}',
|
||||
merge_hash TEXT NOT NULL,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'stub', 'withdrawn')),
|
||||
severity TEXT CHECK (severity IN ('critical', 'high', 'medium', 'low', 'none', 'unknown')),
|
||||
epss_score NUMERIC(5,4),
|
||||
exploit_known BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
title TEXT,
|
||||
summary TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_advisory_canonical_merge_hash UNIQUE (merge_hash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_cve ON vuln.advisory_canonical(cve);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_affects ON vuln.advisory_canonical(affects_key);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_merge_hash ON vuln.advisory_canonical(merge_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_status ON vuln.advisory_canonical(status) WHERE status = 'active';
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_severity ON vuln.advisory_canonical(severity) WHERE severity IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_exploit ON vuln.advisory_canonical(exploit_known) WHERE exploit_known = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_advisory_canonical_updated ON vuln.advisory_canonical(updated_at DESC);
|
||||
|
||||
CREATE TRIGGER trg_advisory_canonical_updated
|
||||
BEFORE UPDATE ON vuln.advisory_canonical
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_timestamp();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.advisory_source_edge (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
canonical_id UUID NOT NULL REFERENCES vuln.advisory_canonical(id) ON DELETE CASCADE,
|
||||
source_id UUID NOT NULL REFERENCES vuln.sources(id) ON DELETE RESTRICT,
|
||||
source_advisory_id TEXT NOT NULL,
|
||||
source_doc_hash TEXT NOT NULL,
|
||||
vendor_status TEXT CHECK (vendor_status IN ('affected', 'not_affected', 'fixed', 'under_investigation')),
|
||||
precedence_rank INT NOT NULL DEFAULT 100,
|
||||
dsse_envelope JSONB,
|
||||
raw_payload JSONB,
|
||||
fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_advisory_source_edge_unique UNIQUE (canonical_id, source_id, source_doc_hash)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_canonical ON vuln.advisory_source_edge(canonical_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_source ON vuln.advisory_source_edge(source_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_advisory_id ON vuln.advisory_source_edge(source_advisory_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_canonical_source ON vuln.advisory_source_edge(canonical_id, source_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_fetched ON vuln.advisory_source_edge(fetched_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_edge_dsse_gin ON vuln.advisory_source_edge USING GIN(dsse_envelope jsonb_path_ops);
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 8: Interest Score and SBOM Registry
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.interest_score (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
canonical_id UUID NOT NULL REFERENCES vuln.advisory_canonical(id) ON DELETE CASCADE,
|
||||
score NUMERIC(3,2) NOT NULL CHECK (score >= 0 AND score <= 1),
|
||||
reasons JSONB NOT NULL DEFAULT '[]',
|
||||
last_seen_in_build UUID,
|
||||
computed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_interest_score_canonical UNIQUE (canonical_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_interest_score_score ON vuln.interest_score(score DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_interest_score_computed ON vuln.interest_score(computed_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_interest_score_high ON vuln.interest_score(canonical_id) WHERE score >= 0.7;
|
||||
CREATE INDEX IF NOT EXISTS idx_interest_score_low ON vuln.interest_score(canonical_id) WHERE score < 0.2;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.sbom_registry (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
digest TEXT NOT NULL,
|
||||
format TEXT NOT NULL CHECK (format IN ('cyclonedx', 'spdx')),
|
||||
spec_version TEXT NOT NULL,
|
||||
primary_name TEXT,
|
||||
primary_version TEXT,
|
||||
component_count INT NOT NULL DEFAULT 0,
|
||||
affected_count INT NOT NULL DEFAULT 0,
|
||||
source TEXT NOT NULL,
|
||||
tenant_id TEXT,
|
||||
registered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_matched_at TIMESTAMPTZ,
|
||||
CONSTRAINT uq_sbom_registry_digest UNIQUE (digest)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_registry_tenant ON vuln.sbom_registry(tenant_id) WHERE tenant_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_registry_primary ON vuln.sbom_registry(primary_name, primary_version);
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_registry_registered ON vuln.sbom_registry(registered_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_registry_affected ON vuln.sbom_registry(affected_count DESC) WHERE affected_count > 0;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.sbom_canonical_match (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
sbom_id UUID NOT NULL REFERENCES vuln.sbom_registry(id) ON DELETE CASCADE,
|
||||
canonical_id UUID NOT NULL REFERENCES vuln.advisory_canonical(id) ON DELETE CASCADE,
|
||||
purl TEXT NOT NULL,
|
||||
match_method TEXT NOT NULL CHECK (match_method IN ('exact_purl', 'purl_version_range', 'cpe', 'name_version')),
|
||||
confidence NUMERIC(3,2) NOT NULL DEFAULT 1.0 CHECK (confidence >= 0 AND confidence <= 1),
|
||||
is_reachable BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
is_deployed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
matched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_sbom_canonical_match UNIQUE (sbom_id, canonical_id, purl)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_match_sbom ON vuln.sbom_canonical_match(sbom_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_match_canonical ON vuln.sbom_canonical_match(canonical_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_match_purl ON vuln.sbom_canonical_match(purl);
|
||||
CREATE INDEX IF NOT EXISTS idx_sbom_match_reachable ON vuln.sbom_canonical_match(canonical_id) WHERE is_reachable = TRUE;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.purl_canonical_index (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
purl TEXT NOT NULL,
|
||||
purl_type TEXT NOT NULL,
|
||||
purl_namespace TEXT,
|
||||
purl_name TEXT NOT NULL,
|
||||
canonical_id UUID NOT NULL REFERENCES vuln.advisory_canonical(id) ON DELETE CASCADE,
|
||||
version_constraint TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_purl_canonical UNIQUE (purl, canonical_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_purl_index_lookup ON vuln.purl_canonical_index(purl_type, purl_namespace, purl_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_purl_index_canonical ON vuln.purl_canonical_index(canonical_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 9: Provenance Scope
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS vuln.provenance_scope (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
canonical_id UUID NOT NULL REFERENCES vuln.advisory_canonical(id) ON DELETE CASCADE,
|
||||
distro_release TEXT NOT NULL,
|
||||
backport_semver TEXT,
|
||||
patch_id TEXT,
|
||||
patch_origin TEXT CHECK (patch_origin IN ('upstream', 'distro', 'vendor')),
|
||||
evidence_ref UUID,
|
||||
confidence NUMERIC(3,2) NOT NULL DEFAULT 0.5 CHECK (confidence >= 0 AND confidence <= 1),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT uq_provenance_scope_canonical_distro UNIQUE (canonical_id, distro_release)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_canonical ON vuln.provenance_scope(canonical_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_distro ON vuln.provenance_scope(distro_release);
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_patch ON vuln.provenance_scope(patch_id) WHERE patch_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_high_confidence ON vuln.provenance_scope(confidence DESC) WHERE confidence >= 0.7;
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_origin ON vuln.provenance_scope(patch_origin) WHERE patch_origin IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_provenance_scope_updated ON vuln.provenance_scope(updated_at DESC);
|
||||
|
||||
CREATE TRIGGER trg_provenance_scope_updated
|
||||
BEFORE UPDATE ON vuln.provenance_scope
|
||||
FOR EACH ROW EXECUTE FUNCTION vuln.update_timestamp();
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 10: Concelier Schema Tables
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.source_documents (
|
||||
id UUID NOT NULL,
|
||||
source_id UUID NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
uri TEXT NOT NULL,
|
||||
sha256 TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
content_type TEXT,
|
||||
headers_json JSONB,
|
||||
metadata_json JSONB,
|
||||
etag TEXT,
|
||||
last_modified TIMESTAMPTZ,
|
||||
payload BYTEA NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMPTZ,
|
||||
CONSTRAINT pk_source_documents PRIMARY KEY (source_name, uri)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_source_documents_source_id ON concelier.source_documents(source_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_source_documents_status ON concelier.source_documents(status);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.dtos (
|
||||
id UUID NOT NULL,
|
||||
document_id UUID NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
format TEXT NOT NULL,
|
||||
payload_json JSONB NOT NULL,
|
||||
schema_version TEXT NOT NULL DEFAULT '',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
validated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT pk_concelier_dtos PRIMARY KEY (document_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_concelier_dtos_source ON concelier.dtos(source_name, created_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.export_states (
|
||||
id TEXT NOT NULL,
|
||||
export_cursor TEXT NOT NULL,
|
||||
last_full_digest TEXT,
|
||||
last_delta_digest TEXT,
|
||||
base_export_id TEXT,
|
||||
base_digest TEXT,
|
||||
target_repository TEXT,
|
||||
files JSONB NOT NULL,
|
||||
exporter_version TEXT NOT NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT pk_concelier_export_states PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.psirt_flags (
|
||||
advisory_id TEXT NOT NULL,
|
||||
vendor TEXT NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
external_id TEXT,
|
||||
recorded_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT pk_concelier_psirt_flags PRIMARY KEY (advisory_id, vendor)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_concelier_psirt_source ON concelier.psirt_flags(source_name, recorded_at DESC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.jp_flags (
|
||||
advisory_key TEXT NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
category TEXT NOT NULL,
|
||||
vendor_status TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT pk_concelier_jp_flags PRIMARY KEY (advisory_key)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS concelier.change_history (
|
||||
id UUID NOT NULL,
|
||||
source_name TEXT NOT NULL,
|
||||
advisory_key TEXT NOT NULL,
|
||||
document_id UUID NOT NULL,
|
||||
document_hash TEXT NOT NULL,
|
||||
snapshot_hash TEXT NOT NULL,
|
||||
previous_snapshot_hash TEXT,
|
||||
snapshot JSONB NOT NULL,
|
||||
previous_snapshot JSONB,
|
||||
changes JSONB NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL,
|
||||
CONSTRAINT pk_concelier_change_history PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_concelier_change_history_advisory ON concelier.change_history(advisory_key, created_at DESC);
|
||||
|
||||
-- ============================================================================
|
||||
-- SECTION 11: Helper Functions for Canonical Operations
|
||||
-- ============================================================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.get_canonical_by_hash(p_merge_hash TEXT)
|
||||
RETURNS vuln.advisory_canonical
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
SELECT * FROM vuln.advisory_canonical WHERE merge_hash = p_merge_hash;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.get_source_edges(p_canonical_id UUID)
|
||||
RETURNS SETOF vuln.advisory_source_edge
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
SELECT * FROM vuln.advisory_source_edge
|
||||
WHERE canonical_id = p_canonical_id
|
||||
ORDER BY precedence_rank ASC, fetched_at DESC;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.upsert_canonical(
|
||||
p_cve TEXT, p_affects_key TEXT, p_version_range JSONB, p_weakness TEXT[],
|
||||
p_merge_hash TEXT, p_severity TEXT DEFAULT NULL, p_epss_score NUMERIC DEFAULT NULL,
|
||||
p_exploit_known BOOLEAN DEFAULT FALSE, p_title TEXT DEFAULT NULL, p_summary TEXT DEFAULT NULL
|
||||
)
|
||||
RETURNS UUID
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE v_id UUID;
|
||||
BEGIN
|
||||
INSERT INTO vuln.advisory_canonical (
|
||||
cve, affects_key, version_range, weakness, merge_hash,
|
||||
severity, epss_score, exploit_known, title, summary
|
||||
) VALUES (
|
||||
p_cve, p_affects_key, p_version_range, p_weakness, p_merge_hash,
|
||||
p_severity, p_epss_score, p_exploit_known, p_title, p_summary
|
||||
)
|
||||
ON CONFLICT (merge_hash) DO UPDATE SET
|
||||
severity = COALESCE(EXCLUDED.severity, vuln.advisory_canonical.severity),
|
||||
epss_score = COALESCE(EXCLUDED.epss_score, vuln.advisory_canonical.epss_score),
|
||||
exploit_known = EXCLUDED.exploit_known OR vuln.advisory_canonical.exploit_known,
|
||||
title = COALESCE(EXCLUDED.title, vuln.advisory_canonical.title),
|
||||
summary = COALESCE(EXCLUDED.summary, vuln.advisory_canonical.summary),
|
||||
updated_at = NOW()
|
||||
RETURNING id INTO v_id;
|
||||
RETURN v_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.add_source_edge(
|
||||
p_canonical_id UUID, p_source_id UUID, p_source_advisory_id TEXT, p_source_doc_hash TEXT,
|
||||
p_vendor_status TEXT DEFAULT NULL, p_precedence_rank INT DEFAULT 100,
|
||||
p_dsse_envelope JSONB DEFAULT NULL, p_raw_payload JSONB DEFAULT NULL, p_fetched_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)
|
||||
RETURNS UUID
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE v_id UUID;
|
||||
BEGIN
|
||||
INSERT INTO vuln.advisory_source_edge (
|
||||
canonical_id, source_id, source_advisory_id, source_doc_hash,
|
||||
vendor_status, precedence_rank, dsse_envelope, raw_payload, fetched_at
|
||||
) VALUES (
|
||||
p_canonical_id, p_source_id, p_source_advisory_id, p_source_doc_hash,
|
||||
p_vendor_status, p_precedence_rank, p_dsse_envelope, p_raw_payload, p_fetched_at
|
||||
)
|
||||
ON CONFLICT (canonical_id, source_id, source_doc_hash) DO UPDATE SET
|
||||
vendor_status = COALESCE(EXCLUDED.vendor_status, vuln.advisory_source_edge.vendor_status),
|
||||
precedence_rank = LEAST(EXCLUDED.precedence_rank, vuln.advisory_source_edge.precedence_rank),
|
||||
dsse_envelope = COALESCE(EXCLUDED.dsse_envelope, vuln.advisory_source_edge.dsse_envelope),
|
||||
raw_payload = COALESCE(EXCLUDED.raw_payload, vuln.advisory_source_edge.raw_payload)
|
||||
RETURNING id INTO v_id;
|
||||
RETURN v_id;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION vuln.count_canonicals_by_cve_year(p_year INT)
|
||||
RETURNS BIGINT
|
||||
LANGUAGE sql STABLE
|
||||
AS $$
|
||||
SELECT COUNT(*) FROM vuln.advisory_canonical
|
||||
WHERE cve LIKE 'CVE-' || p_year::TEXT || '-%' AND status = 'active';
|
||||
$$;
|
||||
|
||||
COMMIT;
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Concelier.Models;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Advisories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Advisories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL advisory storage interface.
|
||||
@@ -2,12 +2,12 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Conversion;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Conversion;
|
||||
using AdvisoryContracts = StellaOps.Concelier.Storage.Advisories;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Advisories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Advisories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL implementation of advisory storage.
|
||||
@@ -4,7 +4,7 @@ using Npgsql;
|
||||
using StellaOps.Infrastructure.Postgres.Connections;
|
||||
using StellaOps.Infrastructure.Postgres.Options;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL data source for the Concelier (vulnerability) module.
|
||||
@@ -5,7 +5,7 @@ using StellaOps.Concelier.Documents.IO;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using LegacyContracts = StellaOps.Concelier.Storage;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres;
|
||||
|
||||
internal static class ContractsMappingExtensions
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Conversion;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Conversion;
|
||||
|
||||
/// <summary>
|
||||
/// Result of converting an advisory document to PostgreSQL entities.
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Concelier.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Conversion;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Conversion;
|
||||
|
||||
/// <summary>
|
||||
/// Converts domain advisories to PostgreSQL entity structures.
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using StellaOps.Concelier.Storage;
|
||||
using Contracts = StellaOps.Concelier.Storage.Contracts;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres;
|
||||
|
||||
/// <summary>
|
||||
/// Postgres-backed implementation that satisfies the legacy IDocumentStore contract and the new Postgres-native storage contract.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an affected package entry for an advisory.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an advisory alias (e.g., CVE, GHSA).
|
||||
@@ -5,7 +5,7 @@
|
||||
// Description: Entity for deduplicated canonical advisory records
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a deduplicated canonical advisory in the vuln schema.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a credit entry for an advisory.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a CVSS score for an advisory.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an advisory entity in the vuln schema.
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a cached Link-Not-Merge linkset snapshot stored in PostgreSQL.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an advisory reference URL.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a snapshot of an advisory at a point in time.
|
||||
@@ -5,7 +5,7 @@
|
||||
// Description: Entity linking canonical advisory to source documents with DSSE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a link between a canonical advisory and its source document.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a CWE weakness linked to an advisory.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
public sealed record DocumentRecordEntity(
|
||||
Guid Id,
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a feed snapshot record.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Known Exploited Vulnerability flag entry.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a merge event audit record.
|
||||
@@ -5,7 +5,7 @@
|
||||
// Description: Entity for distro-specific backport and patch provenance
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents distro-specific backport and patch provenance per canonical advisory.
|
||||
@@ -5,7 +5,7 @@
|
||||
// Description: Entity for per-site federation governance policies
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a site federation policy for governance control.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a vulnerability feed source entity.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Tracks source ingestion cursors and metrics.
|
||||
@@ -5,7 +5,7 @@
|
||||
// Description: Entity for tracking federation sync state per remote site
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Models;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sync ledger entry for federation cursor tracking.
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for advisory affected packages.
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for advisory aliases.
|
||||
@@ -8,10 +8,10 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for canonical advisory and source edge operations.
|
||||
@@ -1,9 +1,9 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using StellaOps.Concelier.Storage.Postgres.Models;
|
||||
using StellaOps.Concelier.Persistence.Postgres.Models;
|
||||
using StellaOps.Infrastructure.Postgres.Repositories;
|
||||
|
||||
namespace StellaOps.Concelier.Storage.Postgres.Repositories;
|
||||
namespace StellaOps.Concelier.Persistence.Postgres.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// PostgreSQL repository for advisory credits.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user