Add unit tests for PhpFrameworkSurface and PhpPharScanner
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled

- Implement comprehensive tests for PhpFrameworkSurface, covering scenarios such as empty surfaces, presence of routes, controllers, middlewares, CLI commands, cron jobs, and event listeners.
- Validate metadata creation for route counts, HTTP methods, protected and public routes, and route patterns.
- Introduce tests for PhpPharScanner, including handling of non-existent files, null or empty paths, invalid PHAR files, and minimal PHAR structures.
- Ensure correct computation of SHA256 for valid PHAR files and validate the properties of PhpPharArchive, PhpPharEntry, and PhpPharScanResult.
This commit is contained in:
StellaOps Bot
2025-12-07 13:44:13 +02:00
parent af30fc322f
commit 965cbf9574
49 changed files with 11935 additions and 152 deletions

View File

@@ -18,6 +18,7 @@ public static class LedgerEventConstants
public const string EventEvidenceSnapshotLinked = "airgap.evidence_snapshot_linked";
public const string EventAirgapTimelineImpact = "airgap.timeline_impact";
public const string EventOrchestratorExportRecorded = "orchestrator.export_recorded";
public const string EventAttestationPointerLinked = "attestation.pointer_linked";
public static readonly ImmutableHashSet<string> SupportedEventTypes = ImmutableHashSet.Create(StringComparer.Ordinal,
EventFindingCreated,
@@ -33,7 +34,8 @@ public static class LedgerEventConstants
EventAirgapBundleImported,
EventEvidenceSnapshotLinked,
EventAirgapTimelineImpact,
EventOrchestratorExportRecorded);
EventOrchestratorExportRecorded,
EventAttestationPointerLinked);
public static readonly ImmutableHashSet<string> FindingEventTypes = ImmutableHashSet.Create(StringComparer.Ordinal,
EventFindingCreated,

View File

@@ -0,0 +1,281 @@
namespace StellaOps.Findings.Ledger.Domain;
/// <summary>
/// Represents a point-in-time snapshot of ledger state.
/// </summary>
public sealed record LedgerSnapshot(
string TenantId,
Guid SnapshotId,
string? Label,
string? Description,
SnapshotStatus Status,
DateTimeOffset CreatedAt,
DateTimeOffset? ExpiresAt,
long SequenceNumber,
DateTimeOffset Timestamp,
SnapshotStatistics Statistics,
string? MerkleRoot,
string? DsseDigest,
Dictionary<string, object>? Metadata = null);
/// <summary>
/// Snapshot lifecycle status.
/// </summary>
public enum SnapshotStatus
{
Creating,
Available,
Exporting,
Expired,
Deleted
}
/// <summary>
/// Statistics for a snapshot.
/// </summary>
public sealed record SnapshotStatistics(
long FindingsCount,
long VexStatementsCount,
long AdvisoriesCount,
long SbomsCount,
long EventsCount,
long SizeBytes);
/// <summary>
/// Input for creating a snapshot.
/// </summary>
public sealed record CreateSnapshotInput(
string TenantId,
string? Label = null,
string? Description = null,
DateTimeOffset? AtTimestamp = null,
long? AtSequence = null,
TimeSpan? ExpiresIn = null,
IReadOnlyList<EntityType>? IncludeEntityTypes = null,
bool Sign = false,
Dictionary<string, object>? Metadata = null);
/// <summary>
/// Result of creating a snapshot.
/// </summary>
public sealed record CreateSnapshotResult(
bool Success,
LedgerSnapshot? Snapshot,
string? Error);
/// <summary>
/// Entity types tracked in the ledger.
/// </summary>
public enum EntityType
{
Finding,
Vex,
Advisory,
Sbom,
Evidence
}
/// <summary>
/// Query point specification (timestamp or sequence).
/// </summary>
public sealed record QueryPoint(
DateTimeOffset Timestamp,
long SequenceNumber,
Guid? SnapshotId = null);
/// <summary>
/// Filters for time-travel queries.
/// </summary>
public sealed record TimeQueryFilters(
string? Status = null,
decimal? SeverityMin = null,
decimal? SeverityMax = null,
string? PolicyVersion = null,
string? ArtifactId = null,
string? VulnId = null,
Dictionary<string, string>? Labels = null);
/// <summary>
/// Request for historical query.
/// </summary>
public sealed record HistoricalQueryRequest(
string TenantId,
DateTimeOffset? AtTimestamp,
long? AtSequence,
Guid? SnapshotId,
EntityType EntityType,
TimeQueryFilters? Filters,
int PageSize = 500,
string? PageToken = null);
/// <summary>
/// Response for historical query.
/// </summary>
public sealed record HistoricalQueryResponse<T>(
QueryPoint QueryPoint,
EntityType EntityType,
IReadOnlyList<T> Items,
string? NextPageToken,
long TotalCount);
/// <summary>
/// Request for replaying events.
/// </summary>
public sealed record ReplayRequest(
string TenantId,
long? FromSequence = null,
long? ToSequence = null,
DateTimeOffset? FromTimestamp = null,
DateTimeOffset? ToTimestamp = null,
IReadOnlyList<Guid>? ChainIds = null,
IReadOnlyList<string>? EventTypes = null,
bool IncludePayload = true,
int PageSize = 1000);
/// <summary>
/// Replayed event record.
/// </summary>
public sealed record ReplayEvent(
Guid EventId,
long SequenceNumber,
Guid ChainId,
int ChainSequence,
string EventType,
DateTimeOffset OccurredAt,
DateTimeOffset RecordedAt,
string? ActorId,
string? ActorType,
string? ArtifactId,
string? FindingId,
string? PolicyVersion,
string EventHash,
string PreviousHash,
object? Payload);
/// <summary>
/// Replay metadata.
/// </summary>
public sealed record ReplayMetadata(
long FromSequence,
long ToSequence,
long EventsCount,
bool HasMore,
long ReplayDurationMs);
/// <summary>
/// Request for computing diff between two points.
/// </summary>
public sealed record DiffRequest(
string TenantId,
DiffPoint From,
DiffPoint To,
IReadOnlyList<EntityType>? EntityTypes = null,
bool IncludeUnchanged = false,
DiffOutputFormat OutputFormat = DiffOutputFormat.Summary);
/// <summary>
/// Diff point specification.
/// </summary>
public sealed record DiffPoint(
DateTimeOffset? Timestamp = null,
long? SequenceNumber = null,
Guid? SnapshotId = null);
/// <summary>
/// Diff output format.
/// </summary>
public enum DiffOutputFormat
{
Summary,
Detailed,
Full
}
/// <summary>
/// Diff summary counts.
/// </summary>
public sealed record DiffSummary(
int Added,
int Modified,
int Removed,
int Unchanged,
Dictionary<EntityType, DiffCounts>? ByEntityType = null);
/// <summary>
/// Diff counts per entity type.
/// </summary>
public sealed record DiffCounts(int Added, int Modified, int Removed);
/// <summary>
/// Individual diff entry.
/// </summary>
public sealed record DiffEntry(
EntityType EntityType,
string EntityId,
DiffChangeType ChangeType,
object? FromState,
object? ToState,
IReadOnlyList<string>? ChangedFields);
/// <summary>
/// Type of change in a diff.
/// </summary>
public enum DiffChangeType
{
Added,
Modified,
Removed
}
/// <summary>
/// Diff response.
/// </summary>
public sealed record DiffResponse(
QueryPoint FromPoint,
QueryPoint ToPoint,
DiffSummary Summary,
IReadOnlyList<DiffEntry>? Changes,
string? NextPageToken);
/// <summary>
/// Changelog entry.
/// </summary>
public sealed record ChangeLogEntry(
long SequenceNumber,
DateTimeOffset Timestamp,
EntityType EntityType,
string EntityId,
string EventType,
string? EventHash,
string? ActorId,
string? Summary);
/// <summary>
/// Staleness check result.
/// </summary>
public sealed record StalenessResult(
bool IsStale,
DateTimeOffset CheckedAt,
DateTimeOffset? LastEventAt,
TimeSpan StalenessThreshold,
TimeSpan? StalenessDuration,
Dictionary<EntityType, EntityStaleness>? ByEntityType = null);
/// <summary>
/// Staleness per entity type.
/// </summary>
public sealed record EntityStaleness(
bool IsStale,
DateTimeOffset? LastEventAt,
long EventsBehind);
/// <summary>
/// Query parameters for listing snapshots.
/// </summary>
public sealed record SnapshotListQuery(
string TenantId,
SnapshotStatus? Status = null,
DateTimeOffset? CreatedAfter = null,
DateTimeOffset? CreatedBefore = null,
int PageSize = 100,
string? PageToken = null);