save progress

This commit is contained in:
StellaOps Bot
2026-01-04 19:08:47 +02:00
parent f7d27c6fda
commit 75611a505f
97 changed files with 4531 additions and 293 deletions

View File

@@ -81,9 +81,64 @@ public sealed class BundleBuilder : IBundleBuilder
cryptoConfig.ExpiresAt));
}
var ruleBundles = new List<RuleBundleComponent>();
foreach (var ruleBundleConfig in request.RuleBundles)
{
// Validate relative path before combining
var targetDir = PathValidation.SafeCombine(outputPath, ruleBundleConfig.RelativePath);
Directory.CreateDirectory(targetDir);
var files = new List<RuleBundleFileComponent>();
long bundleTotalSize = 0;
var digestBuilder = new System.Text.StringBuilder();
// Copy all files from source directory
if (Directory.Exists(ruleBundleConfig.SourceDirectory))
{
foreach (var sourceFile in Directory.GetFiles(ruleBundleConfig.SourceDirectory)
.OrderBy(f => Path.GetFileName(f), StringComparer.Ordinal))
{
var fileName = Path.GetFileName(sourceFile);
var targetFile = Path.Combine(targetDir, fileName);
await using (var input = File.OpenRead(sourceFile))
await using (var output = File.Create(targetFile))
{
await input.CopyToAsync(output, ct).ConfigureAwait(false);
}
await using var digestStream = File.OpenRead(targetFile);
var hash = await SHA256.HashDataAsync(digestStream, ct).ConfigureAwait(false);
var fileDigest = Convert.ToHexString(hash).ToLowerInvariant();
var fileInfo = new FileInfo(targetFile);
files.Add(new RuleBundleFileComponent(fileName, fileDigest, fileInfo.Length));
bundleTotalSize += fileInfo.Length;
digestBuilder.Append(fileDigest);
}
}
// Compute combined digest from all file digests
var combinedDigest = Convert.ToHexString(
SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(digestBuilder.ToString()))).ToLowerInvariant();
ruleBundles.Add(new RuleBundleComponent(
ruleBundleConfig.BundleId,
ruleBundleConfig.BundleType,
ruleBundleConfig.Version,
ruleBundleConfig.RelativePath,
combinedDigest,
bundleTotalSize,
ruleBundleConfig.RuleCount,
ruleBundleConfig.SignerKeyId,
ruleBundleConfig.SignedAt,
files.ToImmutableArray()));
}
var totalSize = feeds.Sum(f => f.SizeBytes) +
policies.Sum(p => p.SizeBytes) +
cryptoMaterials.Sum(c => c.SizeBytes);
cryptoMaterials.Sum(c => c.SizeBytes) +
ruleBundles.Sum(r => r.SizeBytes);
var manifest = new BundleManifest
{
@@ -96,6 +151,7 @@ public sealed class BundleBuilder : IBundleBuilder
Feeds = feeds.ToImmutableArray(),
Policies = policies.ToImmutableArray(),
CryptoMaterials = cryptoMaterials.ToImmutableArray(),
RuleBundles = ruleBundles.ToImmutableArray(),
TotalSizeBytes = totalSize
};
@@ -138,7 +194,8 @@ public sealed record BundleBuildRequest(
DateTimeOffset? ExpiresAt,
IReadOnlyList<FeedBuildConfig> Feeds,
IReadOnlyList<PolicyBuildConfig> Policies,
IReadOnlyList<CryptoBuildConfig> CryptoMaterials);
IReadOnlyList<CryptoBuildConfig> CryptoMaterials,
IReadOnlyList<RuleBundleBuildConfig> RuleBundles);
public abstract record BundleComponentSource(string SourcePath, string RelativePath);
@@ -169,3 +226,24 @@ public sealed record CryptoBuildConfig(
CryptoComponentType Type,
DateTimeOffset? ExpiresAt)
: BundleComponentSource(SourcePath, RelativePath);
/// <summary>
/// Configuration for building a rule bundle component.
/// </summary>
/// <param name="BundleId">Bundle identifier (e.g., "secrets.ruleset").</param>
/// <param name="BundleType">Bundle type (e.g., "secrets", "malware").</param>
/// <param name="Version">Bundle version in YYYY.MM format.</param>
/// <param name="SourceDirectory">Source directory containing the rule bundle files.</param>
/// <param name="RelativePath">Relative path in the output bundle.</param>
/// <param name="RuleCount">Number of rules in the bundle.</param>
/// <param name="SignerKeyId">Key ID used to sign the bundle.</param>
/// <param name="SignedAt">When the bundle was signed.</param>
public sealed record RuleBundleBuildConfig(
string BundleId,
string BundleType,
string Version,
string SourceDirectory,
string RelativePath,
int RuleCount,
string? SignerKeyId,
DateTimeOffset? SignedAt);