save progress

This commit is contained in:
master
2026-01-09 18:27:36 +02:00
parent e608752924
commit a21d3dbc1f
361 changed files with 63068 additions and 1192 deletions

View File

@@ -23,6 +23,7 @@ using StellaOps.Attestor.Core.Storage;
using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Infrastructure;
using StellaOps.Attestor.Spdx3;
using StellaOps.Attestor.WebService.Options;
using StellaOps.Configuration;
using StellaOps.Cryptography.DependencyInjection;
@@ -129,6 +130,9 @@ internal static class AttestorWebServiceComposition
builder.Services.AddScoped<Services.IProofChainQueryService, Services.ProofChainQueryService>();
builder.Services.AddScoped<Services.IProofVerificationService, Services.ProofVerificationService>();
// SPDX 3.0.1 Build profile support (BP-007)
builder.Services.AddSingleton<IBuildAttestationMapper, BuildAttestationMapper>();
builder.Services.AddSingleton<StellaOps.Attestor.StandardPredicates.IStandardPredicateRegistry>(sp =>
{
var registry = new StellaOps.Attestor.StandardPredicates.StandardPredicateRegistry();

View File

@@ -11,6 +11,7 @@ using StellaOps.Attestor.Core.Signing;
using StellaOps.Attestor.Core.Storage;
using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Spdx3;
using StellaOps.Attestor.WebService.Contracts;
namespace StellaOps.Attestor.WebService;
@@ -394,6 +395,125 @@ internal static class AttestorWebServiceEndpoints
return Results.Ok(BulkVerificationContracts.MapJob(job));
}).RequireAuthorization("attestor:write");
// SPDX 3.0.1 Build Profile export endpoint (BP-007)
app.MapPost("/api/v1/attestations:export-build", (
Spdx3BuildExportRequestDto? requestDto,
HttpContext httpContext,
IBuildAttestationMapper mapper) =>
{
if (requestDto is null)
{
return Results.Problem(statusCode: StatusCodes.Status400BadRequest, title: "Request body is required.");
}
if (!IsJsonContentType(httpContext.Request.ContentType))
{
return UnsupportedMediaTypeResult();
}
if (string.IsNullOrWhiteSpace(requestDto.BuildType))
{
return Results.Problem(statusCode: StatusCodes.Status400BadRequest, title: "buildType is required.");
}
// Build the attestation payload from the request
var configSource = (!string.IsNullOrWhiteSpace(requestDto.ConfigSourceUri) ||
requestDto.ConfigSourceDigest?.Count > 0 ||
!string.IsNullOrWhiteSpace(requestDto.ConfigEntryPoint))
? new ConfigSource
{
Uri = requestDto.ConfigSourceUri,
Digest = requestDto.ConfigSourceDigest ?? new Dictionary<string, string>(),
EntryPoint = requestDto.ConfigEntryPoint
}
: null;
var materials = requestDto.Materials?.Select(m => new BuildMaterial
{
Uri = m.Uri,
Digest = m.Digest ?? new Dictionary<string, string>()
}).ToList() ?? new List<BuildMaterial>();
var attestationPayload = new BuildAttestationPayload
{
BuildType = requestDto.BuildType,
Builder = !string.IsNullOrWhiteSpace(requestDto.BuilderId)
? new BuilderInfo
{
Id = requestDto.BuilderId,
Version = requestDto.BuilderVersion
}
: null,
Invocation = new BuildInvocation
{
ConfigSource = configSource,
Environment = requestDto.Environment ?? new Dictionary<string, string>(),
Parameters = requestDto.Parameters ?? new Dictionary<string, string>()
},
Metadata = new BuildMetadata
{
BuildInvocationId = requestDto.BuildId,
BuildStartedOn = requestDto.BuildStartTime,
BuildFinishedOn = requestDto.BuildEndTime
},
Materials = materials
};
// Check if the payload can be mapped
if (!mapper.CanMapToSpdx3(attestationPayload))
{
return Results.Problem(
statusCode: StatusCodes.Status400BadRequest,
title: "Cannot map attestation to SPDX 3.0.1",
detail: "The provided attestation payload is missing required fields for SPDX 3.0.1 Build profile.");
}
// Map to SPDX 3.0.1 Build element
var spdx3Build = mapper.MapToSpdx3(attestationPayload, requestDto.SpdxIdPrefix);
// Build response based on requested format
var response = new Spdx3BuildExportResponseDto
{
Format = requestDto.Format,
BuildSpdxId = spdx3Build.SpdxId,
Spdx3Document = requestDto.Format is BuildAttestationFormat.Spdx3 or BuildAttestationFormat.Both
? new
{
spdxVersion = "SPDX-3.0.1",
conformsTo = new[] { "https://spdx.org/rdf/v3/Build" },
spdxId = $"{requestDto.SpdxIdPrefix}/document",
elements = new object[]
{
new
{
type = spdx3Build.Type,
spdxId = spdx3Build.SpdxId,
name = spdx3Build.Name,
build_buildType = spdx3Build.BuildType,
build_buildId = spdx3Build.BuildId,
build_buildStartTime = spdx3Build.BuildStartTime?.ToString("O", CultureInfo.InvariantCulture),
build_buildEndTime = spdx3Build.BuildEndTime?.ToString("O", CultureInfo.InvariantCulture),
build_configSourceUri = spdx3Build.ConfigSourceUri.IsEmpty ? null : spdx3Build.ConfigSourceUri.ToArray(),
build_configSourceDigest = spdx3Build.ConfigSourceDigest.IsEmpty ? null : spdx3Build.ConfigSourceDigest.Select(h => new { algorithm = h.Algorithm, hashValue = h.HashValue }).ToArray(),
build_configSourceEntrypoint = spdx3Build.ConfigSourceEntrypoint.IsEmpty ? null : spdx3Build.ConfigSourceEntrypoint.ToArray(),
build_environment = spdx3Build.Environment.Count > 0 ? spdx3Build.Environment : null,
build_parameter = spdx3Build.Parameter.Count > 0 ? spdx3Build.Parameter : null
}
}
}
: null,
// DSSE envelope generation would require signing service integration
// For now, return null for DSSE when not specifically requested or when signing is disabled
DsseEnvelope = null,
Signing = null
};
return Results.Ok(response);
})
.RequireAuthorization("attestor:write")
.RequireRateLimiting("attestor-submissions")
.Produces<Spdx3BuildExportResponseDto>(StatusCodes.Status200OK);
}
private static async Task<IResult> GetAttestationDetailResultAsync(

View File

@@ -0,0 +1,221 @@
// -----------------------------------------------------------------------------
// Spdx3BuildProfileContracts.cs
// Sprint: SPRINT_20260107_004_003_BE
// Task: BP-007 - Attestor WebService Integration for SPDX 3.0.1 Build Profile
// Description: DTOs for SPDX 3.0.1 Build profile export endpoint
// -----------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
namespace StellaOps.Attestor.WebService.Contracts;
/// <summary>
/// Supported export formats for build attestations.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum BuildAttestationFormat
{
/// <summary>
/// DSSE (Dead Simple Signing Envelope) format - default.
/// </summary>
Dsse = 0,
/// <summary>
/// SPDX 3.0.1 Build profile format.
/// </summary>
Spdx3 = 1,
/// <summary>
/// Both DSSE and SPDX 3.0.1 formats combined.
/// </summary>
Both = 2
}
/// <summary>
/// Request to export a build attestation in SPDX 3.0.1 format.
/// </summary>
public sealed record Spdx3BuildExportRequestDto
{
/// <summary>
/// Gets or sets the build type URI (e.g., "https://slsa.dev/provenance/v1").
/// </summary>
[Required]
public required string BuildType { get; init; }
/// <summary>
/// Gets or sets the builder ID URI.
/// </summary>
public string? BuilderId { get; init; }
/// <summary>
/// Gets or sets the builder version.
/// </summary>
public string? BuilderVersion { get; init; }
/// <summary>
/// Gets or sets the build invocation ID.
/// </summary>
public string? BuildId { get; init; }
/// <summary>
/// Gets or sets when the build started.
/// </summary>
public DateTimeOffset? BuildStartTime { get; init; }
/// <summary>
/// Gets or sets when the build finished.
/// </summary>
public DateTimeOffset? BuildEndTime { get; init; }
/// <summary>
/// Gets or sets the configuration source URI.
/// </summary>
public string? ConfigSourceUri { get; init; }
/// <summary>
/// Gets or sets the configuration source digest (algorithm:value).
/// </summary>
public Dictionary<string, string>? ConfigSourceDigest { get; init; }
/// <summary>
/// Gets or sets the configuration entry point.
/// </summary>
public string? ConfigEntryPoint { get; init; }
/// <summary>
/// Gets or sets the build environment variables.
/// </summary>
public Dictionary<string, string>? Environment { get; init; }
/// <summary>
/// Gets or sets the build parameters.
/// </summary>
public Dictionary<string, string>? Parameters { get; init; }
/// <summary>
/// Gets or sets the build materials (source inputs).
/// </summary>
public List<BuildMaterialDto>? Materials { get; init; }
/// <summary>
/// Gets or sets the output format.
/// </summary>
public BuildAttestationFormat Format { get; init; } = BuildAttestationFormat.Dsse;
/// <summary>
/// Gets or sets whether to sign the SPDX 3.0.1 document with DSSE.
/// </summary>
public bool Sign { get; init; } = true;
/// <summary>
/// Gets or sets the SPDX ID prefix for generated elements.
/// </summary>
public string SpdxIdPrefix { get; init; } = "urn:stellaops";
}
/// <summary>
/// Build material (input) DTO.
/// </summary>
public sealed record BuildMaterialDto
{
/// <summary>
/// Gets or sets the material URI.
/// </summary>
[Required]
public required string Uri { get; init; }
/// <summary>
/// Gets or sets the material digest (algorithm:value).
/// </summary>
public Dictionary<string, string>? Digest { get; init; }
}
/// <summary>
/// Response containing SPDX 3.0.1 Build profile export result.
/// </summary>
public sealed record Spdx3BuildExportResponseDto
{
/// <summary>
/// Gets or sets the format of the response.
/// </summary>
public required BuildAttestationFormat Format { get; init; }
/// <summary>
/// Gets or sets the SPDX 3.0.1 document (JSON-LD) when format is Spdx3 or Both.
/// </summary>
public object? Spdx3Document { get; init; }
/// <summary>
/// Gets or sets the DSSE envelope when format is Dsse or Both.
/// </summary>
public DsseEnvelopeDto? DsseEnvelope { get; init; }
/// <summary>
/// Gets or sets the SPDX ID of the generated Build element.
/// </summary>
public string? BuildSpdxId { get; init; }
/// <summary>
/// Gets or sets the signing information.
/// </summary>
public BuildSigningInfoDto? Signing { get; init; }
}
/// <summary>
/// DSSE envelope DTO.
/// </summary>
public sealed record DsseEnvelopeDto
{
/// <summary>
/// Gets or sets the payload type.
/// </summary>
public required string PayloadType { get; init; }
/// <summary>
/// Gets or sets the base64-encoded payload.
/// </summary>
public required string PayloadBase64 { get; init; }
/// <summary>
/// Gets or sets the signatures.
/// </summary>
public required List<DsseSignatureDto> Signatures { get; init; }
}
/// <summary>
/// DSSE signature DTO.
/// </summary>
public sealed record DsseSignatureDto
{
/// <summary>
/// Gets or sets the key ID.
/// </summary>
public required string KeyId { get; init; }
/// <summary>
/// Gets or sets the base64-encoded signature.
/// </summary>
public required string Sig { get; init; }
}
/// <summary>
/// Build signing information DTO.
/// </summary>
public sealed record BuildSigningInfoDto
{
/// <summary>
/// Gets or sets the key ID used for signing.
/// </summary>
public required string KeyId { get; init; }
/// <summary>
/// Gets or sets the signing algorithm.
/// </summary>
public required string Algorithm { get; init; }
/// <summary>
/// Gets or sets when the document was signed.
/// </summary>
public required string SignedAt { get; init; }
}

View File

@@ -29,5 +29,6 @@
<ProjectReference Include="../../__Libraries/StellaOps.Attestor.StandardPredicates/StellaOps.Attestor.StandardPredicates.csproj" />
<ProjectReference Include="../../../Router/__Libraries/StellaOps.Router.AspNet/StellaOps.Router.AspNet.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Bundling\StellaOps.Attestor.Bundling.csproj" />
<ProjectReference Include="..\..\__Libraries\StellaOps.Attestor.Spdx3\StellaOps.Attestor.Spdx3.csproj" />
</ItemGroup>
</Project>