Add unit tests for SBOM ingestion and transformation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implement `SbomIngestServiceCollectionExtensionsTests` to verify the SBOM ingestion pipeline exports snapshots correctly. - Create `SbomIngestTransformerTests` to ensure the transformation produces expected nodes and edges, including deduplication of license nodes and normalization of timestamps. - Add `SbomSnapshotExporterTests` to test the export functionality for manifest, adjacency, nodes, and edges. - Introduce `VexOverlayTransformerTests` to validate the transformation of VEX nodes and edges. - Set up project file for the test project with necessary dependencies and configurations. - Include JSON fixture files for testing purposes.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Builders;
|
||||
|
||||
public sealed record EvidenceBundleBuildRequest(
|
||||
EvidenceBundleId BundleId,
|
||||
TenantId TenantId,
|
||||
EvidenceBundleKind Kind,
|
||||
DateTimeOffset CreatedAt,
|
||||
IReadOnlyDictionary<string, string> Metadata,
|
||||
IReadOnlyList<EvidenceBundleMaterial> Materials);
|
||||
|
||||
public sealed record EvidenceBundleMaterial(
|
||||
string Section,
|
||||
string Path,
|
||||
string Sha256,
|
||||
long SizeBytes,
|
||||
string MediaType,
|
||||
IReadOnlyDictionary<string, string>? Attributes = null);
|
||||
|
||||
public sealed record EvidenceManifestEntry(
|
||||
string Section,
|
||||
string CanonicalPath,
|
||||
string Sha256,
|
||||
long SizeBytes,
|
||||
string MediaType,
|
||||
IReadOnlyDictionary<string, string> Attributes);
|
||||
|
||||
public sealed record EvidenceBundleManifest(
|
||||
EvidenceBundleId BundleId,
|
||||
TenantId TenantId,
|
||||
EvidenceBundleKind Kind,
|
||||
DateTimeOffset CreatedAt,
|
||||
IReadOnlyDictionary<string, string> Metadata,
|
||||
IReadOnlyList<EvidenceManifestEntry> Entries);
|
||||
|
||||
public sealed record EvidenceBundleBuildResult(
|
||||
string RootHash,
|
||||
EvidenceBundleManifest Manifest);
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace StellaOps.EvidenceLocker.Core.Builders;
|
||||
|
||||
public interface IEvidenceBundleBuilder
|
||||
{
|
||||
Task<EvidenceBundleBuildResult> BuildAsync(
|
||||
EvidenceBundleBuildRequest request,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Builders;
|
||||
|
||||
public interface IMerkleTreeCalculator
|
||||
{
|
||||
string CalculateRootHash(IEnumerable<string> canonicalLeafValues);
|
||||
}
|
||||
|
||||
public sealed class MerkleTreeCalculator : IMerkleTreeCalculator
|
||||
{
|
||||
public string CalculateRootHash(IEnumerable<string> canonicalLeafValues)
|
||||
{
|
||||
var leaves = canonicalLeafValues
|
||||
.Select(value => HashString(value))
|
||||
.ToArray();
|
||||
|
||||
if (leaves.Length == 0)
|
||||
{
|
||||
return HashString("stellaops:evidence:empty");
|
||||
}
|
||||
|
||||
return BuildTree(leaves);
|
||||
}
|
||||
|
||||
private static string BuildTree(IReadOnlyList<string> currentLevel)
|
||||
{
|
||||
if (currentLevel.Count == 1)
|
||||
{
|
||||
return currentLevel[0];
|
||||
}
|
||||
|
||||
var nextLevel = new List<string>((currentLevel.Count + 1) / 2);
|
||||
for (var i = 0; i < currentLevel.Count; i += 2)
|
||||
{
|
||||
var left = currentLevel[i];
|
||||
var right = i + 1 < currentLevel.Count ? currentLevel[i + 1] : left;
|
||||
var combined = string.CompareOrdinal(left, right) <= 0
|
||||
? $"{left}|{right}"
|
||||
: $"{right}|{left}";
|
||||
nextLevel.Add(HashString(combined));
|
||||
}
|
||||
|
||||
return BuildTree(nextLevel);
|
||||
}
|
||||
|
||||
private static string HashString(string value)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(value);
|
||||
var hash = SHA256.HashData(bytes);
|
||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace StellaOps.EvidenceLocker.Core;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using StellaOps.Cryptography;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Configuration;
|
||||
|
||||
public sealed class EvidenceLockerOptions
|
||||
{
|
||||
public const string SectionName = "EvidenceLocker";
|
||||
|
||||
[Required]
|
||||
public required DatabaseOptions Database { get; init; }
|
||||
|
||||
[Required]
|
||||
public required ObjectStoreOptions ObjectStore { get; init; }
|
||||
|
||||
[Required]
|
||||
public required QuotaOptions Quotas { get; init; }
|
||||
|
||||
[Required]
|
||||
public required SigningOptions Signing { get; init; }
|
||||
|
||||
public TimelineOptions? Timeline { get; init; }
|
||||
|
||||
public PortableOptions Portable { get; init; } = new();
|
||||
|
||||
public IncidentModeOptions Incident { get; init; } = new();
|
||||
}
|
||||
|
||||
public sealed class DatabaseOptions
|
||||
{
|
||||
[Required]
|
||||
public required string ConnectionString { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Enables automatic execution of SQL migrations at startup.
|
||||
/// </summary>
|
||||
public bool ApplyMigrationsAtStartup { get; init; } = true;
|
||||
}
|
||||
|
||||
public enum ObjectStoreKind
|
||||
{
|
||||
FileSystem = 1,
|
||||
AmazonS3 = 2
|
||||
}
|
||||
|
||||
public sealed class ObjectStoreOptions
|
||||
{
|
||||
[Required]
|
||||
public required ObjectStoreKind Kind { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When true, drivers must prevent object overwrite (WORM mode).
|
||||
/// </summary>
|
||||
public bool EnforceWriteOnce { get; init; } = true;
|
||||
|
||||
public FileSystemStoreOptions? FileSystem { get; init; }
|
||||
|
||||
public AmazonS3StoreOptions? AmazonS3 { get; init; }
|
||||
}
|
||||
|
||||
public sealed class FileSystemStoreOptions
|
||||
{
|
||||
[Required]
|
||||
public required string RootPath { get; init; }
|
||||
}
|
||||
|
||||
public sealed class AmazonS3StoreOptions
|
||||
{
|
||||
[Required]
|
||||
public required string BucketName { get; init; }
|
||||
|
||||
[Required]
|
||||
public required string Region { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional prefix to namespace evidence objects.
|
||||
/// </summary>
|
||||
public string? Prefix { get; init; }
|
||||
|
||||
public bool UseIntelligentTiering { get; init; }
|
||||
}
|
||||
|
||||
public sealed class QuotaOptions
|
||||
{
|
||||
[Range(1, 10_000)]
|
||||
public int MaxMaterialCount { get; init; } = 128;
|
||||
|
||||
[Range(1, long.MaxValue)]
|
||||
public long MaxTotalMaterialSizeBytes { get; init; } = 512L * 1024 * 1024;
|
||||
|
||||
[Range(0, 10_000)]
|
||||
public int MaxMetadataEntries { get; init; } = 64;
|
||||
|
||||
[Range(0, 2048)]
|
||||
public int MaxMetadataKeyLength { get; init; } = 128;
|
||||
|
||||
[Range(0, 8192)]
|
||||
public int MaxMetadataValueLength { get; init; } = 512;
|
||||
}
|
||||
|
||||
public sealed class SigningOptions
|
||||
{
|
||||
public bool Enabled { get; init; } = true;
|
||||
|
||||
[Required]
|
||||
public string Algorithm { get; init; } = SignatureAlgorithms.Es256;
|
||||
|
||||
[Required]
|
||||
public string KeyId { get; init; } = string.Empty;
|
||||
|
||||
public string? Provider { get; init; }
|
||||
|
||||
public string PayloadType { get; init; } = "application/vnd.stella.evidence.manifest+json";
|
||||
|
||||
public SigningKeyMaterialOptions? KeyMaterial { get; init; }
|
||||
|
||||
public TimestampingOptions? Timestamping { get; init; }
|
||||
}
|
||||
|
||||
public sealed class SigningKeyMaterialOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional PEM-encoded EC private key used to seed the default provider.
|
||||
/// </summary>
|
||||
public string? EcPrivateKeyPem { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional PEM-encoded EC public key to accompany the private key when seeding providers that require explicit public material.
|
||||
/// </summary>
|
||||
public string? EcPublicKeyPem { get; init; }
|
||||
}
|
||||
|
||||
public sealed class TimestampingOptions
|
||||
{
|
||||
public bool Enabled { get; init; }
|
||||
|
||||
[Url]
|
||||
public string? Endpoint { get; init; }
|
||||
|
||||
public string HashAlgorithm { get; init; } = "SHA256";
|
||||
|
||||
public bool RequireTimestamp { get; init; }
|
||||
|
||||
[Range(1, 300)]
|
||||
public int RequestTimeoutSeconds { get; init; } = 30;
|
||||
|
||||
public TimestampAuthorityAuthenticationOptions? Authentication { get; init; }
|
||||
}
|
||||
|
||||
public sealed class TimestampAuthorityAuthenticationOptions
|
||||
{
|
||||
public string? Username { get; init; }
|
||||
|
||||
public string? Password { get; init; }
|
||||
}
|
||||
|
||||
public sealed class IncidentModeOptions
|
||||
{
|
||||
public bool Enabled { get; init; }
|
||||
|
||||
[Range(0, 3650)]
|
||||
public int RetentionExtensionDays { get; init; } = 30;
|
||||
|
||||
public bool CaptureRequestSnapshot { get; init; } = true;
|
||||
}
|
||||
|
||||
public sealed class TimelineOptions
|
||||
{
|
||||
public bool Enabled { get; init; }
|
||||
|
||||
[Url]
|
||||
public string? Endpoint { get; init; }
|
||||
|
||||
[Range(1, 300)]
|
||||
public int RequestTimeoutSeconds { get; init; } = 15;
|
||||
|
||||
public string Source { get; init; } = "stellaops.evidence-locker";
|
||||
|
||||
public TimelineAuthenticationOptions? Authentication { get; init; }
|
||||
}
|
||||
|
||||
public sealed class TimelineAuthenticationOptions
|
||||
{
|
||||
public string HeaderName { get; init; } = "Authorization";
|
||||
|
||||
public string Scheme { get; init; } = "Bearer";
|
||||
|
||||
public string? Token { get; init; }
|
||||
}
|
||||
|
||||
public sealed class PortableOptions
|
||||
{
|
||||
public bool Enabled { get; init; } = true;
|
||||
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public string ArtifactName { get; init; } = "portable-bundle-v1.tgz";
|
||||
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public string InstructionsFileName { get; init; } = "instructions-portable.txt";
|
||||
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public string OfflineScriptFileName { get; init; } = "verify-offline.sh";
|
||||
|
||||
[Required]
|
||||
[MinLength(1)]
|
||||
public string MetadataFileName { get; init; } = "bundle.json";
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
public enum EvidenceBundleKind
|
||||
{
|
||||
Evaluation = 1,
|
||||
Job = 2,
|
||||
Export = 3
|
||||
}
|
||||
|
||||
public enum EvidenceBundleStatus
|
||||
{
|
||||
Pending = 1,
|
||||
Assembling = 2,
|
||||
Sealed = 3,
|
||||
Failed = 4,
|
||||
Archived = 5
|
||||
}
|
||||
|
||||
public sealed record EvidenceBundle(
|
||||
EvidenceBundleId Id,
|
||||
TenantId TenantId,
|
||||
EvidenceBundleKind Kind,
|
||||
EvidenceBundleStatus Status,
|
||||
string RootHash,
|
||||
string StorageKey,
|
||||
DateTimeOffset CreatedAt,
|
||||
DateTimeOffset UpdatedAt,
|
||||
string? Description = null,
|
||||
DateTimeOffset? SealedAt = null,
|
||||
DateTimeOffset? ExpiresAt = null,
|
||||
string? PortableStorageKey = null,
|
||||
DateTimeOffset? PortableGeneratedAt = null);
|
||||
|
||||
public sealed record EvidenceArtifact(
|
||||
EvidenceArtifactId Id,
|
||||
EvidenceBundleId BundleId,
|
||||
TenantId TenantId,
|
||||
string Name,
|
||||
string ContentType,
|
||||
long SizeBytes,
|
||||
string StorageKey,
|
||||
string Sha256,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public sealed record EvidenceHold(
|
||||
EvidenceHoldId Id,
|
||||
TenantId TenantId,
|
||||
EvidenceBundleId? BundleId,
|
||||
string CaseId,
|
||||
string Reason,
|
||||
DateTimeOffset CreatedAt,
|
||||
DateTimeOffset? ExpiresAt,
|
||||
DateTimeOffset? ReleasedAt,
|
||||
string? Notes = null);
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
public sealed record EvidenceBundleSignature(
|
||||
EvidenceBundleId BundleId,
|
||||
TenantId TenantId,
|
||||
string PayloadType,
|
||||
string Payload,
|
||||
string Signature,
|
||||
string? KeyId,
|
||||
string Algorithm,
|
||||
string Provider,
|
||||
DateTimeOffset SignedAt,
|
||||
DateTimeOffset? TimestampedAt = null,
|
||||
string? TimestampAuthority = null,
|
||||
byte[]? TimestampToken = null);
|
||||
|
||||
public sealed record EvidenceBundleDetails(
|
||||
EvidenceBundle Bundle,
|
||||
EvidenceBundleSignature? Signature);
|
||||
@@ -0,0 +1,41 @@
|
||||
namespace StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
public readonly record struct TenantId(Guid Value)
|
||||
{
|
||||
public static TenantId FromGuid(Guid value)
|
||||
=> value == Guid.Empty
|
||||
? throw new ArgumentException("Tenant identifier cannot be empty.", nameof(value))
|
||||
: new TenantId(value);
|
||||
|
||||
public override string ToString() => Value.ToString("N");
|
||||
}
|
||||
|
||||
public readonly record struct EvidenceBundleId(Guid Value)
|
||||
{
|
||||
public static EvidenceBundleId FromGuid(Guid value)
|
||||
=> value == Guid.Empty
|
||||
? throw new ArgumentException("Bundle identifier cannot be empty.", nameof(value))
|
||||
: new EvidenceBundleId(value);
|
||||
|
||||
public override string ToString() => Value.ToString("N");
|
||||
}
|
||||
|
||||
public readonly record struct EvidenceArtifactId(Guid Value)
|
||||
{
|
||||
public static EvidenceArtifactId FromGuid(Guid value)
|
||||
=> value == Guid.Empty
|
||||
? throw new ArgumentException("Artifact identifier cannot be empty.", nameof(value))
|
||||
: new EvidenceArtifactId(value);
|
||||
|
||||
public override string ToString() => Value.ToString("N");
|
||||
}
|
||||
|
||||
public readonly record struct EvidenceHoldId(Guid Value)
|
||||
{
|
||||
public static EvidenceHoldId FromGuid(Guid value)
|
||||
=> value == Guid.Empty
|
||||
? throw new ArgumentException("Hold identifier cannot be empty.", nameof(value))
|
||||
: new EvidenceHoldId(value);
|
||||
|
||||
public override string ToString() => Value.ToString("N");
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using StellaOps.EvidenceLocker.Core.Builders;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
public sealed record EvidenceSnapshotRequest
|
||||
{
|
||||
public EvidenceBundleKind Kind { get; init; }
|
||||
|
||||
public string? Description { get; init; }
|
||||
|
||||
public IDictionary<string, string> Metadata { get; init; } = new Dictionary<string, string>();
|
||||
|
||||
public IList<EvidenceSnapshotMaterial> Materials { get; init; } = new List<EvidenceSnapshotMaterial>();
|
||||
}
|
||||
|
||||
public sealed record EvidenceSnapshotMaterial
|
||||
{
|
||||
public string? Section { get; init; }
|
||||
|
||||
public string? Path { get; init; }
|
||||
|
||||
public string Sha256 { get; init; } = string.Empty;
|
||||
|
||||
public long SizeBytes { get; init; }
|
||||
|
||||
public string? MediaType { get; init; }
|
||||
|
||||
public IDictionary<string, string> Attributes { get; init; } = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public sealed record EvidenceSnapshotResult(
|
||||
Guid BundleId,
|
||||
string RootHash,
|
||||
EvidenceBundleManifest Manifest,
|
||||
EvidenceBundleSignature? Signature);
|
||||
|
||||
public sealed record EvidenceHoldRequest
|
||||
{
|
||||
public Guid? BundleId { get; init; }
|
||||
|
||||
public string Reason { get; init; } = string.Empty;
|
||||
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
|
||||
public string? Notes { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Incident;
|
||||
|
||||
public interface IIncidentModeState
|
||||
{
|
||||
IncidentModeSnapshot Current { get; }
|
||||
|
||||
bool IsActive { get; }
|
||||
}
|
||||
|
||||
public sealed record IncidentModeSnapshot(
|
||||
bool IsActive,
|
||||
DateTimeOffset ChangedAt,
|
||||
int RetentionExtensionDays,
|
||||
bool CaptureRequestSnapshot);
|
||||
|
||||
public sealed record IncidentModeChange(
|
||||
bool IsActive,
|
||||
DateTimeOffset ChangedAt,
|
||||
int RetentionExtensionDays);
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.EvidenceLocker.Core.Incident;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Notifications;
|
||||
|
||||
public interface IEvidenceIncidentNotifier
|
||||
{
|
||||
Task PublishIncidentModeChangedAsync(IncidentModeChange change, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed class NullEvidenceIncidentNotifier : IEvidenceIncidentNotifier
|
||||
{
|
||||
public Task PublishIncidentModeChangedAsync(IncidentModeChange change, CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Repositories;
|
||||
|
||||
public interface IEvidenceBundleRepository
|
||||
{
|
||||
Task CreateBundleAsync(EvidenceBundle bundle, CancellationToken cancellationToken);
|
||||
|
||||
Task SetBundleAssemblyAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
EvidenceBundleStatus status,
|
||||
string rootHash,
|
||||
DateTimeOffset updatedAt,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task MarkBundleSealedAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
EvidenceBundleStatus status,
|
||||
DateTimeOffset sealedAt,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task UpsertSignatureAsync(EvidenceBundleSignature signature, CancellationToken cancellationToken);
|
||||
|
||||
Task<EvidenceBundleDetails?> GetBundleAsync(EvidenceBundleId bundleId, TenantId tenantId, CancellationToken cancellationToken);
|
||||
|
||||
Task<bool> ExistsAsync(EvidenceBundleId bundleId, TenantId tenantId, CancellationToken cancellationToken);
|
||||
|
||||
Task<EvidenceHold> CreateHoldAsync(EvidenceHold hold, CancellationToken cancellationToken);
|
||||
|
||||
Task ExtendBundleRetentionAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
DateTimeOffset? holdExpiresAt,
|
||||
DateTimeOffset processedAt,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task UpdateStorageKeyAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
string storageKey,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task UpdatePortableStorageKeyAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
string storageKey,
|
||||
DateTimeOffset generatedAt,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.EvidenceLocker.Core.Builders;
|
||||
using StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Signing;
|
||||
|
||||
public interface IEvidenceSignatureService
|
||||
{
|
||||
Task<EvidenceBundleSignature?> SignManifestAsync(
|
||||
EvidenceBundleId bundleId,
|
||||
TenantId tenantId,
|
||||
EvidenceBundleManifest manifest,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Signing;
|
||||
|
||||
public interface ITimestampAuthorityClient
|
||||
{
|
||||
Task<TimestampResult?> RequestTimestampAsync(
|
||||
ReadOnlyMemory<byte> signature,
|
||||
string hashAlgorithm,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public sealed record TimestampResult(
|
||||
DateTimeOffset Timestamp,
|
||||
string Authority,
|
||||
byte[] Token);
|
||||
@@ -1,18 +1,14 @@
|
||||
<?xml version="1.0" ?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
<?xml version="1.0"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography\StellaOps.Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using StellaOps.EvidenceLocker.Core.Domain;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Storage;
|
||||
|
||||
public sealed record EvidenceObjectMetadata(
|
||||
string StorageKey,
|
||||
string ContentType,
|
||||
long SizeBytes,
|
||||
string Sha256,
|
||||
string? ETag,
|
||||
DateTimeOffset CreatedAt);
|
||||
|
||||
public sealed record EvidenceObjectWriteOptions(
|
||||
TenantId TenantId,
|
||||
EvidenceBundleId BundleId,
|
||||
string ArtifactName,
|
||||
string ContentType,
|
||||
bool EnforceWriteOnce = true,
|
||||
IDictionary<string, string>? Tags = null);
|
||||
|
||||
public interface IEvidenceObjectStore
|
||||
{
|
||||
Task<EvidenceObjectMetadata> StoreAsync(
|
||||
Stream content,
|
||||
EvidenceObjectWriteOptions options,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<Stream> OpenReadAsync(
|
||||
string storageKey,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task<bool> ExistsAsync(
|
||||
string storageKey,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using StellaOps.EvidenceLocker.Core.Builders;
|
||||
using StellaOps.EvidenceLocker.Core.Domain;
|
||||
using StellaOps.EvidenceLocker.Core.Incident;
|
||||
|
||||
namespace StellaOps.EvidenceLocker.Core.Timeline;
|
||||
|
||||
public interface IEvidenceTimelinePublisher
|
||||
{
|
||||
Task PublishBundleSealedAsync(
|
||||
EvidenceBundleSignature signature,
|
||||
EvidenceBundleManifest manifest,
|
||||
string rootHash,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task PublishHoldCreatedAsync(
|
||||
EvidenceHold hold,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
Task PublishIncidentModeChangedAsync(
|
||||
IncidentModeChange change,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
Reference in New Issue
Block a user