Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -8,25 +8,23 @@
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" >
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" >
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Attestor.Core\StellaOps.Attestor.Core.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -0,0 +1,325 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// DeltaAttestationService.cs
|
||||
// Sprint: SPRINT_20251228_007_BE_sbom_lineage_graph_ii (LIN-BE-025)
|
||||
// Task: Implement IDeltaVerdictAttestationService
|
||||
// Description: Creates DSSE-signed in-toto statements for lineage delta changes.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using StellaOps.Attestor.Core.Signing;
|
||||
using StellaOps.Attestor.Core.Submission;
|
||||
using StellaOps.Signer.Core;
|
||||
using StellaOps.Signer.Core.Predicates;
|
||||
|
||||
namespace StellaOps.Attestor.Core.Delta;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="IDeltaAttestationService"/> that creates DSSE-signed
|
||||
/// in-toto statements for lineage delta changes.
|
||||
/// </summary>
|
||||
public sealed class DeltaAttestationService : IDeltaAttestationService
|
||||
{
|
||||
private static readonly ActivitySource ActivitySource = new("StellaOps.Attestor.Delta");
|
||||
|
||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
private readonly IAttestationSigningService _signingService;
|
||||
private readonly ILogger<DeltaAttestationService> _logger;
|
||||
private readonly DeltaAttestationOptions _options;
|
||||
|
||||
public DeltaAttestationService(
|
||||
IAttestationSigningService signingService,
|
||||
IOptions<DeltaAttestationOptions> options,
|
||||
ILogger<DeltaAttestationService> logger)
|
||||
{
|
||||
_signingService = signingService ?? throw new ArgumentNullException(nameof(signingService));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options?.Value ?? new DeltaAttestationOptions();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeltaAttestationResult> CreateVexDeltaAttestationAsync(
|
||||
VexDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity("CreateVexDeltaAttestation");
|
||||
activity?.SetTag("from_digest", request.FromDigest);
|
||||
activity?.SetTag("to_digest", request.ToDigest);
|
||||
activity?.SetTag("tenant_id", request.TenantId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Creating VEX delta attestation from {FromDigest} to {ToDigest}",
|
||||
request.FromDigest, request.ToDigest);
|
||||
|
||||
return await CreateDeltaAttestationAsync(
|
||||
request,
|
||||
request.Delta,
|
||||
PredicateTypes.StellaOpsVexDelta,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeltaAttestationResult> CreateSbomDeltaAttestationAsync(
|
||||
SbomDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity("CreateSbomDeltaAttestation");
|
||||
activity?.SetTag("from_digest", request.FromDigest);
|
||||
activity?.SetTag("to_digest", request.ToDigest);
|
||||
activity?.SetTag("tenant_id", request.TenantId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Creating SBOM delta attestation from {FromDigest} to {ToDigest}",
|
||||
request.FromDigest, request.ToDigest);
|
||||
|
||||
return await CreateDeltaAttestationAsync(
|
||||
request,
|
||||
request.Delta,
|
||||
PredicateTypes.StellaOpsSbomDelta,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeltaAttestationResult> CreateVerdictDeltaAttestationAsync(
|
||||
VerdictDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity("CreateVerdictDeltaAttestation");
|
||||
activity?.SetTag("from_digest", request.FromDigest);
|
||||
activity?.SetTag("to_digest", request.ToDigest);
|
||||
activity?.SetTag("tenant_id", request.TenantId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Creating verdict delta attestation from {FromDigest} to {ToDigest}",
|
||||
request.FromDigest, request.ToDigest);
|
||||
|
||||
return await CreateDeltaAttestationAsync(
|
||||
request,
|
||||
request.Delta,
|
||||
PredicateTypes.StellaOpsVerdictDelta,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<DeltaAttestationResult> CreateReachabilityDeltaAttestationAsync(
|
||||
ReachabilityDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity("CreateReachabilityDeltaAttestation");
|
||||
activity?.SetTag("from_digest", request.FromDigest);
|
||||
activity?.SetTag("to_digest", request.ToDigest);
|
||||
activity?.SetTag("tenant_id", request.TenantId);
|
||||
|
||||
_logger.LogInformation(
|
||||
"Creating reachability delta attestation from {FromDigest} to {ToDigest}",
|
||||
request.FromDigest, request.ToDigest);
|
||||
|
||||
return await CreateDeltaAttestationAsync(
|
||||
request,
|
||||
request.Delta,
|
||||
PredicateTypes.StellaOpsReachabilityDelta,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<DeltaAttestationResult> CreateDeltaAttestationAsync<TPredicate>(
|
||||
DeltaAttestationRequestBase request,
|
||||
TPredicate predicate,
|
||||
string predicateType,
|
||||
CancellationToken cancellationToken)
|
||||
where TPredicate : class
|
||||
{
|
||||
try
|
||||
{
|
||||
// Build in-toto statement
|
||||
var statement = BuildInTotoStatement(request, predicate, predicateType);
|
||||
var statementJson = JsonSerializer.Serialize(statement, JsonOptions);
|
||||
var payloadBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(statementJson));
|
||||
|
||||
// Compute envelope digest
|
||||
var envelopeDigest = ComputeSha256(statementJson);
|
||||
|
||||
// Build signing request
|
||||
var signRequest = new AttestationSignRequest
|
||||
{
|
||||
KeyId = request.KeyId ?? _options.DefaultKeyId ?? string.Empty,
|
||||
PayloadType = "application/vnd.in-toto+json",
|
||||
PayloadBase64 = payloadBase64,
|
||||
Mode = request.UseKeyless ? "keyless" : "local",
|
||||
LogPreference = request.UseTransparencyLog ? "primary" : "none",
|
||||
Archive = true,
|
||||
Artifact = new AttestorSubmissionRequest.ArtifactInfo
|
||||
{
|
||||
Sha256 = envelopeDigest,
|
||||
Kind = "delta-attestation",
|
||||
SubjectUri = $"urn:stellaops:lineage:{request.FromDigest}..{request.ToDigest}"
|
||||
}
|
||||
};
|
||||
|
||||
// Create submission context
|
||||
var context = new SubmissionContext
|
||||
{
|
||||
CallerSubject = $"tenant:{request.TenantId}",
|
||||
CallerAudience = "stellaops-attestor",
|
||||
CallerClientId = "delta-attestation-service",
|
||||
CallerTenant = request.TenantId
|
||||
};
|
||||
|
||||
// Sign the attestation
|
||||
var signResult = await _signingService.SignAsync(signRequest, context, cancellationToken);
|
||||
|
||||
// Extract transparency log index if present
|
||||
long? logIndex = null;
|
||||
if (signResult.Bundle?.Dsse?.Signatures?.Count > 0)
|
||||
{
|
||||
// Log index would typically be returned in a separate field after Rekor submission
|
||||
_logger.LogDebug("Attestation signed with mode {Mode}", signResult.Mode);
|
||||
}
|
||||
|
||||
// Build envelope base64 from signed result
|
||||
var envelopeBase64 = signResult.Bundle?.Dsse != null
|
||||
? Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(signResult.Bundle.Dsse, JsonOptions)))
|
||||
: null;
|
||||
|
||||
_logger.LogInformation(
|
||||
"Created delta attestation with digest {Digest} for predicate type {PredicateType}",
|
||||
envelopeDigest, predicateType);
|
||||
|
||||
return new DeltaAttestationResult
|
||||
{
|
||||
Success = true,
|
||||
AttestationDigest = envelopeDigest,
|
||||
EnvelopeBase64 = envelopeBase64,
|
||||
TransparencyLogIndex = logIndex,
|
||||
PredicateType = predicateType,
|
||||
CreatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create delta attestation from {FromDigest} to {ToDigest}",
|
||||
request.FromDigest, request.ToDigest);
|
||||
|
||||
return new DeltaAttestationResult
|
||||
{
|
||||
Success = false,
|
||||
Error = ex.Message,
|
||||
PredicateType = predicateType,
|
||||
CreatedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private InTotoStatement<TPredicate> BuildInTotoStatement<TPredicate>(
|
||||
DeltaAttestationRequestBase request,
|
||||
TPredicate predicate,
|
||||
string predicateType)
|
||||
where TPredicate : class
|
||||
{
|
||||
var subjects = new List<InTotoSubject>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = $"lineage:{request.FromDigest}..{request.ToDigest}",
|
||||
Digest = new Dictionary<string, string>
|
||||
{
|
||||
["sha256_from"] = ExtractHash(request.FromDigest),
|
||||
["sha256_to"] = ExtractHash(request.ToDigest)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add annotations if provided
|
||||
if (request.Annotations?.Count > 0)
|
||||
{
|
||||
foreach (var (key, value) in request.Annotations)
|
||||
{
|
||||
subjects[0].Digest[$"annotation:{key}"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return new InTotoStatement<TPredicate>
|
||||
{
|
||||
Type = "https://in-toto.io/Statement/v1",
|
||||
Subject = subjects,
|
||||
PredicateType = predicateType,
|
||||
Predicate = predicate
|
||||
};
|
||||
}
|
||||
|
||||
private static string ExtractHash(string digest)
|
||||
{
|
||||
// Remove algorithm prefix if present (e.g., "sha256:abc123" -> "abc123")
|
||||
var colonIndex = digest.IndexOf(':');
|
||||
return colonIndex >= 0 ? digest[(colonIndex + 1)..] : digest;
|
||||
}
|
||||
|
||||
private static string ComputeSha256(string content)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
var hash = SHA256.HashData(bytes);
|
||||
return Convert.ToHexStringLower(hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// In-toto statement structure.
|
||||
/// </summary>
|
||||
public sealed class InTotoStatement<TPredicate>
|
||||
where TPredicate : class
|
||||
{
|
||||
[JsonPropertyName("_type")]
|
||||
public string Type { get; set; } = "https://in-toto.io/Statement/v1";
|
||||
|
||||
[JsonPropertyName("subject")]
|
||||
public IList<InTotoSubject> Subject { get; set; } = new List<InTotoSubject>();
|
||||
|
||||
[JsonPropertyName("predicateType")]
|
||||
public string PredicateType { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("predicate")]
|
||||
public TPredicate? Predicate { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subject entry for in-toto statements.
|
||||
/// </summary>
|
||||
public sealed class InTotoSubject
|
||||
{
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("digest")]
|
||||
public IDictionary<string, string> Digest { get; set; } = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration options for delta attestation service.
|
||||
/// </summary>
|
||||
public sealed class DeltaAttestationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Default key ID to use for signing when not specified in request.
|
||||
/// </summary>
|
||||
public string? DefaultKeyId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use keyless signing by default.
|
||||
/// </summary>
|
||||
public bool DefaultUseKeyless { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to publish to transparency log by default.
|
||||
/// </summary>
|
||||
public bool DefaultUseTransparencyLog { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// IDeltaAttestationService.cs
|
||||
// Sprint: SPRINT_20251228_007_BE_sbom_lineage_graph_ii (LIN-BE-025)
|
||||
// Task: Implement IDeltaVerdictAttestationService
|
||||
// Description: Service interface for creating delta attestations for lineage changes.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using StellaOps.Attestor.Core.Signing;
|
||||
using StellaOps.Signer.Core.Predicates;
|
||||
|
||||
namespace StellaOps.Attestor.Core.Delta;
|
||||
|
||||
/// <summary>
|
||||
/// Service for creating DSSE-signed attestations for delta changes between lineage versions.
|
||||
/// Generates in-toto statements with delta predicates and signs them using the configured signer.
|
||||
/// </summary>
|
||||
public interface IDeltaAttestationService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a VEX delta attestation.
|
||||
/// </summary>
|
||||
/// <param name="request">The delta attestation request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The signed attestation result.</returns>
|
||||
Task<DeltaAttestationResult> CreateVexDeltaAttestationAsync(
|
||||
VexDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an SBOM delta attestation.
|
||||
/// </summary>
|
||||
/// <param name="request">The delta attestation request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The signed attestation result.</returns>
|
||||
Task<DeltaAttestationResult> CreateSbomDeltaAttestationAsync(
|
||||
SbomDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a verdict delta attestation.
|
||||
/// </summary>
|
||||
/// <param name="request">The delta attestation request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The signed attestation result.</returns>
|
||||
Task<DeltaAttestationResult> CreateVerdictDeltaAttestationAsync(
|
||||
VerdictDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reachability delta attestation.
|
||||
/// </summary>
|
||||
/// <param name="request">The delta attestation request.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The signed attestation result.</returns>
|
||||
Task<DeltaAttestationResult> CreateReachabilityDeltaAttestationAsync(
|
||||
ReachabilityDeltaAttestationRequest request,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base request for delta attestations.
|
||||
/// </summary>
|
||||
public abstract record DeltaAttestationRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Digest of the source artifact.
|
||||
/// </summary>
|
||||
public required string FromDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest of the target artifact.
|
||||
/// </summary>
|
||||
public required string ToDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Tenant identifier.
|
||||
/// </summary>
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Key ID for signing. If null, uses default.
|
||||
/// </summary>
|
||||
public string? KeyId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to use keyless signing (Sigstore Fulcio).
|
||||
/// </summary>
|
||||
public bool UseKeyless { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to record in transparency log (Rekor).
|
||||
/// </summary>
|
||||
public bool UseTransparencyLog { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Additional annotations to include.
|
||||
/// </summary>
|
||||
public IDictionary<string, string>? Annotations { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to create a VEX delta attestation.
|
||||
/// </summary>
|
||||
public sealed record VexDeltaAttestationRequest : DeltaAttestationRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The VEX delta predicate to attest.
|
||||
/// </summary>
|
||||
public required VexDeltaPredicate Delta { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to create an SBOM delta attestation.
|
||||
/// </summary>
|
||||
public sealed record SbomDeltaAttestationRequest : DeltaAttestationRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The SBOM delta predicate to attest.
|
||||
/// </summary>
|
||||
public required SbomDeltaPredicate Delta { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to create a verdict delta attestation.
|
||||
/// </summary>
|
||||
public sealed record VerdictDeltaAttestationRequest : DeltaAttestationRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The verdict delta predicate to attest.
|
||||
/// </summary>
|
||||
public required VerdictDeltaPredicate Delta { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request to create a reachability delta attestation.
|
||||
/// </summary>
|
||||
public sealed record ReachabilityDeltaAttestationRequest : DeltaAttestationRequestBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The reachability delta predicate to attest.
|
||||
/// </summary>
|
||||
public required ReachabilityDeltaPredicate Delta { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of creating a delta attestation.
|
||||
/// </summary>
|
||||
public sealed record DeltaAttestationResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the attestation was successfully created and signed.
|
||||
/// </summary>
|
||||
public required bool Success { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Digest of the signed attestation envelope.
|
||||
/// </summary>
|
||||
public string? AttestationDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Base64-encoded DSSE envelope.
|
||||
/// </summary>
|
||||
public string? EnvelopeBase64 { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Transparency log entry index (if published to Rekor).
|
||||
/// </summary>
|
||||
public long? TransparencyLogIndex { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Predicate type used.
|
||||
/// </summary>
|
||||
public string? PredicateType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Timestamp of attestation creation.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message if failed.
|
||||
/// </summary>
|
||||
public string? Error { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "stella.ops/sbom-delta@v1",
|
||||
"title": "SBOM Delta Predicate",
|
||||
"description": "Schema for SBOM delta attestations between artifact versions",
|
||||
"type": "object",
|
||||
"required": ["fromDigest", "toDigest", "fromSbomDigest", "toSbomDigest", "tenantId", "summary", "comparedAt"],
|
||||
"properties": {
|
||||
"fromDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the source (baseline) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"toDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the target (current) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"fromSbomDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the source SBOM"
|
||||
},
|
||||
"toSbomDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the target SBOM"
|
||||
},
|
||||
"tenantId": {
|
||||
"type": "string",
|
||||
"description": "Tenant identifier"
|
||||
},
|
||||
"added": {
|
||||
"type": "array",
|
||||
"description": "Components added in the target SBOM",
|
||||
"items": { "$ref": "#/$defs/component" }
|
||||
},
|
||||
"removed": {
|
||||
"type": "array",
|
||||
"description": "Components removed from the baseline SBOM",
|
||||
"items": { "$ref": "#/$defs/component" }
|
||||
},
|
||||
"versionChanged": {
|
||||
"type": "array",
|
||||
"description": "Components that changed version between SBOMs",
|
||||
"items": { "$ref": "#/$defs/versionChange" }
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/summary"
|
||||
},
|
||||
"comparedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When the comparison was performed"
|
||||
},
|
||||
"algorithmVersion": {
|
||||
"type": "string",
|
||||
"description": "Version of the delta computation algorithm"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"component": {
|
||||
"type": "object",
|
||||
"required": ["purl", "name", "version"],
|
||||
"properties": {
|
||||
"purl": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"version": { "type": "string" },
|
||||
"type": { "type": "string" },
|
||||
"ecosystem": { "type": "string" },
|
||||
"knownVulnerabilities": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"licenses": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"versionChange": {
|
||||
"type": "object",
|
||||
"required": ["purl", "name", "previousVersion", "currentVersion", "changeType"],
|
||||
"properties": {
|
||||
"purl": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"previousVersion": { "type": "string" },
|
||||
"currentVersion": { "type": "string" },
|
||||
"changeType": {
|
||||
"type": "string",
|
||||
"enum": ["major", "minor", "patch", "unknown"]
|
||||
},
|
||||
"vulnerabilitiesFixed": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"vulnerabilitiesIntroduced": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"required": ["addedCount", "removedCount", "versionChangedCount", "unchangedCount", "fromTotalCount", "toTotalCount", "vulnerabilitiesFixedCount", "vulnerabilitiesIntroducedCount"],
|
||||
"properties": {
|
||||
"addedCount": { "type": "integer", "minimum": 0 },
|
||||
"removedCount": { "type": "integer", "minimum": 0 },
|
||||
"versionChangedCount": { "type": "integer", "minimum": 0 },
|
||||
"unchangedCount": { "type": "integer", "minimum": 0 },
|
||||
"fromTotalCount": { "type": "integer", "minimum": 0 },
|
||||
"toTotalCount": { "type": "integer", "minimum": 0 },
|
||||
"vulnerabilitiesFixedCount": { "type": "integer", "minimum": 0 },
|
||||
"vulnerabilitiesIntroducedCount": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "stella.ops/verdict-delta@v1",
|
||||
"title": "Verdict Delta Predicate",
|
||||
"description": "Schema for policy verdict delta attestations between artifact versions",
|
||||
"type": "object",
|
||||
"required": ["fromDigest", "toDigest", "tenantId", "fromPolicyVersion", "toPolicyVersion", "fromVerdict", "toVerdict", "summary", "comparedAt"],
|
||||
"properties": {
|
||||
"fromDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the source (baseline) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"toDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the target (current) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"tenantId": {
|
||||
"type": "string",
|
||||
"description": "Tenant identifier"
|
||||
},
|
||||
"fromPolicyVersion": {
|
||||
"type": "string",
|
||||
"description": "Policy pack version used for baseline evaluation"
|
||||
},
|
||||
"toPolicyVersion": {
|
||||
"type": "string",
|
||||
"description": "Policy pack version used for target evaluation"
|
||||
},
|
||||
"fromVerdict": {
|
||||
"$ref": "#/$defs/verdictSummary"
|
||||
},
|
||||
"toVerdict": {
|
||||
"$ref": "#/$defs/verdictSummary"
|
||||
},
|
||||
"findingChanges": {
|
||||
"type": "array",
|
||||
"description": "Individual finding verdicts that changed",
|
||||
"items": { "$ref": "#/$defs/findingChange" }
|
||||
},
|
||||
"ruleChanges": {
|
||||
"type": "array",
|
||||
"description": "Rule evaluations that changed",
|
||||
"items": { "$ref": "#/$defs/ruleChange" }
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/deltaSummary"
|
||||
},
|
||||
"comparedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When the comparison was performed"
|
||||
},
|
||||
"algorithmVersion": {
|
||||
"type": "string",
|
||||
"description": "Version of the delta computation algorithm"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"verdictSummary": {
|
||||
"type": "object",
|
||||
"required": ["outcome", "confidence", "riskScore", "passingRules", "failingRules", "warningRules"],
|
||||
"properties": {
|
||||
"outcome": {
|
||||
"type": "string",
|
||||
"enum": ["pass", "fail", "warn"]
|
||||
},
|
||||
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
|
||||
"riskScore": { "type": "number" },
|
||||
"verdictDigest": { "type": "string" },
|
||||
"passingRules": { "type": "integer", "minimum": 0 },
|
||||
"failingRules": { "type": "integer", "minimum": 0 },
|
||||
"warningRules": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
},
|
||||
"findingChange": {
|
||||
"type": "object",
|
||||
"required": ["vulnerabilityId", "purl", "previousVerdict", "currentVerdict", "changeReason", "riskDirection"],
|
||||
"properties": {
|
||||
"vulnerabilityId": { "type": "string" },
|
||||
"purl": { "type": "string" },
|
||||
"previousVerdict": { "type": "string" },
|
||||
"currentVerdict": { "type": "string" },
|
||||
"changeReason": { "type": "string" },
|
||||
"riskDirection": {
|
||||
"type": "string",
|
||||
"enum": ["increased", "decreased", "neutral"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ruleChange": {
|
||||
"type": "object",
|
||||
"required": ["ruleId", "ruleName", "previousResult", "currentResult"],
|
||||
"properties": {
|
||||
"ruleId": { "type": "string" },
|
||||
"ruleName": { "type": "string" },
|
||||
"previousResult": {
|
||||
"type": "string",
|
||||
"enum": ["pass", "fail", "warn", "skip"]
|
||||
},
|
||||
"currentResult": {
|
||||
"type": "string",
|
||||
"enum": ["pass", "fail", "warn", "skip"]
|
||||
},
|
||||
"previousMessage": { "type": "string" },
|
||||
"currentMessage": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"deltaSummary": {
|
||||
"type": "object",
|
||||
"required": ["verdictChanged", "riskDirection", "riskScoreDelta", "confidenceDelta", "findingsImproved", "findingsWorsened", "findingsNew", "findingsResolved", "rulesChanged"],
|
||||
"properties": {
|
||||
"verdictChanged": { "type": "boolean" },
|
||||
"riskDirection": {
|
||||
"type": "string",
|
||||
"enum": ["increased", "decreased", "neutral"]
|
||||
},
|
||||
"riskScoreDelta": { "type": "number" },
|
||||
"confidenceDelta": { "type": "number" },
|
||||
"findingsImproved": { "type": "integer", "minimum": 0 },
|
||||
"findingsWorsened": { "type": "integer", "minimum": 0 },
|
||||
"findingsNew": { "type": "integer", "minimum": 0 },
|
||||
"findingsResolved": { "type": "integer", "minimum": 0 },
|
||||
"rulesChanged": { "type": "integer", "minimum": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "stella.ops/vex-delta@v1",
|
||||
"title": "VEX Delta Predicate",
|
||||
"description": "Schema for VEX delta attestations between artifact versions",
|
||||
"type": "object",
|
||||
"required": ["fromDigest", "toDigest", "tenantId", "summary", "comparedAt"],
|
||||
"properties": {
|
||||
"fromDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the source (baseline) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"toDigest": {
|
||||
"type": "string",
|
||||
"description": "Digest of the target (current) artifact",
|
||||
"pattern": "^(sha256|sha384|sha512|blake3):[a-fA-F0-9]+$"
|
||||
},
|
||||
"tenantId": {
|
||||
"type": "string",
|
||||
"description": "Tenant identifier"
|
||||
},
|
||||
"added": {
|
||||
"type": "array",
|
||||
"description": "VEX statements added in the target artifact",
|
||||
"items": { "$ref": "#/$defs/vexStatement" }
|
||||
},
|
||||
"removed": {
|
||||
"type": "array",
|
||||
"description": "VEX statements removed from the baseline artifact",
|
||||
"items": { "$ref": "#/$defs/vexStatement" }
|
||||
},
|
||||
"changed": {
|
||||
"type": "array",
|
||||
"description": "VEX statements that changed status between versions",
|
||||
"items": { "$ref": "#/$defs/vexChange" }
|
||||
},
|
||||
"summary": {
|
||||
"$ref": "#/$defs/summary"
|
||||
},
|
||||
"comparedAt": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "When the comparison was performed"
|
||||
},
|
||||
"algorithmVersion": {
|
||||
"type": "string",
|
||||
"description": "Version of the delta computation algorithm"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"vexStatement": {
|
||||
"type": "object",
|
||||
"required": ["vulnerabilityId", "productId", "status"],
|
||||
"properties": {
|
||||
"vulnerabilityId": { "type": "string" },
|
||||
"productId": { "type": "string" },
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["not_affected", "affected", "fixed", "under_investigation"]
|
||||
},
|
||||
"justification": { "type": "string" },
|
||||
"issuer": { "type": "string" },
|
||||
"timestamp": { "type": "string", "format": "date-time" }
|
||||
}
|
||||
},
|
||||
"vexChange": {
|
||||
"type": "object",
|
||||
"required": ["vulnerabilityId", "productId", "previousStatus", "currentStatus", "riskDirection"],
|
||||
"properties": {
|
||||
"vulnerabilityId": { "type": "string" },
|
||||
"productId": { "type": "string" },
|
||||
"previousStatus": { "type": "string" },
|
||||
"currentStatus": { "type": "string" },
|
||||
"previousJustification": { "type": "string" },
|
||||
"currentJustification": { "type": "string" },
|
||||
"riskDirection": {
|
||||
"type": "string",
|
||||
"enum": ["increased", "decreased", "neutral"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"summary": {
|
||||
"type": "object",
|
||||
"required": ["addedCount", "removedCount", "changedCount", "unchangedCount", "netRiskDirection"],
|
||||
"properties": {
|
||||
"addedCount": { "type": "integer", "minimum": 0 },
|
||||
"removedCount": { "type": "integer", "minimum": 0 },
|
||||
"changedCount": { "type": "integer", "minimum": 0 },
|
||||
"unchangedCount": { "type": "integer", "minimum": 0 },
|
||||
"netRiskDirection": {
|
||||
"type": "string",
|
||||
"enum": ["increased", "decreased", "neutral"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,16 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonSchema.Net" Version="7.3.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="JsonSchema.Net" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Schemas\*.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Kms\StellaOps.Cryptography.Kms.csproj" />
|
||||
<ProjectReference Include="..\..\..\Signer\StellaOps.Signer\StellaOps.Signer.Core\StellaOps.Signer.Core.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -114,13 +114,13 @@ public sealed class PredicateSchemaValidator : IPredicateSchemaValidator
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
if (results.HasErrors)
|
||||
if (!results.IsValid && results.Details is not null)
|
||||
{
|
||||
foreach (var detail in results.Details)
|
||||
foreach (var detail in results.Details!)
|
||||
{
|
||||
if (detail.HasErrors && detail.Errors is not null)
|
||||
if (!detail.IsValid && detail.Errors is not null)
|
||||
{
|
||||
foreach (var error in detail.Errors)
|
||||
foreach (var error in detail.Errors!)
|
||||
{
|
||||
var errorMsg = error.Value ?? "Unknown error";
|
||||
var location = detail.InstanceLocation.ToString();
|
||||
@@ -148,7 +148,11 @@ public sealed class PredicateSchemaValidator : IPredicateSchemaValidator
|
||||
("reachability@v1", "reachability.v1.schema.json"),
|
||||
("boundary@v1", "boundary.v1.schema.json"),
|
||||
("policy-decision@v1", "policy-decision.v1.schema.json"),
|
||||
("human-approval@v1", "human-approval.v1.schema.json")
|
||||
("human-approval@v1", "human-approval.v1.schema.json"),
|
||||
// Delta predicate schemas (Sprint 20251228_007 LIN-BE-024)
|
||||
("vex-delta@v1", "vex-delta.v1.schema.json"),
|
||||
("sbom-delta@v1", "sbom-delta.v1.schema.json"),
|
||||
("verdict-delta@v1", "verdict-delta.v1.schema.json")
|
||||
};
|
||||
|
||||
foreach (var (key, fileName) in schemaFiles)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Archived Pre-1.0 Migrations
|
||||
|
||||
This directory contains the original migrations that were compacted into `001_initial_schema.sql`
|
||||
in the `StellaOps.Attestor.Persistence` project for the 1.0.0 release.
|
||||
|
||||
## Original Files
|
||||
- `20251216_001_create_rekor_submission_queue.sql` - Rekor submission queue for durable retry
|
||||
|
||||
## Why Archived
|
||||
Pre-1.0, the schema evolved incrementally. For 1.0.0, migrations were compacted into a single
|
||||
initial schema (in `StellaOps.Attestor.Persistence`) to:
|
||||
- Simplify new deployments
|
||||
- Reduce startup time
|
||||
- Provide cleaner upgrade path
|
||||
|
||||
## For Existing Deployments
|
||||
If upgrading from pre-1.0, run the reset script directly with psql:
|
||||
```bash
|
||||
psql -h <host> -U <user> -d <db> -f devops/scripts/migrations-reset-pre-1.0.sql
|
||||
```
|
||||
This updates `schema_migrations` to recognize the compacted schema.
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable CS0618 // FallbackCredentialsFactory is obsolete - transitioning to DefaultAWSCredentialsIdentityResolver
|
||||
|
||||
using System;
|
||||
using Amazon.Runtime;
|
||||
using Amazon.S3;
|
||||
|
||||
@@ -13,17 +13,17 @@
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.BouncyCastle\StellaOps.Cryptography.Plugin.BouncyCastle.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Kms\StellaOps.Cryptography.Kms.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
|
||||
<ProjectReference Include="..\..\..\Router/__Libraries/StellaOps.Messaging\StellaOps.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" />
|
||||
<PackageReference Include="StackExchange.Redis" />
|
||||
<PackageReference Include="AWSSDK.S3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Attestor.Core\StellaOps.Attestor.Core.csproj" />
|
||||
<ProjectReference Include="..\..\StellaOps.Attestor.Verify\StellaOps.Attestor.Verify.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.BouncyCastle\StellaOps.Cryptography.Plugin.BouncyCastle.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Kms\StellaOps.Cryptography.Kms.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Messaging\StellaOps.Messaging.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="4.0.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -33,6 +33,7 @@ using StellaOps.Attestor.Core.Transparency;
|
||||
using StellaOps.Attestor.Core.Bulk;
|
||||
using StellaOps.Attestor.WebService;
|
||||
using StellaOps.Attestor.Tests.Support;
|
||||
using StellaOps.TestKit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.Tests;
|
||||
@@ -66,7 +67,6 @@ public sealed class AttestationBundleEndpointsTests
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var repository = scope.ServiceProvider.GetRequiredService<IAttestorEntryRepository>();
|
||||
using StellaOps.TestKit;
|
||||
var archiveStore = scope.ServiceProvider.GetRequiredService<IAttestorArchiveStore>();
|
||||
|
||||
var entry = new AttestorEntry
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
@@ -256,7 +256,6 @@ public sealed class AttestorSigningServiceTests : IDisposable
|
||||
|
||||
using var metrics = new AttestorMetrics();
|
||||
using var registry = new AttestorSigningKeyRegistry(options, TimeProvider.System, NullLogger<AttestorSigningKeyRegistry>.Instance);
|
||||
using StellaOps.TestKit;
|
||||
var auditSink = new InMemoryAttestorAuditSink();
|
||||
var service = new AttestorSigningService(
|
||||
registry,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
@@ -277,7 +277,6 @@ public sealed class AttestorSubmissionServiceTests
|
||||
var logger = new NullLogger<AttestorSubmissionService>();
|
||||
using var metrics = new AttestorMetrics();
|
||||
|
||||
using StellaOps.TestKit;
|
||||
var service = new AttestorSubmissionService(
|
||||
validator,
|
||||
repository,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
@@ -700,7 +700,6 @@ public sealed class AttestorVerificationServiceTests
|
||||
private static byte[] ComputeMerkleNode(byte[] left, byte[] right)
|
||||
{
|
||||
using var sha = SHA256.Create();
|
||||
using StellaOps.TestKit;
|
||||
var buffer = new byte[1 + left.Length + right.Length];
|
||||
buffer[0] = 0x01;
|
||||
Buffer.BlockCopy(left, 0, buffer, 1, left.Length);
|
||||
|
||||
@@ -12,7 +12,6 @@ using System.Text;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Attestor.WebService.Tests.Auth;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
@@ -24,7 +24,6 @@ public sealed class BulkVerificationWorkerTests
|
||||
var jobStore = new InMemoryBulkVerificationJobStore();
|
||||
var verificationService = new StubVerificationService();
|
||||
using var metrics = new AttestorMetrics();
|
||||
using StellaOps.TestKit;
|
||||
var options = Options.Create(new AttestorOptions
|
||||
{
|
||||
BulkVerification = new AttestorOptions.BulkVerificationOptions
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
@@ -86,7 +86,6 @@ public sealed class CachedAttestorVerificationServiceTests
|
||||
var options = Options.Create(new AttestorOptions());
|
||||
using var memoryCache = new MemoryCache(new MemoryCacheOptions());
|
||||
using var metrics = new AttestorMetrics();
|
||||
using StellaOps.TestKit;
|
||||
var cache = new InMemoryAttestorVerificationCache(memoryCache, options, new NullLogger<InMemoryAttestorVerificationCache>());
|
||||
var inner = new StubVerificationService();
|
||||
var service = new CachedAttestorVerificationService(
|
||||
|
||||
@@ -13,7 +13,6 @@ using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Attestor.WebService.Tests.Contract;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
@@ -136,7 +136,6 @@ public sealed class HttpTransparencyWitnessClientTests
|
||||
using var metrics = new AttestorMetrics();
|
||||
using var activitySource = new AttestorActivitySource();
|
||||
|
||||
using StellaOps.TestKit;
|
||||
var options = Options.Create(new AttestorOptions
|
||||
{
|
||||
TransparencyWitness = new AttestorOptions.TransparencyWitnessOptions
|
||||
|
||||
@@ -150,7 +150,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
|
||||
// Assert
|
||||
var count = await GetQueueCountAsync();
|
||||
count.Should().BeGreaterOrEqualTo(5);
|
||||
count.Should().BeGreaterThanOrEqualTo(5);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -168,7 +168,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
var items = await _queue.DequeueAsync(10);
|
||||
|
||||
// Assert
|
||||
items.Should().HaveCountGreaterOrEqualTo(2);
|
||||
items.Should().HaveCountGreaterThanOrEqualTo(2);
|
||||
items.Should().OnlyContain(i => i.Status == RekorSubmissionStatus.Submitting);
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
var items = await _queue.DequeueAsync(3);
|
||||
|
||||
// Assert
|
||||
items.Should().HaveCountLessOrEqualTo(3);
|
||||
items.Should().HaveCountLessThanOrEqualTo(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -213,7 +213,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
|
||||
// Assert - Item should only appear in one result
|
||||
var allItems = results.SelectMany(r => r).Where(i => i.BundleSha256 == uniqueBundle).ToList();
|
||||
allItems.Should().HaveCountLessOrEqualTo(1);
|
||||
allItems.Should().HaveCountLessThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -295,7 +295,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
var newDepth = await _queue.GetQueueDepthAsync();
|
||||
|
||||
// Assert
|
||||
newDepth.Should().BeGreaterOrEqualTo(baseDepth + 2);
|
||||
newDepth.Should().BeGreaterThanOrEqualTo(baseDepth + 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -317,7 +317,7 @@ public class PostgresRekorSubmissionQueueIntegrationTests : IAsyncLifetime
|
||||
var dlqCount = await queue.GetDeadLetterCountAsync();
|
||||
|
||||
// Assert
|
||||
dlqCount.Should().BeGreaterOrEqualTo(1);
|
||||
dlqCount.Should().BeGreaterThanOrEqualTo(1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -13,7 +13,6 @@ using System.Text.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Attestor.WebService.Tests.Negative;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ using System.Net.Http.Json;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace StellaOps.Attestor.WebService.Tests.Observability;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using StellaOps.Attestor.Core.Verification;
|
||||
using Xunit;
|
||||
@@ -309,7 +309,6 @@ public sealed class RekorInclusionVerificationIntegrationTests
|
||||
private static byte[] ComputeInteriorHash(byte[] left, byte[] right)
|
||||
{
|
||||
using var sha256 = System.Security.Cryptography.SHA256.Create();
|
||||
using StellaOps.TestKit;
|
||||
var combined = new byte[1 + left.Length + right.Length];
|
||||
combined[0] = 0x01; // Interior node prefix
|
||||
left.CopyTo(combined, 1);
|
||||
|
||||
@@ -5,20 +5,26 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<UseConcelierTestInfra>false</UseConcelierTestInfra>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
|
||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||
<PackageReference Include="Testcontainers" Version="4.3.0" />
|
||||
<PackageReference Include="Testcontainers.PostgreSql" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<Using Include="Xunit.Abstractions" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BouncyCastle.Cryptography" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="NSubstitute" />
|
||||
<PackageReference Include="Testcontainers" />
|
||||
<PackageReference Include="Testcontainers.PostgreSql" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" >
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" >
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Attestor.WebService\StellaOps.Attestor.WebService.csproj" />
|
||||
<ProjectReference Include="..\StellaOps.Attestor.Infrastructure\StellaOps.Attestor.Infrastructure.csproj" />
|
||||
@@ -29,4 +35,4 @@
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -97,7 +97,7 @@ public sealed class ProofChainController : ControllerBase
|
||||
|
||||
var chain = await _queryService.GetProofChainAsync(subjectDigest, depth, cancellationToken);
|
||||
|
||||
if (chain is null || chain.Nodes.Count == 0)
|
||||
if (chain is null || chain.Nodes.Length == 0)
|
||||
{
|
||||
return NotFound(new { error = $"No proof chain found for subject {subjectDigest}" });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"StellaOps.Attestor.WebService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:62507;http://localhost:62508"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,11 @@ public sealed class PredicateTypeRouter : IPredicateTypeRouter
|
||||
"https://stella-ops.org/predicates/reachability-subgraph/v1",
|
||||
"https://stella-ops.org/predicates/delta-verdict/v1",
|
||||
"https://stella-ops.org/predicates/policy-decision/v1",
|
||||
"https://stella-ops.org/predicates/unknowns-budget/v1"
|
||||
"https://stella-ops.org/predicates/unknowns-budget/v1",
|
||||
// Delta predicate types for lineage comparison (Sprint 20251228_007)
|
||||
"stella.ops/vex-delta@v1",
|
||||
"stella.ops/sbom-delta@v1",
|
||||
"stella.ops/verdict-delta@v1"
|
||||
};
|
||||
|
||||
public PredicateTypeRouter(
|
||||
@@ -169,7 +173,7 @@ public sealed class PredicateTypeRouter : IPredicateTypeRouter
|
||||
Metadata = new PredicateMetadata
|
||||
{
|
||||
Format = parseResult.Metadata.Format,
|
||||
Version = parseResult.Metadata.Version,
|
||||
Version = parseResult.Metadata.Version ?? "unknown",
|
||||
Properties = parseResult.Metadata.Properties.ToImmutableDictionary()
|
||||
},
|
||||
Sbom = sbom,
|
||||
|
||||
@@ -37,18 +37,17 @@ public sealed class ProofChainQueryService : IProofChainQueryService
|
||||
// Query attestor entries by artifact sha256
|
||||
var query = new AttestorEntryQuery
|
||||
{
|
||||
ArtifactSha256 = NormalizeDigest(subjectDigest),
|
||||
PageSize = 100,
|
||||
SortBy = "CreatedAt",
|
||||
SortDirection = "Descending"
|
||||
Subject = NormalizeDigest(subjectDigest),
|
||||
PageSize = 100
|
||||
};
|
||||
|
||||
var entries = await _entryRepository.QueryAsync(query, cancellationToken);
|
||||
|
||||
var proofs = entries.Items
|
||||
.OrderByDescending(e => e.CreatedAt)
|
||||
.Select(entry => new ProofSummary
|
||||
{
|
||||
ProofId = entry.RekorUuid ?? entry.Id.ToString(),
|
||||
ProofId = entry.RekorUuid,
|
||||
Type = DetermineProofType(entry.Artifact.Kind),
|
||||
Digest = entry.BundleSha256,
|
||||
CreatedAt = entry.CreatedAt,
|
||||
@@ -154,7 +153,7 @@ public sealed class ProofChainQueryService : IProofChainQueryService
|
||||
|
||||
var detail = new ProofDetail
|
||||
{
|
||||
ProofId = entry.RekorUuid ?? entry.Id.ToString(),
|
||||
ProofId = entry.RekorUuid,
|
||||
Type = DetermineProofType(entry.Artifact.Kind),
|
||||
Digest = entry.BundleSha256,
|
||||
CreatedAt = entry.CreatedAt,
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.37" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
|
||||
<PackageReference Include="Serilog.AspNetCore" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" />
|
||||
<PackageReference Include="StackExchange.Redis" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\StellaOps.Attestor.Core\StellaOps.Attestor.Core.csproj" />
|
||||
@@ -27,7 +27,7 @@
|
||||
<ProjectReference Include="../../../Authority/StellaOps.Authority/StellaOps.Auth.Client/StellaOps.Auth.Client.csproj" />
|
||||
<ProjectReference Include="../../../Authority/StellaOps.Authority/StellaOps.Auth.ServerIntegration/StellaOps.Auth.ServerIntegration.csproj" />
|
||||
<ProjectReference Include="../../__Libraries/StellaOps.Attestor.StandardPredicates/StellaOps.Attestor.StandardPredicates.csproj" />
|
||||
<ProjectReference Include="../../../__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="../../../Router/__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
|
||||
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Bundling\StellaOps.Attestor.Bundling.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Core", "StellaOps.Attestor.Core\StellaOps.Attestor.Core.csproj", "{C0FE77EB-933C-4E47-8195-758AB049157A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Infrastructure", "StellaOps.Attestor.Infrastructure\StellaOps.Attestor.Infrastructure.csproj", "{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.WebService", "StellaOps.Attestor.WebService\StellaOps.Attestor.WebService.csproj", "{B238B098-32B1-4875-99A7-393A63AC3CCF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Configuration", "..\..\__Libraries\StellaOps.Configuration\StellaOps.Configuration.csproj", "{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Cryptography", "..\..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj", "{82EFA477-307D-4B47-A4CF-1627F076D60A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.DependencyInjection", "..\..\__Libraries\StellaOps.DependencyInjection\StellaOps.DependencyInjection.csproj", "{21327A4F-2586-49F8-9D4A-3840DE64C48E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Tests", "StellaOps.Attestor.Tests\StellaOps.Attestor.Tests.csproj", "{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Attestor.Verify", "..\StellaOps.Attestor.Verify\StellaOps.Attestor.Verify.csproj", "{99EC90D8-0D5E-41E4-A895-585A7680916C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C0FE77EB-933C-4E47-8195-758AB049157A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|x64.Build.0 = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{996D74F8-8683-45FA-90AB-DA7ACE78D4B3}.Release|x86.Build.0 = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{B238B098-32B1-4875-99A7-393A63AC3CCF}.Release|x86.Build.0 = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{988E2AC7-50E0-4845-B1C2-BA4931F2FFD7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{82EFA477-307D-4B47-A4CF-1627F076D60A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{21327A4F-2586-49F8-9D4A-3840DE64C48E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4B7592CD-D67C-4F4D-82FE-DF99BAAC4275}.Release|x86.Build.0 = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{99EC90D8-0D5E-41E4-A895-585A7680916C}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Reference in New Issue
Block a user