Add comprehensive security tests for OWASP A03 (Injection) and A10 (SSRF)
- 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:
@@ -0,0 +1,300 @@
|
||||
using StellaOps.Attestor.Core.Verification;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Attestor.Tests;
|
||||
|
||||
public sealed class MerkleProofVerifierTests
|
||||
{
|
||||
[Fact]
|
||||
public void HashLeaf_ProducesDeterministicHash()
|
||||
{
|
||||
var data = "test data"u8.ToArray();
|
||||
|
||||
var hash1 = MerkleProofVerifier.HashLeaf(data);
|
||||
var hash2 = MerkleProofVerifier.HashLeaf(data);
|
||||
|
||||
Assert.Equal(hash1, hash2);
|
||||
Assert.Equal(32, hash1.Length); // SHA-256 produces 32 bytes
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HashLeaf_IncludesLeafPrefix()
|
||||
{
|
||||
var data = Array.Empty<byte>();
|
||||
|
||||
var hash = MerkleProofVerifier.HashLeaf(data);
|
||||
|
||||
// Hash of 0x00 prefix only should be consistent
|
||||
Assert.NotNull(hash);
|
||||
Assert.Equal(32, hash.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HashInterior_ProducesDeterministicHash()
|
||||
{
|
||||
var left = new byte[] { 1, 2, 3 };
|
||||
var right = new byte[] { 4, 5, 6 };
|
||||
|
||||
var hash1 = MerkleProofVerifier.HashInterior(left, right);
|
||||
var hash2 = MerkleProofVerifier.HashInterior(left, right);
|
||||
|
||||
Assert.Equal(hash1, hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HashInterior_OrderMatters()
|
||||
{
|
||||
var a = new byte[] { 1, 2, 3 };
|
||||
var b = new byte[] { 4, 5, 6 };
|
||||
|
||||
var hashAB = MerkleProofVerifier.HashInterior(a, b);
|
||||
var hashBA = MerkleProofVerifier.HashInterior(b, a);
|
||||
|
||||
Assert.NotEqual(hashAB, hashBA);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_SingleLeafTree_Succeeds()
|
||||
{
|
||||
var leafData = "single leaf"u8.ToArray();
|
||||
var leafHash = MerkleProofVerifier.HashLeaf(leafData);
|
||||
|
||||
// In a single-leaf tree, root = leaf hash
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leafHash,
|
||||
leafIndex: 0,
|
||||
treeSize: 1,
|
||||
proofHashes: Array.Empty<byte[]>(),
|
||||
expectedRootHash: leafHash);
|
||||
|
||||
Assert.True(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_TwoLeafTree_LeftLeaf_Succeeds()
|
||||
{
|
||||
var leaf0Data = "leaf 0"u8.ToArray();
|
||||
var leaf1Data = "leaf 1"u8.ToArray();
|
||||
|
||||
var leaf0Hash = MerkleProofVerifier.HashLeaf(leaf0Data);
|
||||
var leaf1Hash = MerkleProofVerifier.HashLeaf(leaf1Data);
|
||||
var rootHash = MerkleProofVerifier.HashInterior(leaf0Hash, leaf1Hash);
|
||||
|
||||
// Verify leaf 0 with sibling leaf 1
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leaf0Hash,
|
||||
leafIndex: 0,
|
||||
treeSize: 2,
|
||||
proofHashes: new[] { leaf1Hash },
|
||||
expectedRootHash: rootHash);
|
||||
|
||||
Assert.True(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_TwoLeafTree_RightLeaf_Succeeds()
|
||||
{
|
||||
var leaf0Data = "leaf 0"u8.ToArray();
|
||||
var leaf1Data = "leaf 1"u8.ToArray();
|
||||
|
||||
var leaf0Hash = MerkleProofVerifier.HashLeaf(leaf0Data);
|
||||
var leaf1Hash = MerkleProofVerifier.HashLeaf(leaf1Data);
|
||||
var rootHash = MerkleProofVerifier.HashInterior(leaf0Hash, leaf1Hash);
|
||||
|
||||
// Verify leaf 1 with sibling leaf 0
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leaf1Hash,
|
||||
leafIndex: 1,
|
||||
treeSize: 2,
|
||||
proofHashes: new[] { leaf0Hash },
|
||||
expectedRootHash: rootHash);
|
||||
|
||||
Assert.True(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_InvalidLeafHash_Fails()
|
||||
{
|
||||
var leaf0Data = "leaf 0"u8.ToArray();
|
||||
var leaf1Data = "leaf 1"u8.ToArray();
|
||||
var tamperedData = "tampered"u8.ToArray();
|
||||
|
||||
var leaf0Hash = MerkleProofVerifier.HashLeaf(leaf0Data);
|
||||
var leaf1Hash = MerkleProofVerifier.HashLeaf(leaf1Data);
|
||||
var tamperedHash = MerkleProofVerifier.HashLeaf(tamperedData);
|
||||
var rootHash = MerkleProofVerifier.HashInterior(leaf0Hash, leaf1Hash);
|
||||
|
||||
// Try to verify tampered leaf
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
tamperedHash,
|
||||
leafIndex: 0,
|
||||
treeSize: 2,
|
||||
proofHashes: new[] { leaf1Hash },
|
||||
expectedRootHash: rootHash);
|
||||
|
||||
Assert.False(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_WrongRootHash_Fails()
|
||||
{
|
||||
var leaf0Hash = MerkleProofVerifier.HashLeaf("leaf 0"u8.ToArray());
|
||||
var leaf1Hash = MerkleProofVerifier.HashLeaf("leaf 1"u8.ToArray());
|
||||
var wrongRoot = MerkleProofVerifier.HashLeaf("wrong"u8.ToArray());
|
||||
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leaf0Hash,
|
||||
leafIndex: 0,
|
||||
treeSize: 2,
|
||||
proofHashes: new[] { leaf1Hash },
|
||||
expectedRootHash: wrongRoot);
|
||||
|
||||
Assert.False(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_InvalidIndex_Fails()
|
||||
{
|
||||
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
|
||||
|
||||
// Index out of range
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leafHash,
|
||||
leafIndex: 10,
|
||||
treeSize: 2,
|
||||
proofHashes: Array.Empty<byte[]>(),
|
||||
expectedRootHash: leafHash);
|
||||
|
||||
Assert.False(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_NegativeIndex_Fails()
|
||||
{
|
||||
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
|
||||
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leafHash,
|
||||
leafIndex: -1,
|
||||
treeSize: 1,
|
||||
proofHashes: Array.Empty<byte[]>(),
|
||||
expectedRootHash: leafHash);
|
||||
|
||||
Assert.False(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_ZeroTreeSize_Fails()
|
||||
{
|
||||
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
|
||||
|
||||
var verified = MerkleProofVerifier.VerifyInclusion(
|
||||
leafHash,
|
||||
leafIndex: 0,
|
||||
treeSize: 0,
|
||||
proofHashes: Array.Empty<byte[]>(),
|
||||
expectedRootHash: leafHash);
|
||||
|
||||
Assert.False(verified);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HexToBytes_ConvertsCorrectly()
|
||||
{
|
||||
var hex = "0102030405";
|
||||
var expected = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
||||
var result = MerkleProofVerifier.HexToBytes(hex);
|
||||
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HexToBytes_Handles0xPrefix()
|
||||
{
|
||||
var hex = "0x0102030405";
|
||||
var expected = new byte[] { 1, 2, 3, 4, 5 };
|
||||
|
||||
var result = MerkleProofVerifier.HexToBytes(hex);
|
||||
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BytesToHex_ConvertsCorrectly()
|
||||
{
|
||||
var bytes = new byte[] { 0xAB, 0xCD, 0xEF };
|
||||
|
||||
var result = MerkleProofVerifier.BytesToHex(bytes);
|
||||
|
||||
Assert.Equal("abcdef", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeRootFromPath_WithEmptyPath_ReturnsSingleLeaf()
|
||||
{
|
||||
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
|
||||
|
||||
var root = MerkleProofVerifier.ComputeRootFromPath(
|
||||
leafHash,
|
||||
leafIndex: 0,
|
||||
treeSize: 1,
|
||||
proofHashes: Array.Empty<byte[]>());
|
||||
|
||||
Assert.NotNull(root);
|
||||
Assert.Equal(leafHash, root);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeRootFromPath_WithEmptyPath_NonSingleTree_ReturnsNull()
|
||||
{
|
||||
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
|
||||
|
||||
var root = MerkleProofVerifier.ComputeRootFromPath(
|
||||
leafHash,
|
||||
leafIndex: 0,
|
||||
treeSize: 5,
|
||||
proofHashes: Array.Empty<byte[]>());
|
||||
|
||||
Assert.Null(root);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifyInclusion_FourLeafTree_AllPositions()
|
||||
{
|
||||
// Build a 4-leaf tree manually
|
||||
var leaves = new[]
|
||||
{
|
||||
MerkleProofVerifier.HashLeaf("leaf0"u8.ToArray()),
|
||||
MerkleProofVerifier.HashLeaf("leaf1"u8.ToArray()),
|
||||
MerkleProofVerifier.HashLeaf("leaf2"u8.ToArray()),
|
||||
MerkleProofVerifier.HashLeaf("leaf3"u8.ToArray())
|
||||
};
|
||||
|
||||
// root
|
||||
// / \
|
||||
// h01 h23
|
||||
// / \ / \
|
||||
// L0 L1 L2 L3
|
||||
|
||||
var h01 = MerkleProofVerifier.HashInterior(leaves[0], leaves[1]);
|
||||
var h23 = MerkleProofVerifier.HashInterior(leaves[2], leaves[3]);
|
||||
var root = MerkleProofVerifier.HashInterior(h01, h23);
|
||||
|
||||
// Verify leaf 0: sibling = leaf1, parent sibling = h23
|
||||
Assert.True(MerkleProofVerifier.VerifyInclusion(
|
||||
leaves[0], 0, 4, new[] { leaves[1], h23 }, root));
|
||||
|
||||
// Verify leaf 1: sibling = leaf0, parent sibling = h23
|
||||
Assert.True(MerkleProofVerifier.VerifyInclusion(
|
||||
leaves[1], 1, 4, new[] { leaves[0], h23 }, root));
|
||||
|
||||
// Verify leaf 2: sibling = leaf3, parent sibling = h01
|
||||
Assert.True(MerkleProofVerifier.VerifyInclusion(
|
||||
leaves[2], 2, 4, new[] { leaves[3], h01 }, root));
|
||||
|
||||
// Verify leaf 3: sibling = leaf2, parent sibling = h01
|
||||
Assert.True(MerkleProofVerifier.VerifyInclusion(
|
||||
leaves[3], 3, 4, new[] { leaves[2], h01 }, root));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user