finish off sprint advisories and sprints

This commit is contained in:
master
2026-01-24 00:12:43 +02:00
parent 726d70dc7f
commit c70e83719e
266 changed files with 46699 additions and 1328 deletions

View File

@@ -0,0 +1,208 @@
// -----------------------------------------------------------------------------
// RuntimeObservationTests.cs
// Sprint: SPRINT_20260122_038_Scanner_ebpf_probe_type
// Task: EBPF-001 - Add ProbeType field to RuntimeObservation
// Description: Unit tests for RuntimeObservation and EbpfProbeType
// -----------------------------------------------------------------------------
using StellaOps.RuntimeInstrumentation.Tetragon;
using Xunit;
namespace StellaOps.RuntimeInstrumentation.Tetragon.Tests;
public class RuntimeObservationTests
{
[Fact]
public void RuntimeObservation_WithoutProbeType_CreatesValidInstance()
{
// Arrange & Act - backward compatibility: ProbeType is optional
var observation = new RuntimeObservation
{
ObservedAt = DateTimeOffset.UtcNow,
SourceType = RuntimeObservationSourceType.Tetragon,
ContainerId = "container123"
};
// Assert
Assert.Null(observation.ProbeType);
Assert.Null(observation.FunctionName);
Assert.Null(observation.FunctionAddress);
Assert.Equal(RuntimeObservationSourceType.Tetragon, observation.SourceType);
}
[Fact]
public void RuntimeObservation_WithProbeType_SetsFieldCorrectly()
{
// Arrange & Act
var observation = new RuntimeObservation
{
ObservedAt = DateTimeOffset.UtcNow,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = EbpfProbeType.Uprobe,
FunctionName = "SSL_connect",
FunctionAddress = 0x7f1234567890
};
// Assert
Assert.Equal(EbpfProbeType.Uprobe, observation.ProbeType);
Assert.Equal("SSL_connect", observation.FunctionName);
Assert.Equal(0x7f1234567890, observation.FunctionAddress);
}
[Theory]
[InlineData(EbpfProbeType.Kprobe)]
[InlineData(EbpfProbeType.Kretprobe)]
[InlineData(EbpfProbeType.Uprobe)]
[InlineData(EbpfProbeType.Uretprobe)]
[InlineData(EbpfProbeType.Tracepoint)]
[InlineData(EbpfProbeType.Usdt)]
[InlineData(EbpfProbeType.Fentry)]
[InlineData(EbpfProbeType.Fexit)]
public void RuntimeObservation_WithAllProbeTypes_Succeeds(EbpfProbeType probeType)
{
// Arrange & Act
var observation = new RuntimeObservation
{
ObservedAt = DateTimeOffset.UtcNow,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = probeType
};
// Assert
Assert.Equal(probeType, observation.ProbeType);
}
[Fact]
public void RuntimeObservation_RecordEquality_WorksWithNewFields()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow;
var obs1 = new RuntimeObservation
{
ObservedAt = timestamp,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = EbpfProbeType.Uprobe,
FunctionName = "SSL_connect",
FunctionAddress = 0x12345678
};
var obs2 = new RuntimeObservation
{
ObservedAt = timestamp,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = EbpfProbeType.Uprobe,
FunctionName = "SSL_connect",
FunctionAddress = 0x12345678
};
// Assert - records with same values are equal
Assert.Equal(obs1, obs2);
}
[Fact]
public void RuntimeObservation_RecordInequality_WithDifferentProbeType()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow;
var obs1 = new RuntimeObservation
{
ObservedAt = timestamp,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = EbpfProbeType.Uprobe
};
var obs2 = new RuntimeObservation
{
ObservedAt = timestamp,
SourceType = RuntimeObservationSourceType.Tetragon,
ProbeType = EbpfProbeType.Uretprobe
};
// Assert - different probe types are not equal
Assert.NotEqual(obs1, obs2);
}
[Fact]
public void RuntimeObservation_WithAllFields_CreatesCompleteInstance()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow;
var observationId = Guid.NewGuid().ToString();
// Act
var observation = new RuntimeObservation
{
ObservedAt = timestamp,
ObservationCount = 5,
StackSampleHash = "sha256:abc123",
ProcessId = 12345,
ContainerId = "container123",
PodName = "my-pod",
Namespace = "production",
SourceType = RuntimeObservationSourceType.Tetragon,
ObservationId = observationId,
ProbeType = EbpfProbeType.Uprobe,
FunctionName = "crypto_encrypt",
FunctionAddress = 0x7f1234567890
};
// Assert - all fields set correctly
Assert.Equal(timestamp, observation.ObservedAt);
Assert.Equal(5, observation.ObservationCount);
Assert.Equal("sha256:abc123", observation.StackSampleHash);
Assert.Equal(12345, observation.ProcessId);
Assert.Equal("container123", observation.ContainerId);
Assert.Equal("my-pod", observation.PodName);
Assert.Equal("production", observation.Namespace);
Assert.Equal(RuntimeObservationSourceType.Tetragon, observation.SourceType);
Assert.Equal(observationId, observation.ObservationId);
Assert.Equal(EbpfProbeType.Uprobe, observation.ProbeType);
Assert.Equal("crypto_encrypt", observation.FunctionName);
Assert.Equal(0x7f1234567890, observation.FunctionAddress);
}
}
public class EbpfProbeTypeTests
{
[Fact]
public void EbpfProbeType_HasAllExpectedValues()
{
// Assert - enum has all expected probe types
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Kprobe));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Kretprobe));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Uprobe));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Uretprobe));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Tracepoint));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Usdt));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Fentry));
Assert.True(Enum.IsDefined(typeof(EbpfProbeType), EbpfProbeType.Fexit));
}
[Fact]
public void EbpfProbeType_EnumCount_Is8()
{
// Assert - exactly 8 probe types as specified in EBPF-001
var values = Enum.GetValues<EbpfProbeType>();
Assert.Equal(8, values.Length);
}
[Theory]
[InlineData("Kprobe", EbpfProbeType.Kprobe)]
[InlineData("Kretprobe", EbpfProbeType.Kretprobe)]
[InlineData("Uprobe", EbpfProbeType.Uprobe)]
[InlineData("Uretprobe", EbpfProbeType.Uretprobe)]
[InlineData("Tracepoint", EbpfProbeType.Tracepoint)]
[InlineData("Usdt", EbpfProbeType.Usdt)]
[InlineData("Fentry", EbpfProbeType.Fentry)]
[InlineData("Fexit", EbpfProbeType.Fexit)]
public void EbpfProbeType_ParsesFromString(string name, EbpfProbeType expected)
{
// Act
var parsed = Enum.Parse<EbpfProbeType>(name);
// Assert
Assert.Equal(expected, parsed);
}
}

View File

@@ -0,0 +1,253 @@
// -----------------------------------------------------------------------------
// TetragonEventAdapterProbeTypeTests.cs
// Sprint: SPRINT_20260122_038_Scanner_ebpf_probe_type
// Task: EBPF-002 - Update Tetragon event parser to populate ProbeType
// Description: Integration tests for Tetragon event parser probe type mapping
// -----------------------------------------------------------------------------
using Moq;
using StellaOps.RuntimeInstrumentation.Tetragon;
using Xunit;
namespace StellaOps.RuntimeInstrumentation.Tetragon.Tests;
public class TetragonEventAdapterProbeTypeTests
{
private readonly Mock<ISymbolResolver> _mockSymbolResolver;
private readonly Mock<IHotSymbolIndex> _mockHotSymbolIndex;
private readonly Mock<ILogger<TetragonEventAdapter>> _mockLogger;
private readonly TetragonEventAdapter _adapter;
public TetragonEventAdapterProbeTypeTests()
{
_mockSymbolResolver = new Mock<ISymbolResolver>();
_mockHotSymbolIndex = new Mock<IHotSymbolIndex>();
_mockLogger = new Mock<ILogger<TetragonEventAdapter>>();
var options = new TestOptions<TetragonAdapterOptions>(new TetragonAdapterOptions
{
UpdateHotSymbolIndex = false // Disable for these tests
});
_adapter = new TetragonEventAdapter(
_mockSymbolResolver.Object,
_mockHotSymbolIndex.Object,
options,
_mockLogger.Object);
}
[Theory]
[InlineData(TetragonEventType.Kprobe, EbpfProbeType.Kprobe)]
[InlineData(TetragonEventType.Kretprobe, EbpfProbeType.Kretprobe)]
[InlineData(TetragonEventType.Uprobe, EbpfProbeType.Uprobe)]
[InlineData(TetragonEventType.Uretprobe, EbpfProbeType.Uretprobe)]
[InlineData(TetragonEventType.Tracepoint, EbpfProbeType.Tracepoint)]
[InlineData(TetragonEventType.Usdt, EbpfProbeType.Usdt)]
[InlineData(TetragonEventType.Fentry, EbpfProbeType.Fentry)]
[InlineData(TetragonEventType.Fexit, EbpfProbeType.Fexit)]
public async Task AdaptAsync_WithEbpfProbeType_MapsCorrectly(
TetragonEventType tetragonType,
EbpfProbeType expectedProbeType)
{
// Arrange
var tetragonEvent = CreateTestEvent(tetragonType, "test_function", 0x12345678);
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Equal(expectedProbeType, result.ProbeType);
}
[Theory]
[InlineData(TetragonEventType.ProcessExec)]
[InlineData(TetragonEventType.ProcessExit)]
public async Task AdaptAsync_WithProcessEvent_ReturnsNullProbeType(TetragonEventType tetragonType)
{
// Arrange
var tetragonEvent = CreateTestEvent(tetragonType, null, null);
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Null(result.ProbeType);
}
[Fact]
public async Task AdaptAsync_WithFunctionAddress_PopulatesFunctionAddress()
{
// Arrange
const ulong expectedAddress = 0x7f1234567890;
var tetragonEvent = CreateTestEvent(TetragonEventType.Uprobe, "SSL_connect", expectedAddress);
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Equal(expectedAddress, result.FunctionAddress);
}
[Fact]
public async Task AdaptAsync_WithoutStackTrace_HasNullFunctionAddress()
{
// Arrange
var tetragonEvent = new TetragonEvent
{
Type = TetragonEventType.Uprobe,
Time = DateTimeOffset.UtcNow,
FunctionName = "SSL_connect",
Process = new TetragonProcess { Pid = 1234 },
StackTrace = null // No stack trace
};
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Null(result.FunctionAddress);
}
[Theory]
[InlineData(TetragonEventType.Kprobe, RuntimeEventSource.Syscall)]
[InlineData(TetragonEventType.Kretprobe, RuntimeEventSource.Syscall)]
[InlineData(TetragonEventType.Fentry, RuntimeEventSource.Syscall)]
[InlineData(TetragonEventType.Fexit, RuntimeEventSource.Syscall)]
[InlineData(TetragonEventType.Uprobe, RuntimeEventSource.LibraryLoad)]
[InlineData(TetragonEventType.Uretprobe, RuntimeEventSource.LibraryLoad)]
[InlineData(TetragonEventType.Tracepoint, RuntimeEventSource.Tracepoint)]
[InlineData(TetragonEventType.Usdt, RuntimeEventSource.Tracepoint)]
public async Task AdaptAsync_MapsSourceCorrectly(
TetragonEventType tetragonType,
RuntimeEventSource expectedSource)
{
// Arrange
var tetragonEvent = CreateTestEvent(tetragonType, "test_func", 0x12345678);
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Equal(expectedSource, result.Source);
}
[Fact]
public async Task AdaptAsync_WithFullContext_PopulatesAllFields()
{
// Arrange
var timestamp = DateTimeOffset.UtcNow;
var tetragonEvent = new TetragonEvent
{
Type = TetragonEventType.Uprobe,
Time = timestamp,
FunctionName = "SSL_connect",
Process = new TetragonProcess
{
Pid = 1234,
Tid = 5678,
Binary = "/usr/lib/libssl.so",
Docker = "container123",
Pod = new TetragonPod
{
Name = "my-pod",
Namespace = "production",
Container = new TetragonContainer { Id = "container123", Name = "app" }
}
},
StackTrace = new TetragonStackTrace
{
Frames = new List<TetragonStackFrame>
{
new()
{
Address = 0x7f1234567890,
Offset = 0x100,
Module = "libssl.so",
Symbol = "SSL_connect",
Flags = StackFrameFlags.User
}
}
}
};
_mockSymbolResolver
.Setup(r => r.ResolveAsync(It.IsAny<ulong>(), It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ResolvedSymbol
{
Name = "SSL_connect",
DemangledName = "SSL_connect",
Confidence = 1.0
});
// Act
var result = await _adapter.AdaptAsync(tetragonEvent);
// Assert
Assert.NotNull(result);
Assert.Equal(EbpfProbeType.Uprobe, result.ProbeType);
Assert.Equal(0x7f1234567890ul, result.FunctionAddress);
Assert.Equal("SSL_connect", result.SyscallName);
Assert.Equal(1234, result.ProcessId);
Assert.Equal(5678, result.ThreadId);
Assert.Equal("container123", result.ContainerId);
Assert.Equal("my-pod", result.PodName);
Assert.Equal("production", result.Namespace);
}
[Fact]
public async Task AdaptAsync_NullEvent_ReturnsNull()
{
// Act
var result = await _adapter.AdaptAsync(null!);
// Assert
Assert.Null(result);
}
private static TetragonEvent CreateTestEvent(
TetragonEventType type,
string? functionName,
ulong? address)
{
return new TetragonEvent
{
Type = type,
Time = DateTimeOffset.UtcNow,
FunctionName = functionName,
Process = new TetragonProcess
{
Pid = 1234,
Tid = 5678,
Binary = "/usr/bin/test"
},
StackTrace = address.HasValue
? new TetragonStackTrace
{
Frames = new List<TetragonStackFrame>
{
new()
{
Address = address.Value,
Offset = 0x100,
Module = "test",
Symbol = functionName,
Flags = StackFrameFlags.User
}
}
}
: null
};
}
// Helper class for options
private sealed class TestOptions<T> : IOptions<T> where T : class
{
public TestOptions(T value) => Value = value;
public T Value { get; }
}
}