feat: Implement Runtime Facts ingestion service and NDJSON reader
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Added RuntimeFactsNdjsonReader for reading NDJSON formatted runtime facts.
- Introduced IRuntimeFactsIngestionService interface and its implementation.
- Enhanced Program.cs to register new services and endpoints for runtime facts.
- Updated CallgraphIngestionService to include CAS URI in stored artifacts.
- Created RuntimeFactsValidationException for validation errors during ingestion.
- Added tests for RuntimeFactsIngestionService and RuntimeFactsNdjsonReader.
- Implemented SignalsSealedModeMonitor for compliance checks in sealed mode.
- Updated project dependencies for testing utilities.
This commit is contained in:
master
2025-11-10 07:56:15 +02:00
parent 9df52d84aa
commit 69c59defdc
132 changed files with 19718 additions and 9334 deletions

View File

@@ -1,35 +1,35 @@
[
{
"analyzerId": "java",
"componentKey": "purl::pkg:maven/com/example/demo@1.0.0",
"purl": "pkg:maven/com/example/demo@1.0.0",
"name": "demo",
"version": "1.0.0",
"type": "maven",
"usedByEntrypoint": true,
"metadata": {
"artifactId": "demo",
"displayName": "Demo Library",
"groupId": "com.example",
"jarPath": "libs/demo.jar",
"manifestTitle": "Demo",
"manifestVendor": "Example Corp",
"manifestVersion": "1.0.0",
"packaging": "jar"
},
"evidence": [
{
"kind": "file",
"source": "MANIFEST.MF",
"locator": "libs/demo.jar!META-INF/MANIFEST.MF",
"value": "title=Demo;version=1.0.0;vendor=Example Corp"
},
{
"kind": "file",
"source": "pom.properties",
"locator": "libs/demo.jar!META-INF/maven/com.example/demo/pom.properties",
"sha256": "c20f36aa1b9d89d28cf9ed131519ffd6287a4dac0c7cb926130496f3f8157bf1"
}
]
}
]
[
{
"analyzerId": "java",
"componentKey": "purl::pkg:maven/com/example/demo@1.0.0",
"purl": "pkg:maven/com/example/demo@1.0.0",
"name": "demo",
"version": "1.0.0",
"type": "maven",
"usedByEntrypoint": true,
"metadata": {
"artifactId": "demo",
"displayName": "Demo Library",
"groupId": "com.example",
"jarPath": "libs/demo.jar",
"manifestTitle": "Demo",
"manifestVendor": "Example Corp",
"manifestVersion": "1.0.0",
"packaging": "jar"
},
"evidence": [
{
"kind": "file",
"source": "MANIFEST.MF",
"locator": "libs/demo.jar!META-INF/MANIFEST.MF",
"value": "title=Demo;version=1.0.0;vendor=Example Corp"
},
{
"kind": "file",
"source": "pom.properties",
"locator": "libs/demo.jar!META-INF/maven/com.example/demo/pom.properties",
"sha256": "82e3c738508fbe8110680d88b0db8c2d8013e2a3be3c3a3c6cddfd065e94249d"
}
]
}
]

View File

@@ -1,3 +1,7 @@
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using StellaOps.Scanner.Analyzers.Lang.Java;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
@@ -30,4 +34,103 @@ public sealed class JavaLanguageAnalyzerTests
TestPaths.SafeDelete(root);
}
}
[Fact]
public async Task LockfilesProduceDeclaredOnlyComponentsAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var root = TestPaths.CreateTemporaryDirectory();
try
{
var jarPath = CreateSampleJar(root, "com.example", "runtime-only", "1.0.0");
var lockPath = Path.Combine(root, "gradle.lockfile");
var lockContent = new StringBuilder()
.AppendLine("com.example:declared-only:2.0.0=runtimeClasspath")
.ToString();
await File.WriteAllTextAsync(lockPath, lockContent, cancellationToken);
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
root,
analyzers,
cancellationToken,
new LanguageUsageHints(new[] { jarPath }));
using var document = JsonDocument.Parse(json);
var rootElement = document.RootElement;
Assert.True(ComponentHasMetadata(rootElement, "declared-only", "declaredOnly", "true"));
Assert.True(ComponentHasMetadata(rootElement, "declared-only", "lockSource", "gradle.lockfile"));
Assert.True(ComponentHasMetadata(rootElement, "runtime-only", "lockMissing", "true"));
}
finally
{
TestPaths.SafeDelete(root);
}
}
private static bool ComponentHasMetadata(JsonElement root, string componentName, string key, string expected)
{
foreach (var element in root.EnumerateArray())
{
if (!element.TryGetProperty("name", out var nameElement) ||
!string.Equals(nameElement.GetString(), componentName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (!element.TryGetProperty("metadata", out var metadataElement) || metadataElement.ValueKind != JsonValueKind.Object)
{
continue;
}
if (!metadataElement.TryGetProperty(key, out var valueElement))
{
continue;
}
if (string.Equals(valueElement.GetString(), expected, StringComparison.Ordinal))
{
return true;
}
}
return false;
}
private static string CreateSampleJar(string root, string groupId, string artifactId, string version)
{
var jarPath = Path.Combine(root, $"{artifactId}-{version}.jar");
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
using var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create);
var pomPropertiesPath = $"META-INF/maven/{groupId}/{artifactId}/pom.properties";
var pomPropertiesEntry = archive.CreateEntry(pomPropertiesPath);
using (var writer = new StreamWriter(pomPropertiesEntry.Open(), Encoding.UTF8))
{
writer.WriteLine($"groupId={groupId}");
writer.WriteLine($"artifactId={artifactId}");
writer.WriteLine($"version={version}");
writer.WriteLine("packaging=jar");
writer.WriteLine("name=Sample");
}
var manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF");
using (var writer = new StreamWriter(manifestEntry.Open(), Encoding.UTF8))
{
writer.WriteLine("Manifest-Version: 1.0");
writer.WriteLine($"Implementation-Title: {artifactId}");
writer.WriteLine($"Implementation-Version: {version}");
writer.WriteLine($"Implementation-Vendor: {groupId}");
}
var classEntry = archive.CreateEntry($"{artifactId.Replace('-', '_')}/Main.class");
using (var stream = classEntry.Open())
{
stream.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
return jarPath;
}
}