Add comprehensive security tests for OWASP A03 (Injection) and A10 (SSRF)
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled

- Implemented InjectionTests.cs to cover various injection vulnerabilities including SQL, NoSQL, Command, LDAP, and XPath injections.
- Created SsrfTests.cs to test for Server-Side Request Forgery (SSRF) vulnerabilities, including internal URL access, cloud metadata access, and URL allowlist bypass attempts.
- Introduced MaliciousPayloads.cs to store a collection of malicious payloads for testing various security vulnerabilities.
- Added SecurityAssertions.cs for common security-specific assertion helpers.
- Established SecurityTestBase.cs as a base class for security tests, providing common infrastructure and mocking utilities.
- Configured the test project StellaOps.Security.Tests.csproj with necessary dependencies for testing.
This commit is contained in:
master
2025-12-16 13:11:57 +02:00
parent 5a480a3c2a
commit b55d9fa68d
72 changed files with 8051 additions and 71 deletions

View File

@@ -0,0 +1,219 @@
using StellaOps.Scanner.Worker.Determinism;
using StellaOps.Scanner.Worker.Determinism.Calculators;
using Xunit;
namespace StellaOps.Scanner.Worker.Tests.Determinism;
public sealed class FidelityMetricsServiceTests
{
private readonly FidelityMetricsService _service = new();
[Fact]
public void Calculate_WithAllIdentical_ReturnsFullScores()
{
var baselineHashes = new Dictionary<string, string>
{
["sbom.json"] = "sha256:abc",
["findings.ndjson"] = "sha256:def"
};
var replayHashes = new List<IReadOnlyDictionary<string, string>>
{
new Dictionary<string, string>
{
["sbom.json"] = "sha256:abc",
["findings.ndjson"] = "sha256:def"
}
};
var baselineFindings = CreateNormalizedFindings();
var replayFindings = new List<NormalizedFindings> { CreateNormalizedFindings() };
var baselineDecision = CreatePolicyDecision();
var replayDecisions = new List<PolicyDecision> { CreatePolicyDecision() };
var metrics = _service.Calculate(
baselineHashes, replayHashes,
baselineFindings, replayFindings,
baselineDecision, replayDecisions);
Assert.Equal(1.0, metrics.BitwiseFidelity);
Assert.Equal(1.0, metrics.SemanticFidelity);
Assert.Equal(1.0, metrics.PolicyFidelity);
Assert.Equal(1, metrics.TotalReplays);
Assert.Equal(1, metrics.IdenticalOutputs);
Assert.Equal(1, metrics.SemanticMatches);
Assert.Equal(1, metrics.PolicyMatches);
Assert.Null(metrics.Mismatches);
}
[Fact]
public void Calculate_WithMixedResults_ReturnsCorrectMetrics()
{
var baselineHashes = new Dictionary<string, string> { ["file.json"] = "hash1" };
var replayHashes = new List<IReadOnlyDictionary<string, string>>
{
new Dictionary<string, string> { ["file.json"] = "hash1" }, // Match
new Dictionary<string, string> { ["file.json"] = "hash2" }, // Mismatch
new Dictionary<string, string> { ["file.json"] = "hash1" } // Match
};
var baselineFindings = CreateNormalizedFindings();
var replayFindings = new List<NormalizedFindings>
{
CreateNormalizedFindings(),
CreateNormalizedFindings(),
CreateNormalizedFindings()
};
var baselineDecision = CreatePolicyDecision();
var replayDecisions = new List<PolicyDecision>
{
CreatePolicyDecision(),
CreatePolicyDecision(),
CreatePolicyDecision()
};
var metrics = _service.Calculate(
baselineHashes, replayHashes,
baselineFindings, replayFindings,
baselineDecision, replayDecisions);
Assert.Equal(2.0 / 3, metrics.BitwiseFidelity, precision: 4);
Assert.Equal(1.0, metrics.SemanticFidelity);
Assert.Equal(1.0, metrics.PolicyFidelity);
Assert.NotNull(metrics.Mismatches);
Assert.Single(metrics.Mismatches!);
}
[Fact]
public void Evaluate_WithPassingMetrics_ReturnsPass()
{
var metrics = new FidelityMetrics
{
BitwiseFidelity = 0.99,
SemanticFidelity = 1.0,
PolicyFidelity = 1.0,
TotalReplays = 10,
IdenticalOutputs = 10,
SemanticMatches = 10,
PolicyMatches = 10,
ComputedAt = DateTimeOffset.UtcNow
};
var thresholds = FidelityThresholds.Default;
var evaluation = _service.Evaluate(metrics, thresholds);
Assert.True(evaluation.Passed);
Assert.False(evaluation.ShouldBlockRelease);
Assert.Empty(evaluation.FailureReasons);
}
[Fact]
public void Evaluate_WithFailingBitwiseFidelity_ReturnsFail()
{
var metrics = new FidelityMetrics
{
BitwiseFidelity = 0.90, // Below 0.98 threshold
SemanticFidelity = 1.0,
PolicyFidelity = 1.0,
TotalReplays = 10,
IdenticalOutputs = 9,
SemanticMatches = 10,
PolicyMatches = 10,
ComputedAt = DateTimeOffset.UtcNow
};
var thresholds = FidelityThresholds.Default;
var evaluation = _service.Evaluate(metrics, thresholds);
Assert.False(evaluation.Passed);
Assert.Single(evaluation.FailureReasons);
Assert.Contains("BF", evaluation.FailureReasons[0]);
}
[Fact]
public void Evaluate_WithCriticallyLowBF_ShouldBlockRelease()
{
var metrics = new FidelityMetrics
{
BitwiseFidelity = 0.85, // Below 0.90 block threshold
SemanticFidelity = 1.0,
PolicyFidelity = 1.0,
TotalReplays = 10,
IdenticalOutputs = 8,
SemanticMatches = 10,
PolicyMatches = 10,
ComputedAt = DateTimeOffset.UtcNow
};
var thresholds = FidelityThresholds.Default;
var evaluation = _service.Evaluate(metrics, thresholds);
Assert.False(evaluation.Passed);
Assert.True(evaluation.ShouldBlockRelease);
}
[Fact]
public void Evaluate_WithRegulatedProject_UsesLowerThreshold()
{
var metrics = new FidelityMetrics
{
BitwiseFidelity = 0.96, // Above 0.95 regulated, below 0.98 general
SemanticFidelity = 1.0,
PolicyFidelity = 1.0,
TotalReplays = 10,
IdenticalOutputs = 9,
SemanticMatches = 10,
PolicyMatches = 10,
ComputedAt = DateTimeOffset.UtcNow
};
var thresholds = FidelityThresholds.Default;
var generalEval = _service.Evaluate(metrics, thresholds, isRegulated: false);
var regulatedEval = _service.Evaluate(metrics, thresholds, isRegulated: true);
Assert.False(generalEval.Passed); // Fails 0.98 threshold
Assert.True(regulatedEval.Passed); // Passes 0.95 threshold
}
[Fact]
public void Evaluate_WithMultipleFailures_ReportsAll()
{
var metrics = new FidelityMetrics
{
BitwiseFidelity = 0.90,
SemanticFidelity = 0.80,
PolicyFidelity = 0.70,
TotalReplays = 10,
IdenticalOutputs = 9,
SemanticMatches = 8,
PolicyMatches = 7,
ComputedAt = DateTimeOffset.UtcNow
};
var thresholds = FidelityThresholds.Default;
var evaluation = _service.Evaluate(metrics, thresholds);
Assert.False(evaluation.Passed);
Assert.Equal(3, evaluation.FailureReasons.Count);
}
private static NormalizedFindings CreateNormalizedFindings() => new()
{
Packages = new List<NormalizedPackage>
{
new("pkg:npm/test@1.0.0", "1.0.0")
},
Cves = new HashSet<string> { "CVE-2024-0001" },
SeverityCounts = new Dictionary<string, int> { ["MEDIUM"] = 1 },
Verdicts = new Dictionary<string, string> { ["overall"] = "pass" }
};
private static PolicyDecision CreatePolicyDecision() => new()
{
Passed = true,
ReasonCodes = new List<string> { "CLEAN" },
ViolationCount = 0,
BlockLevel = "none"
};
}

View File

@@ -0,0 +1,213 @@
using StellaOps.Scanner.Worker.Determinism;
using StellaOps.Scanner.Worker.Determinism.Calculators;
using Xunit;
namespace StellaOps.Scanner.Worker.Tests.Determinism;
public sealed class PolicyFidelityCalculatorTests
{
private readonly PolicyFidelityCalculator _calculator = new();
[Fact]
public void Calculate_WithEmptyReplays_ReturnsFullScore()
{
var baseline = CreatePassingDecision();
var replays = Array.Empty<PolicyDecision>();
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(1.0, score);
Assert.Equal(0, matchCount);
Assert.Empty(mismatches);
}
[Fact]
public void Calculate_WithIdenticalDecisions_ReturnsFullScore()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
CreatePassingDecision(),
CreatePassingDecision()
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(1.0, score);
Assert.Equal(2, matchCount);
Assert.Empty(mismatches);
}
[Fact]
public void Calculate_WithDifferentOutcome_DetectsMismatch()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = false, // Different outcome
ReasonCodes = new List<string> { "NO_VIOLATIONS" },
ViolationCount = 0,
BlockLevel = "none"
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(0.0, score);
Assert.Equal(0, matchCount);
Assert.Single(mismatches);
Assert.Equal(FidelityMismatchType.PolicyDrift, mismatches[0].Type);
Assert.Contains("outcome:True→False", mismatches[0].AffectedArtifacts!);
}
[Fact]
public void Calculate_WithDifferentReasonCodes_DetectsMismatch()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = true,
ReasonCodes = new List<string> { "DIFFERENT_REASON" }, // Different reason
ViolationCount = 0,
BlockLevel = "none"
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(0.0, score);
Assert.Contains("reason_codes", mismatches[0].AffectedArtifacts!);
}
[Fact]
public void Calculate_WithDifferentViolationCount_DetectsMismatch()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = true,
ReasonCodes = new List<string> { "NO_VIOLATIONS" },
ViolationCount = 5, // Different count
BlockLevel = "none"
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(0.0, score);
Assert.Contains("violations:0→5", mismatches[0].AffectedArtifacts!);
}
[Fact]
public void Calculate_WithDifferentBlockLevel_DetectsMismatch()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = true,
ReasonCodes = new List<string> { "NO_VIOLATIONS" },
ViolationCount = 0,
BlockLevel = "warn" // Different block level
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(0.0, score);
Assert.Contains("block_level:none→warn", mismatches[0].AffectedArtifacts!);
}
[Fact]
public void Calculate_WithMultipleDifferences_ReportsAll()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = false, // Different
ReasonCodes = new List<string> { "CRITICAL_VULN" }, // Different
ViolationCount = 3, // Different
BlockLevel = "block" // Different
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(0.0, score);
Assert.Single(mismatches);
var mismatch = mismatches[0];
Assert.Equal(4, mismatch.AffectedArtifacts!.Count); // All 4 differences detected
}
[Fact]
public void Calculate_WithPartialMatches_ReturnsCorrectScore()
{
var baseline = CreatePassingDecision();
var replays = new List<PolicyDecision>
{
CreatePassingDecision(), // Match
new PolicyDecision // Mismatch
{
Passed = false,
ReasonCodes = new List<string>(),
ViolationCount = 1,
BlockLevel = "block"
},
CreatePassingDecision(), // Match
CreatePassingDecision() // Match
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(3.0 / 4, score, precision: 4);
Assert.Equal(3, matchCount);
Assert.Single(mismatches);
Assert.Equal(1, mismatches[0].RunIndex);
}
[Fact]
public void Calculate_WithReasonCodesInDifferentOrder_StillMatches()
{
var baseline = new PolicyDecision
{
Passed = true,
ReasonCodes = new List<string> { "CODE_A", "CODE_B", "CODE_C" },
ViolationCount = 0,
BlockLevel = "none"
};
var replays = new List<PolicyDecision>
{
new PolicyDecision
{
Passed = true,
ReasonCodes = new List<string> { "CODE_C", "CODE_A", "CODE_B" }, // Different order
ViolationCount = 0,
BlockLevel = "none"
}
};
var (score, matchCount, mismatches) = _calculator.Calculate(baseline, replays);
Assert.Equal(1.0, score);
Assert.Equal(1, matchCount);
Assert.Empty(mismatches);
}
private static PolicyDecision CreatePassingDecision() => new()
{
Passed = true,
ReasonCodes = new List<string> { "NO_VIOLATIONS" },
ViolationCount = 0,
BlockLevel = "none",
PolicyHash = "sha256:abc123"
};
}