Add Authority Advisory AI and API Lifecycle Configuration

- Introduced AuthorityAdvisoryAiOptions and related classes for managing advisory AI configurations, including remote inference options and tenant-specific settings.
- Added AuthorityApiLifecycleOptions to control API lifecycle settings, including legacy OAuth endpoint configurations.
- Implemented validation and normalization methods for both advisory AI and API lifecycle options to ensure proper configuration.
- Created AuthorityNotificationsOptions and its related classes for managing notification settings, including ack tokens, webhooks, and escalation options.
- Developed IssuerDirectoryClient and related models for interacting with the issuer directory service, including caching mechanisms and HTTP client configurations.
- Added support for dependency injection through ServiceCollectionExtensions for the Issuer Directory Client.
- Updated project file to include necessary package references for the new Issuer Directory Client library.
This commit is contained in:
master
2025-11-02 13:40:38 +02:00
parent 66cb6c4b8a
commit f98cea3bcf
516 changed files with 68157 additions and 24754 deletions

View File

@@ -0,0 +1,25 @@
[
{
"analyzerId": "rust",
"componentKey": "bin::sha256:10f3c03766e4403be40add0467a2b2d07fd7006e4b8515ab88740ffa327ea775",
"purl": null,
"name": "opaque_bin",
"version": null,
"type": "bin",
"usedByEntrypoint": true,
"metadata": {
"binary.path": "usr/local/bin/opaque_bin",
"binary.sha256": "10f3c03766e4403be40add0467a2b2d07fd7006e4b8515ab88740ffa327ea775",
"provenance": "binary"
},
"evidence": [
{
"kind": "file",
"source": "binary",
"locator": "usr/local/bin/opaque_bin",
"value": null,
"sha256": "10f3c03766e4403be40add0467a2b2d07fd7006e4b8515ab88740ffa327ea775"
}
]
}
]

View File

@@ -0,0 +1,8 @@
{
"detectedCrates": [
{
"name": "serde",
"note": "Binary symbol scan matched only serde"
}
]
}

View File

@@ -0,0 +1,68 @@
[
{
"analyzerId": "rust",
"componentKey": "rust::heuristic::reqwest::usr/local/bin/heuristic_app",
"name": "reqwest",
"type": "cargo",
"usedByEntrypoint": true,
"metadata": {
"binary.paths": "usr/local/bin/heuristic_app",
"binary.sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b",
"crate": "reqwest",
"provenance": "heuristic"
},
"evidence": [
{
"kind": "derived",
"source": "rust.heuristic",
"locator": "usr/local/bin/heuristic_app",
"value": "reqwest",
"sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b"
}
]
},
{
"analyzerId": "rust",
"componentKey": "rust::heuristic::serde::usr/local/bin/heuristic_app",
"name": "serde",
"type": "cargo",
"usedByEntrypoint": true,
"metadata": {
"binary.paths": "usr/local/bin/heuristic_app",
"binary.sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b",
"crate": "serde",
"provenance": "heuristic"
},
"evidence": [
{
"kind": "derived",
"source": "rust.heuristic",
"locator": "usr/local/bin/heuristic_app",
"value": "serde",
"sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b"
}
]
},
{
"analyzerId": "rust",
"componentKey": "rust::heuristic::tokio::usr/local/bin/heuristic_app",
"name": "tokio",
"type": "cargo",
"usedByEntrypoint": true,
"metadata": {
"binary.paths": "usr/local/bin/heuristic_app",
"binary.sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b",
"crate": "tokio",
"provenance": "heuristic"
},
"evidence": [
{
"kind": "derived",
"source": "rust.heuristic",
"locator": "usr/local/bin/heuristic_app",
"value": "tokio",
"sha256": "4caf60c501a594b5d4b8d909b3e91fccc4447692b9e144f322a333255909310b"
}
]
}
]

View File

@@ -0,0 +1,77 @@
using System.Linq;
using System.Text.Json;
using StellaOps.Scanner.Analyzers.Lang.Rust;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Rust;
public sealed class RustHeuristicCoverageComparisonTests
{
[Fact]
public async Task HeuristicCoverageExceedsCompetitorBaselineAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "heuristics");
var baselinePath = Path.Combine(fixturePath, "competitor-baseline.json");
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
var output = await LanguageAnalyzerTestHarness.RunToJsonAsync(
fixturePath,
analyzers,
cancellationToken);
using var ours = JsonDocument.Parse(output);
var heuristicNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var element in ours.RootElement.EnumerateArray())
{
if (!element.TryGetProperty("metadata", out var metadata) || metadata.ValueKind != JsonValueKind.Object)
{
continue;
}
var provenance = metadata.EnumerateObject()
.FirstOrDefault(p => string.Equals(p.Name, "provenance", StringComparison.OrdinalIgnoreCase));
if (provenance.Value.ValueKind == JsonValueKind.String &&
string.Equals(provenance.Value.GetString(), "heuristic", StringComparison.OrdinalIgnoreCase))
{
if (element.TryGetProperty("name", out var nameProperty) && nameProperty.ValueKind == JsonValueKind.String)
{
var value = nameProperty.GetString();
if (!string.IsNullOrWhiteSpace(value))
{
heuristicNames.Add(value);
}
}
}
}
using var competitor = JsonDocument.Parse(await File.ReadAllTextAsync(baselinePath, cancellationToken));
var competitorNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (competitor.RootElement.TryGetProperty("detectedCrates", out var detectedCrates) && detectedCrates.ValueKind == JsonValueKind.Array)
{
foreach (var entry in detectedCrates.EnumerateArray())
{
if (entry.ValueKind == JsonValueKind.Object && entry.TryGetProperty("name", out var nameProperty) && nameProperty.ValueKind == JsonValueKind.String)
{
var name = nameProperty.GetString();
if (!string.IsNullOrWhiteSpace(name))
{
competitorNames.Add(name);
}
}
}
}
Assert.NotEmpty(competitorNames);
Assert.True(heuristicNames.IsSupersetOf(competitorNames));
var improvement = (double)heuristicNames.Count / competitorNames.Count;
Assert.True(improvement >= 1.15, $"Expected at least 15% improvement; got {improvement:P2} ({heuristicNames.Count} vs {competitorNames.Count}).");
}
}

View File

@@ -1,7 +1,8 @@
using System;
using System.IO;
using System.Linq;
using StellaOps.Scanner.Analyzers.Lang.Rust;
using System.Text.Json.Nodes;
using StellaOps.Scanner.Analyzers.Lang.Rust;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
@@ -35,25 +36,86 @@ public sealed class RustLanguageAnalyzerTests
}
[Fact]
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "simple");
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
var workers = Math.Max(Environment.ProcessorCount, 4);
var tasks = Enumerable.Range(0, workers)
.Select(_ => LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken));
var results = await Task.WhenAll(tasks);
var baseline = results[0];
foreach (var result in results)
{
Assert.Equal(baseline, result);
}
}
}
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "simple");
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
var workers = Math.Max(Environment.ProcessorCount, 4);
var tasks = Enumerable.Range(0, workers)
.Select(_ => LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken));
var results = await Task.WhenAll(tasks);
var baseline = results[0];
foreach (var result in results)
{
Assert.Equal(baseline, result);
}
}
[Fact]
public async Task HeuristicFixtureProducesExpectedOutputAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "heuristics");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var usageHints = new LanguageUsageHints(new[]
{
Path.Combine(fixturePath, "usr/local/bin/heuristic_app")
});
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken,
usageHints);
}
[Fact]
public async Task FallbackFixtureProducesExpectedOutputAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "fallback");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var usageHints = new LanguageUsageHints(new[]
{
Path.Combine(fixturePath, "usr/local/bin/opaque_bin")
});
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
var actualJson = await LanguageAnalyzerTestHarness.RunToJsonAsync(
fixturePath,
analyzers,
cancellationToken,
usageHints);
var repeat = await LanguageAnalyzerTestHarness.RunToJsonAsync(
fixturePath,
analyzers,
cancellationToken,
usageHints);
Assert.Equal(actualJson, repeat);
var expectedJson = await File.ReadAllTextAsync(goldenPath, cancellationToken);
var actualNode = JsonNode.Parse(actualJson);
var expectedNode = JsonNode.Parse(expectedJson);
Assert.True(
JsonNode.DeepEquals(expectedNode, actualNode),
"Fallback fixture output does not match expected snapshot.");
}
}