Extend Vexer attestation/export stack and Concelier OSV fixes
This commit is contained in:
@@ -3,11 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using StellaOps.Vexer.Core;
|
||||
|
||||
namespace StellaOps.Vexer.Storage.Mongo;
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexRawDocumentRecord
|
||||
{
|
||||
[BsonId]
|
||||
@@ -26,9 +28,13 @@ internal sealed class VexRawDocumentRecord
|
||||
|
||||
public byte[] Content { get; set; } = Array.Empty<byte>();
|
||||
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string? GridFsObjectId { get; set; }
|
||||
= null;
|
||||
|
||||
public Dictionary<string, string> Metadata { get; set; } = new(StringComparer.Ordinal);
|
||||
|
||||
public static VexRawDocumentRecord FromDomain(VexRawDocument document)
|
||||
public static VexRawDocumentRecord FromDomain(VexRawDocument document, bool includeContent = true)
|
||||
=> new()
|
||||
{
|
||||
Id = document.Digest,
|
||||
@@ -37,22 +43,26 @@ internal sealed class VexRawDocumentRecord
|
||||
SourceUri = document.SourceUri.ToString(),
|
||||
RetrievedAt = document.RetrievedAt.UtcDateTime,
|
||||
Digest = document.Digest,
|
||||
Content = document.Content.ToArray(),
|
||||
Content = includeContent ? document.Content.ToArray() : Array.Empty<byte>(),
|
||||
Metadata = document.Metadata.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.Ordinal),
|
||||
};
|
||||
|
||||
public VexRawDocument ToDomain()
|
||||
=> ToDomain(new ReadOnlyMemory<byte>(Content ?? Array.Empty<byte>()));
|
||||
|
||||
public VexRawDocument ToDomain(ReadOnlyMemory<byte> content)
|
||||
=> new(
|
||||
ProviderId,
|
||||
Enum.Parse<VexDocumentFormat>(Format, ignoreCase: true),
|
||||
new Uri(SourceUri),
|
||||
RetrievedAt,
|
||||
Digest,
|
||||
new ReadOnlyMemory<byte>(Content ?? Array.Empty<byte>()),
|
||||
content,
|
||||
(Metadata ?? new Dictionary<string, string>(StringComparer.Ordinal))
|
||||
.ToImmutableDictionary(StringComparer.Ordinal));
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexExportManifestRecord
|
||||
{
|
||||
[BsonId]
|
||||
@@ -162,5 +172,401 @@ internal sealed class VexExportManifestRecord
|
||||
}
|
||||
|
||||
public static string CreateId(VexQuerySignature signature, VexExportFormat format)
|
||||
=> string.Create(CultureInfo.InvariantCulture, $"{signature.Value}|{format.ToString().ToLowerInvariant()}");
|
||||
=> string.Format(CultureInfo.InvariantCulture, "{0}|{1}", signature.Value, format.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexProviderRecord
|
||||
{
|
||||
[BsonId]
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
public string DisplayName { get; set; } = default!;
|
||||
|
||||
public string Kind { get; set; } = default!;
|
||||
|
||||
public List<string> BaseUris { get; set; } = new();
|
||||
|
||||
public VexProviderDiscoveryDocument? Discovery { get; set; }
|
||||
= null;
|
||||
|
||||
public VexProviderTrustDocument? Trust { get; set; }
|
||||
= null;
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
= true;
|
||||
|
||||
public static VexProviderRecord FromDomain(VexProvider provider)
|
||||
=> new()
|
||||
{
|
||||
Id = provider.Id,
|
||||
DisplayName = provider.DisplayName,
|
||||
Kind = provider.Kind.ToString().ToLowerInvariant(),
|
||||
BaseUris = provider.BaseUris.Select(uri => uri.ToString()).ToList(),
|
||||
Discovery = VexProviderDiscoveryDocument.FromDomain(provider.Discovery),
|
||||
Trust = VexProviderTrustDocument.FromDomain(provider.Trust),
|
||||
Enabled = provider.Enabled,
|
||||
};
|
||||
|
||||
public VexProvider ToDomain()
|
||||
{
|
||||
var uris = BaseUris?.Select(uri => new Uri(uri)) ?? Enumerable.Empty<Uri>();
|
||||
return new VexProvider(
|
||||
Id,
|
||||
DisplayName,
|
||||
Enum.Parse<VexProviderKind>(Kind, ignoreCase: true),
|
||||
uris,
|
||||
Discovery?.ToDomain(),
|
||||
Trust?.ToDomain(),
|
||||
Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexProviderDiscoveryDocument
|
||||
{
|
||||
public string? WellKnownMetadata { get; set; }
|
||||
= null;
|
||||
|
||||
public string? RolIeService { get; set; }
|
||||
= null;
|
||||
|
||||
public static VexProviderDiscoveryDocument? FromDomain(VexProviderDiscovery? discovery)
|
||||
=> discovery is null
|
||||
? null
|
||||
: new VexProviderDiscoveryDocument
|
||||
{
|
||||
WellKnownMetadata = discovery.WellKnownMetadata?.ToString(),
|
||||
RolIeService = discovery.RolIeService?.ToString(),
|
||||
};
|
||||
|
||||
public VexProviderDiscovery ToDomain()
|
||||
=> new(
|
||||
WellKnownMetadata is null ? null : new Uri(WellKnownMetadata),
|
||||
RolIeService is null ? null : new Uri(RolIeService));
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexProviderTrustDocument
|
||||
{
|
||||
public double Weight { get; set; }
|
||||
= 1.0;
|
||||
|
||||
public VexCosignTrustDocument? Cosign { get; set; }
|
||||
= null;
|
||||
|
||||
public List<string> PgpFingerprints { get; set; } = new();
|
||||
|
||||
public static VexProviderTrustDocument? FromDomain(VexProviderTrust? trust)
|
||||
=> trust is null
|
||||
? null
|
||||
: new VexProviderTrustDocument
|
||||
{
|
||||
Weight = trust.Weight,
|
||||
Cosign = trust.Cosign is null ? null : VexCosignTrustDocument.FromDomain(trust.Cosign),
|
||||
PgpFingerprints = trust.PgpFingerprints.ToList(),
|
||||
};
|
||||
|
||||
public VexProviderTrust ToDomain()
|
||||
=> new(
|
||||
Weight,
|
||||
Cosign?.ToDomain(),
|
||||
PgpFingerprints);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexCosignTrustDocument
|
||||
{
|
||||
public string Issuer { get; set; } = default!;
|
||||
|
||||
public string IdentityPattern { get; set; } = default!;
|
||||
|
||||
public static VexCosignTrustDocument FromDomain(VexCosignTrust trust)
|
||||
=> new()
|
||||
{
|
||||
Issuer = trust.Issuer,
|
||||
IdentityPattern = trust.IdentityPattern,
|
||||
};
|
||||
|
||||
public VexCosignTrust ToDomain()
|
||||
=> new(Issuer, IdentityPattern);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexConsensusRecord
|
||||
{
|
||||
[BsonId]
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
public string VulnerabilityId { get; set; } = default!;
|
||||
|
||||
public VexProductDocument Product { get; set; } = default!;
|
||||
|
||||
public string Status { get; set; } = default!;
|
||||
|
||||
public DateTime CalculatedAt { get; set; }
|
||||
= DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
||||
|
||||
public List<VexConsensusSourceDocument> Sources { get; set; } = new();
|
||||
|
||||
public List<VexConsensusConflictDocument> Conflicts { get; set; } = new();
|
||||
|
||||
public string? PolicyVersion { get; set; }
|
||||
= null;
|
||||
|
||||
public string? PolicyRevisionId { get; set; }
|
||||
= null;
|
||||
|
||||
public string? PolicyDigest { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Summary { get; set; }
|
||||
= null;
|
||||
|
||||
public static string CreateId(string vulnerabilityId, string productKey)
|
||||
=> string.Format(CultureInfo.InvariantCulture, "{0}|{1}", vulnerabilityId.Trim(), productKey.Trim());
|
||||
|
||||
public static VexConsensusRecord FromDomain(VexConsensus consensus)
|
||||
=> new()
|
||||
{
|
||||
Id = CreateId(consensus.VulnerabilityId, consensus.Product.Key),
|
||||
VulnerabilityId = consensus.VulnerabilityId,
|
||||
Product = VexProductDocument.FromDomain(consensus.Product),
|
||||
Status = consensus.Status.ToString().ToLowerInvariant(),
|
||||
CalculatedAt = consensus.CalculatedAt.UtcDateTime,
|
||||
Sources = consensus.Sources.Select(VexConsensusSourceDocument.FromDomain).ToList(),
|
||||
Conflicts = consensus.Conflicts.Select(VexConsensusConflictDocument.FromDomain).ToList(),
|
||||
PolicyVersion = consensus.PolicyVersion,
|
||||
PolicyRevisionId = consensus.PolicyRevisionId,
|
||||
PolicyDigest = consensus.PolicyDigest,
|
||||
Summary = consensus.Summary,
|
||||
};
|
||||
|
||||
public VexConsensus ToDomain()
|
||||
=> new(
|
||||
VulnerabilityId,
|
||||
Product.ToDomain(),
|
||||
Enum.Parse<VexConsensusStatus>(Status, ignoreCase: true),
|
||||
new DateTimeOffset(CalculatedAt, TimeSpan.Zero),
|
||||
Sources.Select(static source => source.ToDomain()),
|
||||
Conflicts.Select(static conflict => conflict.ToDomain()),
|
||||
PolicyVersion,
|
||||
Summary,
|
||||
PolicyRevisionId,
|
||||
PolicyDigest);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexProductDocument
|
||||
{
|
||||
public string Key { get; set; } = default!;
|
||||
|
||||
public string? Name { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Version { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Purl { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Cpe { get; set; }
|
||||
= null;
|
||||
|
||||
public List<string> ComponentIdentifiers { get; set; } = new();
|
||||
|
||||
public static VexProductDocument FromDomain(VexProduct product)
|
||||
=> new()
|
||||
{
|
||||
Key = product.Key,
|
||||
Name = product.Name,
|
||||
Version = product.Version,
|
||||
Purl = product.Purl,
|
||||
Cpe = product.Cpe,
|
||||
ComponentIdentifiers = product.ComponentIdentifiers.ToList(),
|
||||
};
|
||||
|
||||
public VexProduct ToDomain()
|
||||
=> new(
|
||||
Key,
|
||||
Name,
|
||||
Version,
|
||||
Purl,
|
||||
Cpe,
|
||||
ComponentIdentifiers);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexConsensusSourceDocument
|
||||
{
|
||||
public string ProviderId { get; set; } = default!;
|
||||
|
||||
public string Status { get; set; } = default!;
|
||||
|
||||
public string DocumentDigest { get; set; } = default!;
|
||||
|
||||
public double Weight { get; set; }
|
||||
= 0;
|
||||
|
||||
public string? Justification { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Detail { get; set; }
|
||||
= null;
|
||||
|
||||
public VexConfidenceDocument? Confidence { get; set; }
|
||||
= null;
|
||||
|
||||
public static VexConsensusSourceDocument FromDomain(VexConsensusSource source)
|
||||
=> new()
|
||||
{
|
||||
ProviderId = source.ProviderId,
|
||||
Status = source.Status.ToString().ToLowerInvariant(),
|
||||
DocumentDigest = source.DocumentDigest,
|
||||
Weight = source.Weight,
|
||||
Justification = source.Justification?.ToString().ToLowerInvariant(),
|
||||
Detail = source.Detail,
|
||||
Confidence = source.Confidence is null ? null : VexConfidenceDocument.FromDomain(source.Confidence),
|
||||
};
|
||||
|
||||
public VexConsensusSource ToDomain()
|
||||
=> new(
|
||||
ProviderId,
|
||||
Enum.Parse<VexClaimStatus>(Status, ignoreCase: true),
|
||||
DocumentDigest,
|
||||
Weight,
|
||||
string.IsNullOrWhiteSpace(Justification) ? null : Enum.Parse<VexJustification>(Justification, ignoreCase: true),
|
||||
Detail,
|
||||
Confidence?.ToDomain());
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexConsensusConflictDocument
|
||||
{
|
||||
public string ProviderId { get; set; } = default!;
|
||||
|
||||
public string Status { get; set; } = default!;
|
||||
|
||||
public string DocumentDigest { get; set; } = default!;
|
||||
|
||||
public string? Justification { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Detail { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Reason { get; set; }
|
||||
= null;
|
||||
|
||||
public static VexConsensusConflictDocument FromDomain(VexConsensusConflict conflict)
|
||||
=> new()
|
||||
{
|
||||
ProviderId = conflict.ProviderId,
|
||||
Status = conflict.Status.ToString().ToLowerInvariant(),
|
||||
DocumentDigest = conflict.DocumentDigest,
|
||||
Justification = conflict.Justification?.ToString().ToLowerInvariant(),
|
||||
Detail = conflict.Detail,
|
||||
Reason = conflict.Reason,
|
||||
};
|
||||
|
||||
public VexConsensusConflict ToDomain()
|
||||
=> new(
|
||||
ProviderId,
|
||||
Enum.Parse<VexClaimStatus>(Status, ignoreCase: true),
|
||||
DocumentDigest,
|
||||
string.IsNullOrWhiteSpace(Justification) ? null : Enum.Parse<VexJustification>(Justification, ignoreCase: true),
|
||||
Detail,
|
||||
Reason);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexConfidenceDocument
|
||||
{
|
||||
public string Level { get; set; } = default!;
|
||||
|
||||
public double? Score { get; set; }
|
||||
= null;
|
||||
|
||||
public string? Method { get; set; }
|
||||
= null;
|
||||
|
||||
public static VexConfidenceDocument FromDomain(VexConfidence confidence)
|
||||
=> new()
|
||||
{
|
||||
Level = confidence.Level,
|
||||
Score = confidence.Score,
|
||||
Method = confidence.Method,
|
||||
};
|
||||
|
||||
public VexConfidence ToDomain()
|
||||
=> new(Level, Score, Method);
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
internal sealed class VexCacheEntryRecord
|
||||
{
|
||||
[BsonId]
|
||||
public string Id { get; set; } = default!;
|
||||
|
||||
public string QuerySignature { get; set; } = default!;
|
||||
|
||||
public string Format { get; set; } = default!;
|
||||
|
||||
public string ArtifactAlgorithm { get; set; } = default!;
|
||||
|
||||
public string ArtifactDigest { get; set; } = default!;
|
||||
|
||||
public DateTime CreatedAt { get; set; }
|
||||
= DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
||||
|
||||
public long SizeBytes { get; set; }
|
||||
= 0;
|
||||
|
||||
public string? ManifestId { get; set; }
|
||||
= null;
|
||||
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string? GridFsObjectId { get; set; }
|
||||
= null;
|
||||
|
||||
public DateTime? ExpiresAt { get; set; }
|
||||
= null;
|
||||
|
||||
public static string CreateId(VexQuerySignature signature, VexExportFormat format)
|
||||
=> string.Format(CultureInfo.InvariantCulture, "{0}|{1}", signature.Value, format.ToString().ToLowerInvariant());
|
||||
|
||||
public static VexCacheEntryRecord FromDomain(VexCacheEntry entry)
|
||||
=> new()
|
||||
{
|
||||
Id = CreateId(entry.QuerySignature, entry.Format),
|
||||
QuerySignature = entry.QuerySignature.Value,
|
||||
Format = entry.Format.ToString().ToLowerInvariant(),
|
||||
ArtifactAlgorithm = entry.Artifact.Algorithm,
|
||||
ArtifactDigest = entry.Artifact.Digest,
|
||||
CreatedAt = entry.CreatedAt.UtcDateTime,
|
||||
SizeBytes = entry.SizeBytes,
|
||||
ManifestId = entry.ManifestId,
|
||||
GridFsObjectId = entry.GridFsObjectId,
|
||||
ExpiresAt = entry.ExpiresAt?.UtcDateTime,
|
||||
};
|
||||
|
||||
public VexCacheEntry ToDomain()
|
||||
{
|
||||
var signature = new VexQuerySignature(QuerySignature);
|
||||
var artifact = new VexContentAddress(ArtifactAlgorithm, ArtifactDigest);
|
||||
var createdAt = new DateTimeOffset(DateTime.SpecifyKind(CreatedAt, DateTimeKind.Utc));
|
||||
var expires = ExpiresAt.HasValue
|
||||
? new DateTimeOffset(DateTime.SpecifyKind(ExpiresAt.Value, DateTimeKind.Utc))
|
||||
: (DateTimeOffset?)null;
|
||||
|
||||
return new VexCacheEntry(
|
||||
signature,
|
||||
Enum.Parse<VexExportFormat>(Format, ignoreCase: true),
|
||||
artifact,
|
||||
createdAt,
|
||||
SizeBytes,
|
||||
ManifestId,
|
||||
GridFsObjectId,
|
||||
expires);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user