up the blokcing tasks
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Risk Bundle CI / risk-bundle-build (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Risk Bundle CI / risk-bundle-offline-kit (push) Has been cancelled
Risk Bundle CI / publish-checksums (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Risk Bundle CI / risk-bundle-build (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Risk Bundle CI / risk-bundle-offline-kit (push) Has been cancelled
Risk Bundle CI / publish-checksums (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
devportal-offline / build-offline (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,379 @@
|
||||
using System.Text;
|
||||
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.Runtime;
|
||||
|
||||
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for SCANNER-ANALYZERS-JAVA-21-010: Runtime ingestion via Java agent + JFR reader.
|
||||
/// </summary>
|
||||
public sealed class JavaRuntimeIngestionTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ParseAsync_ClassLoadEvent_ProducesRuntimeClassEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/MyService","class_loader":"app","source":"/app/lib/myservice.jar","initiating_class":"com/example/Main"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.Events);
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeClass, edge.EdgeType);
|
||||
Assert.Equal("com/example/Main", edge.SourceClass);
|
||||
Assert.Equal("com/example/MyService", edge.TargetClass);
|
||||
Assert.Equal(JavaRuntimeEdgeReason.ClassLoadApplication, edge.Reason);
|
||||
Assert.Equal(1.0, edge.Confidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ServiceLoaderEvent_ProducesRuntimeSpiEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.service.load","ts":"2025-12-10T10:00:01.000Z","service_interface":"com/example/spi/Service","providers":[{"provider_class":"com/example/impl/ServiceImpl","source":"/app/lib/impl.jar"}],"initiating_class":"com/example/ServiceLoader"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
Assert.Single(result.RuntimeEntrypoints);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeSpi, edge.EdgeType);
|
||||
Assert.Equal("com/example/spi/Service", edge.SourceClass);
|
||||
Assert.Equal("com/example/impl/ServiceImpl", edge.TargetClass);
|
||||
|
||||
var entrypoint = result.RuntimeEntrypoints[0];
|
||||
Assert.Equal(JavaRuntimeEntrypointType.ServiceProvider, entrypoint.EntrypointType);
|
||||
Assert.Equal("com/example/impl/ServiceImpl", entrypoint.ClassName);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_NativeLoadEvent_ProducesRuntimeNativeLoadEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.native.load","ts":"2025-12-10T10:00:02.000Z","library_name":"jni_native","resolved_path":"/usr/lib/libjni_native.so","load_method":"System.loadLibrary","initiating_class":"com/example/NativeLoader","success":true}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeNativeLoad, edge.EdgeType);
|
||||
Assert.Equal("com/example/NativeLoader", edge.SourceClass);
|
||||
Assert.Equal("jni_native", edge.TargetClass);
|
||||
Assert.Equal(JavaRuntimeEdgeReason.SystemLoadLibrary, edge.Reason);
|
||||
Assert.Equal(1.0, edge.Confidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_FailedNativeLoad_ProducesLowerConfidenceEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.native.load","ts":"2025-12-10T10:00:02.000Z","library_name":"missing_lib","load_method":"System.loadLibrary","initiating_class":"com/example/NativeLoader","success":false}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeReason.NativeLoadFailure, edge.Reason);
|
||||
Assert.Equal(0.5, edge.Confidence);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ReflectionEvent_ProducesRuntimeReflectionEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.reflection.access","ts":"2025-12-10T10:00:03.000Z","target_class":"com/example/DynamicClass","reflection_method":"Class.forName","initiating_class":"com/example/Reflector"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
Assert.Single(result.RuntimeEntrypoints);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeReflection, edge.EdgeType);
|
||||
Assert.Equal(JavaRuntimeEdgeReason.ClassForName, edge.Reason);
|
||||
Assert.Equal(0.9, edge.Confidence);
|
||||
|
||||
var entrypoint = result.RuntimeEntrypoints[0];
|
||||
Assert.Equal(JavaRuntimeEntrypointType.ReflectionTarget, entrypoint.EntrypointType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ResourceAccessEvent_ProducesEdgeOnlyWhenFound()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.resource.access","ts":"2025-12-10T10:00:04.000Z","resource_name":"config.properties","source":"/app/conf.jar","initiating_class":"com/example/ConfigLoader","found":true}
|
||||
{"type":"java.resource.access","ts":"2025-12-10T10:00:05.000Z","resource_name":"missing.properties","initiating_class":"com/example/ConfigLoader","found":false}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Equal(2, result.Events.Length);
|
||||
Assert.Single(result.RuntimeEdges); // Only found=true produces edge
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeResource, edge.EdgeType);
|
||||
Assert.Equal("config.properties", edge.TargetClass);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ModuleResolveEvent_ProducesRuntimeModuleEdge()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.module.resolve","ts":"2025-12-10T10:00:06.000Z","module_name":"com.example.api","module_location":"file:///app/lib/api.jar","required_by":"com.example.app","is_open":false}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
|
||||
var edge = result.RuntimeEdges[0];
|
||||
Assert.Equal(JavaRuntimeEdgeType.RuntimeModule, edge.EdgeType);
|
||||
Assert.Equal("com.example.app", edge.SourceClass);
|
||||
Assert.Equal("com.example.api", edge.TargetClass);
|
||||
Assert.Equal(JavaRuntimeEdgeReason.ModuleRequires, edge.Reason);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_DeduplicatesEdgesByDefault()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/MyService","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/MyService","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:02.000Z","class_name":"com/example/MyService","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Equal(3, result.Events.Length);
|
||||
Assert.Single(result.RuntimeEdges); // Deduplicated
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_WithDeduplicationDisabled_ProducesAllEdges()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/MyService","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/MyService","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var config = new JavaRuntimeIngestionConfig(DeduplicateEdges: false);
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream, config);
|
||||
|
||||
Assert.Equal(2, result.RuntimeEdges.Length); // Not deduplicated
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_FiltersJdkClassesByDefault()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"java/lang/String","class_loader":"bootstrap","initiating_class":"com/example/Main"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/MyClass","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.Events);
|
||||
Assert.Single(result.RuntimeEdges);
|
||||
Assert.Equal("com/example/MyClass", result.RuntimeEdges[0].TargetClass);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_IncludesJdkClassesWhenConfigured()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"java/lang/String","class_loader":"bootstrap","initiating_class":"com/example/Main"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/MyClass","class_loader":"app","initiating_class":"com/example/Main"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var config = new JavaRuntimeIngestionConfig(IncludeJdkClasses: true);
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream, config);
|
||||
|
||||
Assert.Equal(2, result.Events.Length);
|
||||
Assert.Equal(2, result.RuntimeEdges.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_RespectsMaxEventsLimit()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/Class1","class_loader":"app"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/Class2","class_loader":"app"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:02.000Z","class_name":"com/example/Class3","class_loader":"app"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:03.000Z","class_name":"com/example/Class4","class_loader":"app"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:04.000Z","class_name":"com/example/Class5","class_loader":"app"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var config = new JavaRuntimeIngestionConfig(MaxEvents: 3);
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream, config);
|
||||
|
||||
Assert.Equal(3, result.Events.Length);
|
||||
Assert.Single(result.Warnings);
|
||||
Assert.Equal("MAX_EVENTS_REACHED", result.Warnings[0].WarningCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ComputesContentHash()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/MyClass","class_loader":"app"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.NotEmpty(result.ContentHash);
|
||||
Assert.Equal(64, result.ContentHash.Length); // SHA-256 hex is 64 chars
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_SamContentProducesSameHash()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/MyClass","class_loader":"app"}
|
||||
""";
|
||||
|
||||
using var stream1 = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
using var stream2 = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
|
||||
var result1 = await JavaRuntimeIngestor.IngestAsync(stream1);
|
||||
var result2 = await JavaRuntimeIngestor.IngestAsync(stream2);
|
||||
|
||||
Assert.Equal(result1.ContentHash, result2.ContentHash);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_PopulatesSummaryStatistics()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/Class1","class_loader":"app"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:01.000Z","class_name":"com/example/Class2","class_loader":"app"}
|
||||
{"type":"java.service.load","ts":"2025-12-10T10:00:02.000Z","service_interface":"spi/Service","providers":[]}
|
||||
{"type":"java.native.load","ts":"2025-12-10T10:00:03.000Z","library_name":"native","load_method":"System.loadLibrary","success":true}
|
||||
{"type":"java.reflection.access","ts":"2025-12-10T10:00:04.000Z","target_class":"com/example/Dynamic","reflection_method":"Class.forName"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Equal(2, result.Summary.ClassLoadCount);
|
||||
Assert.Equal(1, result.Summary.ServiceLoaderCount);
|
||||
Assert.Equal(1, result.Summary.NativeLoadCount);
|
||||
Assert.Equal(1, result.Summary.ReflectionCount);
|
||||
Assert.Equal(DateTimeOffset.Parse("2025-12-10T10:00:00.000Z"), result.Summary.StartTime);
|
||||
Assert.Equal(DateTimeOffset.Parse("2025-12-10T10:00:04.000Z"), result.Summary.EndTime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_HandlesInvalidJson_ProducesWarning()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/Class1","class_loader":"app"}
|
||||
{invalid json}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:02.000Z","class_name":"com/example/Class2","class_loader":"app"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Equal(2, result.Events.Length); // Valid events parsed
|
||||
Assert.Single(result.Warnings);
|
||||
Assert.Equal("PARSE_ERROR", result.Warnings[0].WarningCode);
|
||||
Assert.Equal(2, result.Warnings[0].Line);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_SkipsUnknownEventTypes()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/Class1","class_loader":"app"}
|
||||
{"type":"java.unknown.event","ts":"2025-12-10T10:00:01.000Z","data":"something"}
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:02.000Z","class_name":"com/example/Class2","class_loader":"app"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Equal(2, result.Events.Length); // Only known event types
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_HandlesEmptyStream()
|
||||
{
|
||||
using var stream = new MemoryStream(Array.Empty<byte>());
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Empty(result.Events);
|
||||
Assert.Empty(result.RuntimeEdges);
|
||||
Assert.Empty(result.Warnings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_ComputesPathHash_WhenScrubPathsEnabled()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.class.load","ts":"2025-12-10T10:00:00.000Z","class_name":"com/example/Class1","class_loader":"app","source":"/app/lib/my.jar"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var config = new JavaRuntimeIngestionConfig(ScrubPaths: true);
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream, config);
|
||||
|
||||
var evt = result.Events[0] as JavaClassLoadEvent;
|
||||
Assert.NotNull(evt);
|
||||
Assert.NotNull(evt.SourceHash);
|
||||
Assert.Equal(64, evt.SourceHash.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputePathHash_NormalizesPathSeparators()
|
||||
{
|
||||
var windowsPath = "C:\\app\\lib\\my.jar";
|
||||
var unixPath = "C:/app/lib/my.jar";
|
||||
|
||||
var hash1 = JavaRuntimeEventParser.ComputePathHash(windowsPath);
|
||||
var hash2 = JavaRuntimeEventParser.ComputePathHash(unixPath);
|
||||
|
||||
Assert.Equal(hash1, hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParseAsync_TracksEntrypointInvocationCounts()
|
||||
{
|
||||
var ndjson = """
|
||||
{"type":"java.reflection.access","ts":"2025-12-10T10:00:00.000Z","target_class":"com/example/Dynamic","reflection_method":"Class.forName","initiating_class":"com/example/A"}
|
||||
{"type":"java.reflection.access","ts":"2025-12-10T10:00:01.000Z","target_class":"com/example/Dynamic","reflection_method":"Class.forName","initiating_class":"com/example/B"}
|
||||
{"type":"java.reflection.access","ts":"2025-12-10T10:00:02.000Z","target_class":"com/example/Dynamic","reflection_method":"Class.forName","initiating_class":"com/example/C"}
|
||||
""";
|
||||
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
|
||||
var result = await JavaRuntimeIngestor.IngestAsync(stream);
|
||||
|
||||
Assert.Single(result.RuntimeEntrypoints);
|
||||
var entrypoint = result.RuntimeEntrypoints[0];
|
||||
Assert.Equal("com/example/Dynamic", entrypoint.ClassName);
|
||||
Assert.Equal(3, entrypoint.InvocationCount);
|
||||
Assert.Equal(DateTimeOffset.Parse("2025-12-10T10:00:00.000Z"), entrypoint.FirstSeen);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user