Implement VEX document verification system with issuer management and signature verification
- Added IIssuerDirectory interface for managing VEX document issuers, including methods for registration, revocation, and trust validation. - Created InMemoryIssuerDirectory class as an in-memory implementation of IIssuerDirectory for testing and single-instance deployments. - Introduced ISignatureVerifier interface for verifying signatures on VEX documents, with support for multiple signature formats. - Developed SignatureVerifier class as the default implementation of ISignatureVerifier, allowing extensibility for different signature formats. - Implemented handlers for DSSE and JWS signature formats, including methods for verification and signature extraction. - Defined various records and enums for issuer and signature metadata, enhancing the structure and clarity of the verification process.
This commit is contained in:
183
src/VexLens/StellaOps.VexLens/Models/NormalizedVexModels.cs
Normal file
183
src/VexLens/StellaOps.VexLens/Models/NormalizedVexModels.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace StellaOps.VexLens.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Normalized VEX document per vex-normalization.schema.json.
|
||||
/// Supports OpenVEX, CSAF VEX, CycloneDX VEX, SPDX VEX, and StellaOps formats.
|
||||
/// </summary>
|
||||
public sealed record NormalizedVexDocument(
|
||||
[property: JsonPropertyName("schemaVersion")] int SchemaVersion,
|
||||
[property: JsonPropertyName("documentId")] string DocumentId,
|
||||
[property: JsonPropertyName("sourceFormat")] VexSourceFormat SourceFormat,
|
||||
[property: JsonPropertyName("sourceDigest")] string? SourceDigest,
|
||||
[property: JsonPropertyName("sourceUri")] string? SourceUri,
|
||||
[property: JsonPropertyName("issuer")] VexIssuer? Issuer,
|
||||
[property: JsonPropertyName("issuedAt")] DateTimeOffset? IssuedAt,
|
||||
[property: JsonPropertyName("lastUpdatedAt")] DateTimeOffset? LastUpdatedAt,
|
||||
[property: JsonPropertyName("statements")] IReadOnlyList<NormalizedStatement> Statements,
|
||||
[property: JsonPropertyName("provenance")] NormalizationProvenance? Provenance)
|
||||
{
|
||||
public const int CurrentSchemaVersion = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Original VEX document format before normalization.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<VexSourceFormat>))]
|
||||
public enum VexSourceFormat
|
||||
{
|
||||
[JsonPropertyName("OPENVEX")]
|
||||
OpenVex,
|
||||
|
||||
[JsonPropertyName("CSAF_VEX")]
|
||||
CsafVex,
|
||||
|
||||
[JsonPropertyName("CYCLONEDX_VEX")]
|
||||
CycloneDxVex,
|
||||
|
||||
[JsonPropertyName("SPDX_VEX")]
|
||||
SpdxVex,
|
||||
|
||||
[JsonPropertyName("STELLAOPS")]
|
||||
StellaOps
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issuing authority for a VEX document.
|
||||
/// </summary>
|
||||
public sealed record VexIssuer(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("name")] string Name,
|
||||
[property: JsonPropertyName("category")] IssuerCategory? Category,
|
||||
[property: JsonPropertyName("trustTier")] TrustTier? TrustTier,
|
||||
[property: JsonPropertyName("keyFingerprints")] IReadOnlyList<string>? KeyFingerprints);
|
||||
|
||||
/// <summary>
|
||||
/// Issuer category for trust weighting.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<IssuerCategory>))]
|
||||
public enum IssuerCategory
|
||||
{
|
||||
[JsonPropertyName("VENDOR")]
|
||||
Vendor,
|
||||
|
||||
[JsonPropertyName("DISTRIBUTOR")]
|
||||
Distributor,
|
||||
|
||||
[JsonPropertyName("COMMUNITY")]
|
||||
Community,
|
||||
|
||||
[JsonPropertyName("INTERNAL")]
|
||||
Internal,
|
||||
|
||||
[JsonPropertyName("AGGREGATOR")]
|
||||
Aggregator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trust tier for policy evaluation.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<TrustTier>))]
|
||||
public enum TrustTier
|
||||
{
|
||||
[JsonPropertyName("AUTHORITATIVE")]
|
||||
Authoritative,
|
||||
|
||||
[JsonPropertyName("TRUSTED")]
|
||||
Trusted,
|
||||
|
||||
[JsonPropertyName("UNTRUSTED")]
|
||||
Untrusted,
|
||||
|
||||
[JsonPropertyName("UNKNOWN")]
|
||||
Unknown
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalized VEX statement extracted from source.
|
||||
/// </summary>
|
||||
public sealed record NormalizedStatement(
|
||||
[property: JsonPropertyName("statementId")] string StatementId,
|
||||
[property: JsonPropertyName("vulnerabilityId")] string VulnerabilityId,
|
||||
[property: JsonPropertyName("vulnerabilityAliases")] IReadOnlyList<string>? VulnerabilityAliases,
|
||||
[property: JsonPropertyName("product")] NormalizedProduct Product,
|
||||
[property: JsonPropertyName("status")] VexStatus Status,
|
||||
[property: JsonPropertyName("statusNotes")] string? StatusNotes,
|
||||
[property: JsonPropertyName("justification")] VexJustification? Justification,
|
||||
[property: JsonPropertyName("impactStatement")] string? ImpactStatement,
|
||||
[property: JsonPropertyName("actionStatement")] string? ActionStatement,
|
||||
[property: JsonPropertyName("actionStatementTimestamp")] DateTimeOffset? ActionStatementTimestamp,
|
||||
[property: JsonPropertyName("versions")] VersionRange? Versions,
|
||||
[property: JsonPropertyName("subcomponents")] IReadOnlyList<NormalizedProduct>? Subcomponents,
|
||||
[property: JsonPropertyName("firstSeen")] DateTimeOffset? FirstSeen,
|
||||
[property: JsonPropertyName("lastSeen")] DateTimeOffset? LastSeen);
|
||||
|
||||
/// <summary>
|
||||
/// Normalized VEX status using OpenVEX terminology.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<VexStatus>))]
|
||||
public enum VexStatus
|
||||
{
|
||||
[JsonPropertyName("not_affected")]
|
||||
NotAffected,
|
||||
|
||||
[JsonPropertyName("affected")]
|
||||
Affected,
|
||||
|
||||
[JsonPropertyName("fixed")]
|
||||
Fixed,
|
||||
|
||||
[JsonPropertyName("under_investigation")]
|
||||
UnderInvestigation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalized justification when status is not_affected.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter<VexJustification>))]
|
||||
public enum VexJustification
|
||||
{
|
||||
[JsonPropertyName("component_not_present")]
|
||||
ComponentNotPresent,
|
||||
|
||||
[JsonPropertyName("vulnerable_code_not_present")]
|
||||
VulnerableCodeNotPresent,
|
||||
|
||||
[JsonPropertyName("vulnerable_code_not_in_execute_path")]
|
||||
VulnerableCodeNotInExecutePath,
|
||||
|
||||
[JsonPropertyName("vulnerable_code_cannot_be_controlled_by_adversary")]
|
||||
VulnerableCodeCannotBeControlledByAdversary,
|
||||
|
||||
[JsonPropertyName("inline_mitigations_already_exist")]
|
||||
InlineMitigationsAlreadyExist
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalized product reference.
|
||||
/// </summary>
|
||||
public sealed record NormalizedProduct(
|
||||
[property: JsonPropertyName("key")] string Key,
|
||||
[property: JsonPropertyName("name")] string? Name,
|
||||
[property: JsonPropertyName("version")] string? Version,
|
||||
[property: JsonPropertyName("purl")] string? Purl,
|
||||
[property: JsonPropertyName("cpe")] string? Cpe,
|
||||
[property: JsonPropertyName("hashes")] IReadOnlyDictionary<string, string>? Hashes);
|
||||
|
||||
/// <summary>
|
||||
/// Version constraints for a statement.
|
||||
/// </summary>
|
||||
public sealed record VersionRange(
|
||||
[property: JsonPropertyName("affected")] IReadOnlyList<string>? Affected,
|
||||
[property: JsonPropertyName("fixed")] IReadOnlyList<string>? Fixed,
|
||||
[property: JsonPropertyName("unaffected")] IReadOnlyList<string>? Unaffected);
|
||||
|
||||
/// <summary>
|
||||
/// Metadata about the normalization process.
|
||||
/// </summary>
|
||||
public sealed record NormalizationProvenance(
|
||||
[property: JsonPropertyName("normalizedAt")] DateTimeOffset NormalizedAt,
|
||||
[property: JsonPropertyName("normalizer")] string Normalizer,
|
||||
[property: JsonPropertyName("sourceRevision")] string? SourceRevision,
|
||||
[property: JsonPropertyName("transformationRules")] IReadOnlyList<string>? TransformationRules);
|
||||
Reference in New Issue
Block a user