Add post-quantum cryptography support with PqSoftCryptoProvider
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
wine-csp-build / Build Wine CSP Image (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
wine-csp-build / Build Wine CSP Image (push) Has been cancelled
- Implemented PqSoftCryptoProvider for software-only post-quantum algorithms (Dilithium3, Falcon512) using BouncyCastle. - Added PqSoftProviderOptions and PqSoftKeyOptions for configuration. - Created unit tests for Dilithium3 and Falcon512 signing and verification. - Introduced EcdsaPolicyCryptoProvider for compliance profiles (FIPS/eIDAS) with explicit allow-lists. - Added KcmvpHashOnlyProvider for KCMVP baseline compliance. - Updated project files and dependencies for new libraries and testing frameworks.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
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;
|
||||
@@ -37,12 +37,12 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LockfilesProduceDeclaredOnlyComponentsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
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");
|
||||
@@ -64,132 +64,330 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CapturesFrameworkConfigurationHintsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "demo-framework.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "demo-framework", "1.0.0");
|
||||
WriteManifest(archive, "demo-framework", "1.0.0", "com.example");
|
||||
|
||||
CreateTextEntry(archive, "META-INF/spring.factories");
|
||||
CreateTextEntry(archive, "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports");
|
||||
CreateTextEntry(archive, "META-INF/spring/org.springframework.boot.actuate.autoconfigure.AutoConfiguration.imports");
|
||||
CreateTextEntry(archive, "BOOT-INF/classes/application.yml");
|
||||
CreateTextEntry(archive, "WEB-INF/web.xml");
|
||||
CreateTextEntry(archive, "META-INF/web-fragment.xml");
|
||||
CreateTextEntry(archive, "META-INF/persistence.xml");
|
||||
CreateTextEntry(archive, "META-INF/beans.xml");
|
||||
CreateTextEntry(archive, "META-INF/jaxb.index");
|
||||
CreateTextEntry(archive, "META-INF/services/jakarta.ws.rs.ext.RuntimeDelegate");
|
||||
CreateTextEntry(archive, "logback.xml");
|
||||
CreateTextEntry(archive, "META-INF/native-image/demo/reflect-config.json");
|
||||
}
|
||||
|
||||
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 component = document.RootElement
|
||||
.EnumerateArray()
|
||||
.First(element => string.Equals(element.GetProperty("name").GetString(), "demo-framework", StringComparison.Ordinal));
|
||||
|
||||
var metadata = component.GetProperty("metadata");
|
||||
Assert.Equal("demo-framework.jar!META-INF/spring.factories", metadata.GetProperty("config.spring.factories").GetString());
|
||||
Assert.Equal(
|
||||
"demo-framework.jar!META-INF/spring/org.springframework.boot.actuate.autoconfigure.AutoConfiguration.imports,demo-framework.jar!META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
|
||||
metadata.GetProperty("config.spring.imports").GetString());
|
||||
Assert.Equal("demo-framework.jar!BOOT-INF/classes/application.yml", metadata.GetProperty("config.spring.properties").GetString());
|
||||
Assert.Equal("demo-framework.jar!WEB-INF/web.xml", metadata.GetProperty("config.web.xml").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/web-fragment.xml", metadata.GetProperty("config.web.fragment").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/persistence.xml", metadata.GetProperty("config.jpa").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/beans.xml", metadata.GetProperty("config.cdi").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/jaxb.index", metadata.GetProperty("config.jaxb").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/services/jakarta.ws.rs.ext.RuntimeDelegate", metadata.GetProperty("config.jaxrs").GetString());
|
||||
Assert.Equal("demo-framework.jar!logback.xml", metadata.GetProperty("config.logging").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/native-image/demo/reflect-config.json", metadata.GetProperty("config.graal").GetString());
|
||||
|
||||
var evidence = component.GetProperty("evidence").EnumerateArray().ToArray();
|
||||
Assert.Contains(evidence, e =>
|
||||
string.Equals(e.GetProperty("source").GetString(), "framework-config", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(e.GetProperty("locator").GetString(), "demo-framework.jar!META-INF/spring.factories", StringComparison.OrdinalIgnoreCase) &&
|
||||
e.TryGetProperty("sha256", out var sha) &&
|
||||
!string.IsNullOrWhiteSpace(sha.GetString()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CapturesJniHintsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "demo-jni.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "demo-jni", "1.0.0");
|
||||
WriteManifest(archive, "demo-jni", "1.0.0", "com.example");
|
||||
|
||||
CreateBinaryEntry(archive, "com/example/App.class", "System.loadLibrary(\"foo\")");
|
||||
CreateTextEntry(archive, "lib/native/libfoo.so");
|
||||
CreateTextEntry(archive, "META-INF/native-image/demo/jni-config.json");
|
||||
}
|
||||
|
||||
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 component = document.RootElement
|
||||
.EnumerateArray()
|
||||
.First(element => string.Equals(element.GetProperty("name").GetString(), "demo-jni", StringComparison.Ordinal));
|
||||
|
||||
var metadata = component.GetProperty("metadata");
|
||||
Assert.Equal("libfoo.so", metadata.GetProperty("jni.nativeLibs").GetString());
|
||||
Assert.Equal("demo-jni.jar!META-INF/native-image/demo/jni-config.json", metadata.GetProperty("jni.graalConfig").GetString());
|
||||
Assert.Equal("demo-jni.jar!com/example/App.class", metadata.GetProperty("jni.loadCalls").GetString());
|
||||
}
|
||||
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) ||
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CapturesFrameworkConfigurationHintsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "demo-framework.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "demo-framework", "1.0.0");
|
||||
WriteManifest(archive, "demo-framework", "1.0.0", "com.example");
|
||||
|
||||
CreateTextEntry(archive, "META-INF/spring.factories");
|
||||
CreateTextEntry(archive, "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports");
|
||||
CreateTextEntry(archive, "META-INF/spring/org.springframework.boot.actuate.autoconfigure.AutoConfiguration.imports");
|
||||
CreateTextEntry(archive, "BOOT-INF/classes/application.yml");
|
||||
CreateTextEntry(archive, "WEB-INF/web.xml");
|
||||
CreateTextEntry(archive, "META-INF/web-fragment.xml");
|
||||
CreateTextEntry(archive, "META-INF/persistence.xml");
|
||||
CreateTextEntry(archive, "META-INF/beans.xml");
|
||||
CreateTextEntry(archive, "META-INF/jaxb.index");
|
||||
CreateTextEntry(archive, "META-INF/services/jakarta.ws.rs.ext.RuntimeDelegate");
|
||||
CreateTextEntry(archive, "logback.xml");
|
||||
CreateTextEntry(archive, "META-INF/native-image/demo/reflect-config.json");
|
||||
}
|
||||
|
||||
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 component = document.RootElement
|
||||
.EnumerateArray()
|
||||
.First(element => string.Equals(element.GetProperty("name").GetString(), "demo-framework", StringComparison.Ordinal));
|
||||
|
||||
var metadata = component.GetProperty("metadata");
|
||||
Assert.Equal("demo-framework.jar!META-INF/spring.factories", metadata.GetProperty("config.spring.factories").GetString());
|
||||
Assert.Equal(
|
||||
"demo-framework.jar!META-INF/spring/org.springframework.boot.actuate.autoconfigure.AutoConfiguration.imports,demo-framework.jar!META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
|
||||
metadata.GetProperty("config.spring.imports").GetString());
|
||||
Assert.Equal("demo-framework.jar!BOOT-INF/classes/application.yml", metadata.GetProperty("config.spring.properties").GetString());
|
||||
Assert.Equal("demo-framework.jar!WEB-INF/web.xml", metadata.GetProperty("config.web.xml").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/web-fragment.xml", metadata.GetProperty("config.web.fragment").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/persistence.xml", metadata.GetProperty("config.jpa").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/beans.xml", metadata.GetProperty("config.cdi").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/jaxb.index", metadata.GetProperty("config.jaxb").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/services/jakarta.ws.rs.ext.RuntimeDelegate", metadata.GetProperty("config.jaxrs").GetString());
|
||||
Assert.Equal("demo-framework.jar!logback.xml", metadata.GetProperty("config.logging").GetString());
|
||||
Assert.Equal("demo-framework.jar!META-INF/native-image/demo/reflect-config.json", metadata.GetProperty("config.graal").GetString());
|
||||
|
||||
var evidence = component.GetProperty("evidence").EnumerateArray().ToArray();
|
||||
Assert.Contains(evidence, e =>
|
||||
string.Equals(e.GetProperty("source").GetString(), "framework-config", StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(e.GetProperty("locator").GetString(), "demo-framework.jar!META-INF/spring.factories", StringComparison.OrdinalIgnoreCase) &&
|
||||
e.TryGetProperty("sha256", out var sha) &&
|
||||
!string.IsNullOrWhiteSpace(sha.GetString()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CapturesJniHintsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var root = TestPaths.CreateTemporaryDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
var jarPath = Path.Combine(root, "demo-jni.jar");
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
|
||||
|
||||
using (var archive = ZipFile.Open(jarPath, ZipArchiveMode.Create))
|
||||
{
|
||||
WritePomProperties(archive, "com.example", "demo-jni", "1.0.0");
|
||||
WriteManifest(archive, "demo-jni", "1.0.0", "com.example");
|
||||
|
||||
CreateBinaryEntry(archive, "com/example/App.class", "System.loadLibrary(\"foo\")");
|
||||
CreateTextEntry(archive, "lib/native/libfoo.so");
|
||||
CreateTextEntry(archive, "META-INF/native-image/demo/jni-config.json");
|
||||
}
|
||||
|
||||
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 component = document.RootElement
|
||||
.EnumerateArray()
|
||||
.First(element => string.Equals(element.GetProperty("name").GetString(), "demo-jni", StringComparison.Ordinal));
|
||||
|
||||
var metadata = component.GetProperty("metadata");
|
||||
Assert.Equal("libfoo.so", metadata.GetProperty("jni.nativeLibs").GetString());
|
||||
Assert.Equal("demo-jni.jar!META-INF/native-image/demo/jni-config.json", metadata.GetProperty("jni.graalConfig").GetString());
|
||||
Assert.Equal("demo-jni.jar!com/example/App.class", metadata.GetProperty("jni.loadCalls").GetString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestPaths.SafeDelete(root);
|
||||
}
|
||||
}
|
||||
|
||||
#region Build File Fixture Integration Tests
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesGradleGroovyBuildFileAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "gradle-groovy");
|
||||
var goldenPath = TestPaths.ResolveFixture("java", "gradle-groovy", "expected.json");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify key dependencies are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "guava"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "commons-lang3"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "slf4j-api"));
|
||||
|
||||
// Verify declaredOnly flag is set for build file dependencies
|
||||
var guava = components.First(c => c.GetProperty("name").GetString() == "guava");
|
||||
Assert.True(guava.GetProperty("metadata").TryGetProperty("declaredOnly", out var declaredOnly));
|
||||
Assert.Equal("true", declaredOnly.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesGradleKotlinBuildFileAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "gradle-kotlin");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify Kotlin DSL dependencies are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "kotlin-stdlib"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "jackson-databind"));
|
||||
|
||||
// Verify kapt/ksp dependencies are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "mapstruct-processor"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesGradleVersionCatalogAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "gradle-catalog");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify version catalog dependencies are resolved
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "kotlin-stdlib"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "commons-lang3"));
|
||||
|
||||
// Verify version is resolved from catalog
|
||||
var kotlinStdlib = components.First(c => c.GetProperty("name").GetString() == "kotlin-stdlib");
|
||||
Assert.Equal("1.9.21", kotlinStdlib.GetProperty("version").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesMavenParentPomAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "maven-parent");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify dependencies with inherited versions are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "guava"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "slf4j-api"));
|
||||
|
||||
// Verify version is inherited from parent
|
||||
var guava = components.First(c => c.GetProperty("name").GetString() == "guava");
|
||||
Assert.Equal("32.1.3-jre", guava.GetProperty("version").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesMavenBomImportsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "maven-bom");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify BOM imports are detected
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "spring-boot-dependencies"));
|
||||
Assert.True(components.Any(c => c.GetProperty("name").GetString() == "jackson-bom"));
|
||||
|
||||
// Verify BOM metadata
|
||||
var springBom = components.First(c => c.GetProperty("name").GetString() == "spring-boot-dependencies");
|
||||
var metadata = springBom.GetProperty("metadata");
|
||||
Assert.True(metadata.TryGetProperty("bomImport", out var bomImport));
|
||||
Assert.Equal("true", bomImport.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesMavenPropertyPlaceholdersAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "maven-properties");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify property placeholders are resolved
|
||||
var springCore = components.FirstOrDefault(c => c.GetProperty("name").GetString() == "spring-core");
|
||||
Assert.NotNull(springCore);
|
||||
Assert.Equal("6.1.0", springCore.Value.GetProperty("version").GetString());
|
||||
|
||||
// Verify versionProperty metadata is captured
|
||||
var metadata = springCore.Value.GetProperty("metadata");
|
||||
Assert.True(metadata.TryGetProperty("versionProperty", out var versionProp));
|
||||
Assert.Equal("spring.version", versionProp.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ParsesMavenScopesAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "maven-scopes");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify different scopes are captured
|
||||
var guava = components.First(c => c.GetProperty("name").GetString() == "guava");
|
||||
Assert.Equal("compile", guava.GetProperty("metadata").GetProperty("declaredScope").GetString());
|
||||
|
||||
var servletApi = components.First(c => c.GetProperty("name").GetString() == "jakarta.servlet-api");
|
||||
Assert.Equal("provided", servletApi.GetProperty("metadata").GetProperty("declaredScope").GetString());
|
||||
|
||||
var postgresql = components.First(c => c.GetProperty("name").GetString() == "postgresql");
|
||||
Assert.Equal("runtime", postgresql.GetProperty("metadata").GetProperty("declaredScope").GetString());
|
||||
|
||||
var junit = components.First(c => c.GetProperty("name").GetString() == "junit-jupiter");
|
||||
Assert.Equal("test", junit.GetProperty("metadata").GetProperty("declaredScope").GetString());
|
||||
|
||||
// Verify optional flag
|
||||
var springContext = components.First(c => c.GetProperty("name").GetString() == "spring-context");
|
||||
Assert.True(springContext.GetProperty("metadata").TryGetProperty("optional", out var optional));
|
||||
Assert.Equal("true", optional.GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DetectsVersionConflictsAsync()
|
||||
{
|
||||
var cancellationToken = TestContext.Current.CancellationToken;
|
||||
var fixturePath = TestPaths.ResolveFixture("java", "version-conflict");
|
||||
|
||||
var analyzers = new ILanguageAnalyzer[] { new JavaLanguageAnalyzer() };
|
||||
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
|
||||
|
||||
using var document = JsonDocument.Parse(json);
|
||||
var components = document.RootElement.EnumerateArray().ToArray();
|
||||
|
||||
// Verify Jackson version conflict is detected
|
||||
var jacksonDatabind = components.First(c => c.GetProperty("name").GetString() == "jackson-databind");
|
||||
var metadata = jacksonDatabind.GetProperty("metadata");
|
||||
|
||||
if (metadata.TryGetProperty("versionConflict.group", out var conflictGroup))
|
||||
{
|
||||
Assert.Equal("com.fasterxml.jackson.core", conflictGroup.GetString());
|
||||
}
|
||||
|
||||
// Verify Spring version conflict is detected
|
||||
var springCore = components.First(c => c.GetProperty("name").GetString() == "spring-core");
|
||||
var springMetadata = springCore.GetProperty("metadata");
|
||||
|
||||
if (springMetadata.TryGetProperty("versionConflict.group", out var springConflictGroup))
|
||||
{
|
||||
Assert.Equal("org.springframework", springConflictGroup.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
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;
|
||||
@@ -211,53 +409,53 @@ public sealed class JavaLanguageAnalyzerTests
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void WritePomProperties(ZipArchive archive, string groupId, string artifactId, string version)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
private static void WriteManifest(ZipArchive archive, string artifactId, string version, string groupId)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
private static void CreateTextEntry(ZipArchive archive, string path, string? content = null)
|
||||
{
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
writer.Write(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateBinaryEntry(ZipArchive archive, string path, string content)
|
||||
{
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var stream = entry.Open();
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
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)!);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void WritePomProperties(ZipArchive archive, string groupId, string artifactId, string version)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
private static void WriteManifest(ZipArchive archive, string artifactId, string version, string groupId)
|
||||
{
|
||||
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}");
|
||||
}
|
||||
|
||||
private static void CreateTextEntry(ZipArchive archive, string path, string? content = null)
|
||||
{
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8);
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
writer.Write(content);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateBinaryEntry(ZipArchive archive, string path, string content)
|
||||
{
|
||||
var entry = archive.CreateEntry(path);
|
||||
using var stream = entry.Open();
|
||||
var bytes = Encoding.UTF8.GetBytes(content);
|
||||
stream.Write(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
Reference in New Issue
Block a user