up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
This commit is contained in:
@@ -7,6 +7,7 @@ namespace StellaOps.Policy.Engine.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Compiles policy DSL to canonical representation, signs it deterministically, and stores per revision.
|
||||
/// Captures AOC (Attestation of Compliance) metadata for policy revisions.
|
||||
/// </summary>
|
||||
internal sealed class PolicyBundleService
|
||||
{
|
||||
@@ -40,7 +41,9 @@ internal sealed class PolicyBundleService
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
var compiledAt = _timeProvider.GetUtcNow();
|
||||
var compileResult = _compilationService.Compile(new PolicyCompileRequest(request.Dsl));
|
||||
|
||||
if (!compileResult.Success || compileResult.CanonicalRepresentation.IsDefaultOrEmpty)
|
||||
{
|
||||
return new PolicyBundleResponse(
|
||||
@@ -49,30 +52,55 @@ internal sealed class PolicyBundleService
|
||||
Signature: null,
|
||||
SizeBytes: 0,
|
||||
CreatedAt: null,
|
||||
Diagnostics: compileResult.Diagnostics);
|
||||
Diagnostics: compileResult.Diagnostics,
|
||||
AocMetadata: null);
|
||||
}
|
||||
|
||||
var payload = compileResult.CanonicalRepresentation.ToArray();
|
||||
var digest = compileResult.Digest ?? $"sha256:{ComputeSha256Hex(payload)}";
|
||||
var signature = Sign(digest, request.SigningKeyId);
|
||||
var artifactDigest = compileResult.Digest ?? $"sha256:{ComputeSha256Hex(payload)}";
|
||||
var sourceDigest = ComputeSourceDigest(request.Dsl.Source);
|
||||
var signature = Sign(artifactDigest, request.SigningKeyId);
|
||||
var createdAt = _timeProvider.GetUtcNow();
|
||||
|
||||
// Generate AOC metadata
|
||||
var compilationId = GenerateCompilationId(packId, version, compiledAt);
|
||||
var aocMetadata = CreateAocMetadata(
|
||||
compilationId,
|
||||
request.Dsl.Syntax,
|
||||
compiledAt,
|
||||
sourceDigest,
|
||||
artifactDigest,
|
||||
compileResult,
|
||||
request.Provenance);
|
||||
|
||||
var record = new PolicyBundleRecord(
|
||||
Digest: digest,
|
||||
Digest: artifactDigest,
|
||||
Signature: signature,
|
||||
Size: payload.Length,
|
||||
CreatedAt: createdAt,
|
||||
Payload: payload.ToImmutableArray());
|
||||
Payload: payload.ToImmutableArray(),
|
||||
AocMetadata: aocMetadata);
|
||||
|
||||
await _repository.StoreBundleAsync(packId, version, record, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var aocResponse = new PolicyAocMetadataResponse(
|
||||
CompilationId: aocMetadata.CompilationId,
|
||||
CompilerVersion: aocMetadata.CompilerVersion,
|
||||
CompiledAt: aocMetadata.CompiledAt,
|
||||
SourceDigest: aocMetadata.SourceDigest,
|
||||
ArtifactDigest: aocMetadata.ArtifactDigest,
|
||||
ComplexityScore: aocMetadata.ComplexityScore,
|
||||
RuleCount: aocMetadata.RuleCount,
|
||||
DurationMilliseconds: aocMetadata.DurationMilliseconds);
|
||||
|
||||
return new PolicyBundleResponse(
|
||||
Success: true,
|
||||
Digest: digest,
|
||||
Digest: artifactDigest,
|
||||
Signature: signature,
|
||||
SizeBytes: payload.Length,
|
||||
CreatedAt: createdAt,
|
||||
Diagnostics: compileResult.Diagnostics);
|
||||
Diagnostics: compileResult.Diagnostics,
|
||||
AocMetadata: aocResponse);
|
||||
}
|
||||
|
||||
private static string ComputeSha256Hex(byte[] payload)
|
||||
@@ -82,6 +110,14 @@ internal sealed class PolicyBundleService
|
||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static string ComputeSourceDigest(string source)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(source);
|
||||
Span<byte> hash = stackalloc byte[32];
|
||||
SHA256.HashData(bytes, hash);
|
||||
return $"sha256:{Convert.ToHexString(hash).ToLowerInvariant()}";
|
||||
}
|
||||
|
||||
private static string Sign(string digest, string? signingKeyId)
|
||||
{
|
||||
// Deterministic signature stub suitable for offline testing.
|
||||
@@ -89,4 +125,51 @@ internal sealed class PolicyBundleService
|
||||
var mac = HMACSHA256.HashData(Encoding.UTF8.GetBytes(key), Encoding.UTF8.GetBytes(digest));
|
||||
return $"sig:sha256:{Convert.ToHexString(mac).ToLowerInvariant()}";
|
||||
}
|
||||
|
||||
private static string GenerateCompilationId(string packId, int version, DateTimeOffset timestamp)
|
||||
{
|
||||
// Deterministic compilation ID based on pack, version, and timestamp
|
||||
var input = $"{packId}:{version}:{timestamp:O}";
|
||||
var bytes = Encoding.UTF8.GetBytes(input);
|
||||
Span<byte> hash = stackalloc byte[32];
|
||||
SHA256.HashData(bytes, hash);
|
||||
return $"comp-{Convert.ToHexString(hash).ToLowerInvariant()[..16]}";
|
||||
}
|
||||
|
||||
private static PolicyAocMetadata CreateAocMetadata(
|
||||
string compilationId,
|
||||
string compilerVersion,
|
||||
DateTimeOffset compiledAt,
|
||||
string sourceDigest,
|
||||
string artifactDigest,
|
||||
PolicyCompilationResultDto compileResult,
|
||||
PolicyProvenanceInput? provenanceInput)
|
||||
{
|
||||
var complexity = compileResult.Complexity;
|
||||
var statistics = compileResult.Statistics;
|
||||
|
||||
PolicyProvenance? provenance = null;
|
||||
if (provenanceInput is not null)
|
||||
{
|
||||
provenance = new PolicyProvenance(
|
||||
SourceType: provenanceInput.SourceType,
|
||||
SourceUrl: provenanceInput.SourceUrl,
|
||||
Submitter: provenanceInput.Submitter,
|
||||
CommitSha: provenanceInput.CommitSha,
|
||||
Branch: provenanceInput.Branch,
|
||||
IngestedAt: compiledAt);
|
||||
}
|
||||
|
||||
return new PolicyAocMetadata(
|
||||
CompilationId: compilationId,
|
||||
CompilerVersion: compilerVersion,
|
||||
CompiledAt: compiledAt,
|
||||
SourceDigest: sourceDigest,
|
||||
ArtifactDigest: artifactDigest,
|
||||
ComplexityScore: complexity?.Score ?? 0,
|
||||
RuleCount: statistics?.RuleCount ?? complexity?.RuleCount ?? 0,
|
||||
DurationMilliseconds: compileResult.DurationMilliseconds,
|
||||
Provenance: provenance,
|
||||
AttestationRef: null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user