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
Export Center CI / export-ci (push) Has been cancelled
devportal-offline / build-offline (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
Export Center CI / export-ci (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
This commit is contained in:
@@ -12,6 +12,7 @@ namespace StellaOps.ExportCenter.RiskBundles;
|
||||
public sealed class RiskBundleBuilder
|
||||
{
|
||||
private const string ManifestVersion = "1";
|
||||
private static readonly string[] MandatoryProviderIds = { "cisa-kev" };
|
||||
private static readonly UnixFileMode DefaultFileMode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead;
|
||||
private static readonly DateTimeOffset FixedTimestamp = new(2024, 01, 01, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
@@ -54,6 +55,7 @@ public sealed class RiskBundleBuilder
|
||||
private static List<RiskBundleProviderEntry> CollectProviders(RiskBundleBuildRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var entries = new List<RiskBundleProviderEntry>(request.Providers.Count);
|
||||
var seen = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var provider in request.Providers.OrderBy(p => p.ProviderId, StringComparer.Ordinal))
|
||||
{
|
||||
@@ -88,17 +90,26 @@ public sealed class RiskBundleBuilder
|
||||
var sha256 = ComputeSha256FromFile(fullPath);
|
||||
var size = new FileInfo(fullPath).Length;
|
||||
var bundlePath = $"providers/{provider.ProviderId}/snapshot";
|
||||
var signaturePath = ResolveSignaturePath(provider);
|
||||
string? signatureSha256 = null;
|
||||
if (!string.IsNullOrWhiteSpace(signaturePath) && File.Exists(signaturePath))
|
||||
{
|
||||
signatureSha256 = ComputeSha256FromFile(signaturePath);
|
||||
}
|
||||
|
||||
seen.Add(provider.ProviderId);
|
||||
|
||||
entries.Add(new RiskBundleProviderEntry(
|
||||
provider.ProviderId,
|
||||
provider.Source,
|
||||
provider.SnapshotDate,
|
||||
sha256,
|
||||
signatureSha256,
|
||||
size,
|
||||
provider.Optional,
|
||||
bundlePath,
|
||||
fullPath,
|
||||
SignaturePath: null));
|
||||
signaturePath));
|
||||
}
|
||||
|
||||
if (entries.Count == 0)
|
||||
@@ -106,6 +117,14 @@ public sealed class RiskBundleBuilder
|
||||
throw new InvalidOperationException("No provider artefacts collected. Provide at least one valid provider input.");
|
||||
}
|
||||
|
||||
foreach (var mandatory in MandatoryProviderIds)
|
||||
{
|
||||
if (!seen.Contains(mandatory))
|
||||
{
|
||||
throw new InvalidOperationException($"Mandatory provider '{mandatory}' is missing from build inputs.");
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
@@ -135,6 +154,8 @@ public sealed class RiskBundleBuilder
|
||||
.Append(entry.Optional ? '1' : '0')
|
||||
.Append('\0')
|
||||
.Append(entry.SnapshotDate?.ToString("yyyy-MM-dd") ?? string.Empty)
|
||||
.Append('\0')
|
||||
.Append(entry.SignatureSha256 ?? string.Empty)
|
||||
.Append('\0');
|
||||
}
|
||||
|
||||
@@ -207,6 +228,18 @@ public sealed class RiskBundleBuilder
|
||||
DataStream = dataStream
|
||||
};
|
||||
writer.WriteEntry(tarEntry);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(entry.SignaturePath) && File.Exists(entry.SignaturePath))
|
||||
{
|
||||
using var sigStream = new FileStream(entry.SignaturePath, FileMode.Open, FileAccess.Read, FileShare.Read, 64 * 1024, FileOptions.SequentialScan);
|
||||
var sigEntry = new PaxTarEntry(TarEntryType.RegularFile, $"{Path.GetDirectoryName(entry.BundlePath)?.TrimEnd('/')}/signature")
|
||||
{
|
||||
Mode = DefaultFileMode,
|
||||
ModificationTime = FixedTimestamp,
|
||||
DataStream = sigStream
|
||||
};
|
||||
writer.WriteEntry(sigEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteTextEntry(TarWriter writer, string path, string content, UnixFileMode mode)
|
||||
@@ -238,4 +271,15 @@ public sealed class RiskBundleBuilder
|
||||
stream.Write(buffer);
|
||||
stream.Position = originalPosition;
|
||||
}
|
||||
|
||||
private static string? ResolveSignaturePath(RiskBundleProviderInput provider)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(provider.SignaturePath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var full = Path.GetFullPath(provider.SignaturePath);
|
||||
return File.Exists(full) ? full : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ public sealed record RiskBundleProviderInput(
|
||||
string ProviderId,
|
||||
string SourcePath,
|
||||
string Source,
|
||||
string? SignaturePath = null,
|
||||
bool Optional = false,
|
||||
DateOnly? SnapshotDate = null);
|
||||
|
||||
@@ -14,6 +15,7 @@ public sealed record RiskBundleProviderEntry(
|
||||
string Source,
|
||||
DateOnly? SnapshotDate,
|
||||
string Sha256,
|
||||
string? SignatureSha256,
|
||||
long SizeBytes,
|
||||
bool Optional,
|
||||
string BundlePath,
|
||||
|
||||
Reference in New Issue
Block a user