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,128 @@
|
||||
// =============================================================================
|
||||
// SecurityTestBase.cs
|
||||
// Base class for all security tests providing common infrastructure
|
||||
// =============================================================================
|
||||
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
namespace StellaOps.Security.Tests.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Base class for OWASP-category security tests.
|
||||
/// Provides common test infrastructure, mocking utilities, and security assertions.
|
||||
/// </summary>
|
||||
[Trait("Category", "Security")]
|
||||
public abstract class SecurityTestBase : IDisposable
|
||||
{
|
||||
protected readonly Mock<ILogger> LoggerMock;
|
||||
protected readonly CancellationToken TestCancellation;
|
||||
private readonly CancellationTokenSource _cts;
|
||||
|
||||
protected SecurityTestBase()
|
||||
{
|
||||
LoggerMock = new Mock<ILogger>();
|
||||
_cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
TestCancellation = _cts.Token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that an action throws a security-related exception.
|
||||
/// </summary>
|
||||
protected static void AssertSecurityException<TException>(Action action, string? expectedMessage = null)
|
||||
where TException : Exception
|
||||
{
|
||||
var exception = Assert.Throws<TException>(action);
|
||||
if (expectedMessage != null)
|
||||
{
|
||||
exception.Message.Should().Contain(expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that an async action throws a security-related exception.
|
||||
/// </summary>
|
||||
protected static async Task AssertSecurityExceptionAsync<TException>(Func<Task> action, string? expectedMessage = null)
|
||||
where TException : Exception
|
||||
{
|
||||
var exception = await Assert.ThrowsAsync<TException>(action);
|
||||
if (expectedMessage != null)
|
||||
{
|
||||
exception.Message.Should().Contain(expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that the logger was called with a security warning.
|
||||
/// </summary>
|
||||
protected void AssertSecurityWarningLogged(string expectedMessage)
|
||||
{
|
||||
LoggerMock.Verify(
|
||||
x => x.Log(
|
||||
LogLevel.Warning,
|
||||
It.IsAny<EventId>(),
|
||||
It.Is<It.IsAnyType>((v, t) => v.ToString()!.Contains(expectedMessage)),
|
||||
It.IsAny<Exception?>(),
|
||||
It.IsAny<Func<It.IsAnyType, Exception?, string>>()),
|
||||
Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert that no sensitive data is present in the response.
|
||||
/// </summary>
|
||||
protected static void AssertNoSensitiveDataLeakage(string content)
|
||||
{
|
||||
var sensitivePatterns = new[]
|
||||
{
|
||||
"password",
|
||||
"secret",
|
||||
"api_key",
|
||||
"apikey",
|
||||
"private_key",
|
||||
"token",
|
||||
"bearer",
|
||||
"authorization"
|
||||
};
|
||||
|
||||
foreach (var pattern in sensitivePatterns)
|
||||
{
|
||||
// Case-insensitive check for sensitive patterns in unexpected places
|
||||
content.ToLowerInvariant().Should().NotContain(pattern,
|
||||
$"Response should not contain sensitive data pattern: {pattern}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random tenant ID for isolation.
|
||||
/// </summary>
|
||||
protected static Guid GenerateTestTenantId() => Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random user ID for isolation.
|
||||
/// </summary>
|
||||
protected static Guid GenerateTestUserId() => Guid.NewGuid();
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_cts.Cancel();
|
||||
_cts.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trait for categorizing tests by OWASP category.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class OwaspCategoryAttribute : Attribute
|
||||
{
|
||||
public string Category { get; }
|
||||
public string Description { get; }
|
||||
|
||||
public OwaspCategoryAttribute(string category, string description)
|
||||
{
|
||||
Category = category;
|
||||
Description = description;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user