feat: Add VEX compact fixture and implement offline verifier for Findings Ledger exports
- Introduced a new VEX compact fixture for testing purposes. - Implemented `verify_export.py` script to validate Findings Ledger exports, ensuring deterministic ordering and applying redaction manifests. - Added a lightweight stub `HarnessRunner` for unit tests to validate ledger hashing expectations. - Documented tasks related to the Mirror Creator. - Created models for entropy signals and implemented the `EntropyPenaltyCalculator` to compute penalties based on scanner outputs. - Developed unit tests for `EntropyPenaltyCalculator` to ensure correct penalty calculations and handling of edge cases. - Added tests for symbol ID normalization in the reachability scanner. - Enhanced console status service with comprehensive unit tests for connection handling and error recovery. - Included Cosign tool version 2.6.0 with checksums for various platforms.
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using StellaOps.Policy.Engine.Options;
|
||||
using StellaOps.Policy.Engine.Signals.Entropy;
|
||||
using Xunit;
|
||||
using OptionsFactory = Microsoft.Extensions.Options.Options;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests.Signals;
|
||||
|
||||
public sealed class EntropyPenaltyCalculatorTests
|
||||
{
|
||||
private readonly EntropyPenaltyCalculator _calculator = new(
|
||||
OptionsFactory.Create(new PolicyEngineOptions()),
|
||||
NullLogger<EntropyPenaltyCalculator>.Instance);
|
||||
|
||||
[Fact]
|
||||
public void ComputeFromJson_ComputesPenaltyAndBlock_WhenImageOpaqueHighAndProvenanceUnknown()
|
||||
{
|
||||
var summaryJson = """
|
||||
{
|
||||
"schema": "stellaops.entropy/layer-summary@1",
|
||||
"imageOpaqueRatio": 0.18,
|
||||
"layers": [
|
||||
{ "digest": "sha256:l1", "opaqueBytes": 2306867, "totalBytes": 10485760, "opaqueRatio": 0.22, "indicators": ["packed", "no-symbols"] },
|
||||
{ "digest": "sha256:l2", "opaqueBytes": 0, "totalBytes": 1048576, "opaqueRatio": 0.0, "indicators": ["symbols"] }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
var reportJson = """
|
||||
{
|
||||
"schema": "stellaops.entropy/report@1",
|
||||
"files": [
|
||||
{ "path": "/opt/app/libblob.so", "size": 5242880, "opaqueBytes": 1342177, "opaqueRatio": 0.25, "flags": ["stripped", "section:.UPX0"], "windows": [ { "offset": 0, "length": 4096, "entropy": 7.45 } ] },
|
||||
{ "path": "/opt/app/ok.bin", "size": 1024, "opaqueBytes": 0, "opaqueRatio": 0.0, "flags": [] }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
var result = _calculator.ComputeFromJson(summaryJson, reportJson, provenanceAttested: false);
|
||||
|
||||
Assert.True(result.Blocked);
|
||||
Assert.False(result.Warned);
|
||||
Assert.InRange(result.Penalty, 0.099m, 0.101m); // ~0.1 after K=0.5
|
||||
Assert.Contains("image_opaque_ratio_exceeds_threshold", result.ReasonCodes);
|
||||
Assert.Contains(result.TopFiles, tf => tf.Path == "/opt/app/libblob.so" && tf.OpaqueRatio == 0.25m);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_AppliesMitigationAndCap_WhenSymbolsPresentAndProvenanceAttested()
|
||||
{
|
||||
var summary = new EntropyLayerSummary
|
||||
{
|
||||
ImageOpaqueRatio = 0.9m,
|
||||
Layers = new List<EntropyLayer>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Digest = "sha256:layer",
|
||||
OpaqueBytes = 900,
|
||||
TotalBytes = 1000,
|
||||
Indicators = new List<string> { "symbols" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var report = new EntropyReport
|
||||
{
|
||||
Files = new List<EntropyFile>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Path = "/bin/high.bin",
|
||||
Size = 1000,
|
||||
OpaqueBytes = 900,
|
||||
OpaqueRatio = 0.9m,
|
||||
Flags = new List<string> { "packed" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = _calculator.Compute(summary, report, provenanceAttested: true);
|
||||
|
||||
Assert.False(result.Blocked); // provenance attested suppresses block
|
||||
Assert.False(result.Capped);
|
||||
Assert.InRange(result.Penalty, 0.224m, 0.226m); // mitigation reduces below cap and stays under cap
|
||||
Assert.Contains("symbols_mitigated", result.ReasonCodes);
|
||||
Assert.DoesNotContain("penalty_capped", result.ReasonCodes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Compute_WarnsWhenLayerExceedsThresholdWithoutReport()
|
||||
{
|
||||
var summary = new EntropyLayerSummary
|
||||
{
|
||||
ImageOpaqueRatio = 0.05m,
|
||||
Layers = new List<EntropyLayer>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Digest = "sha256:l1",
|
||||
OpaqueBytes = 40,
|
||||
TotalBytes = 100,
|
||||
Indicators = new List<string> { "packed" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var result = _calculator.Compute(summary, report: null, provenanceAttested: false);
|
||||
|
||||
Assert.False(result.Blocked);
|
||||
Assert.True(result.Warned);
|
||||
Assert.Contains("file_opaque_ratio_exceeds_threshold", result.ReasonCodes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user