feat: Implement Runtime Facts ingestion service and NDJSON reader
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
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:
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user