Refactor and enhance LDAP plugin configuration and validation
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled

- Updated `LdapPluginOptions` to enforce TLS and client certificate requirements.
- Added validation checks for TLS configuration in `LdapPluginOptionsTests`.
- Improved error handling in `DirectoryServicesLdapConnectionFactory` for StartTLS negotiation.
- Enhanced logging in `LdapCredentialStore` to include detailed audit properties for credential verification.
- Introduced `StubStructuredRetriever` and `StubVectorRetriever` for testing in `ToolsetServiceCollectionExtensionsTests`.
- Refactored `AdvisoryGuardrailPipelineTests` to improve test clarity and structure.
- Added `FileSystemAdvisoryTaskQueueTests` for testing queue functionality.
- Updated JSON test data for consistency with new requirements.
- Modified `AdvisoryPipelineOrchestratorTests` to reflect changes in metadata keys.
This commit is contained in:
master
2025-11-05 09:29:51 +02:00
parent 3bd0955202
commit 40e7f827da
37 changed files with 744 additions and 315 deletions

View File

@@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
namespace StellaOps.AdvisoryAI.Orchestration;
/// <summary>
/// Queue payload sent to workers to execute a pipeline plan.
/// </summary>
public sealed class AdvisoryPipelineExecutionMessage
{
public AdvisoryPipelineExecutionMessage(
string planCacheKey,
AdvisoryTaskRequest request,
IReadOnlyDictionary<string, string> planMetadata)
{
ArgumentException.ThrowIfNullOrWhiteSpace(planCacheKey);
PlanCacheKey = planCacheKey;
Request = request ?? throw new ArgumentNullException(nameof(request));
PlanMetadata = planMetadata ?? throw new ArgumentNullException(nameof(planMetadata));
}
public string PlanCacheKey { get; }
public AdvisoryTaskRequest Request { get; }
public IReadOnlyDictionary<string, string> PlanMetadata { get; }
}

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Extensions.Logging;
@@ -118,8 +120,9 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
.RetrieveAsync(sbomRequest, cancellationToken)
.ConfigureAwait(false);
var analysis = _toolset.AnalyzeDependencies(context);
return (context, analysis);
var sanitizedContext = SanitizeContext(context, configuration);
var analysis = _toolset.AnalyzeDependencies(sanitizedContext);
return (sanitizedContext, analysis);
}
private static ImmutableDictionary<string, string> BuildMetadata(
@@ -133,7 +136,7 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
builder["task_type"] = request.TaskType.ToString();
builder["advisory_key"] = request.AdvisoryKey;
builder["profile"] = request.Profile;
builder["structured_chunk_count"] = structured.Chunks.Count.ToString(CultureInfo.InvariantCulture);
builder["structured_chunk_count"] = structured.Chunks.Count().ToString(CultureInfo.InvariantCulture);
builder["vector_query_count"] = vectors.Length.ToString(CultureInfo.InvariantCulture);
builder["vector_match_count"] = vectors.Sum(result => result.Matches.Length).ToString(CultureInfo.InvariantCulture);
builder["includes_sbom"] = (sbom is not null).ToString();
@@ -147,8 +150,8 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
if (sbom is not null)
{
builder["sbom_version_count"] = sbom.VersionTimeline.Count.ToString(CultureInfo.InvariantCulture);
builder["sbom_dependency_path_count"] = sbom.DependencyPaths.Count.ToString(CultureInfo.InvariantCulture);
builder["sbom_version_count"] = sbom.VersionTimeline.Length.ToString(CultureInfo.InvariantCulture);
builder["sbom_dependency_path_count"] = sbom.DependencyPaths.Length.ToString(CultureInfo.InvariantCulture);
if (!sbom.EnvironmentFlags.IsEmpty)
{
@@ -197,6 +200,34 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
return builder.ToImmutable();
}
private static SbomContextResult SanitizeContext(
SbomContextResult context,
AdvisoryTaskConfiguration configuration)
{
if ((configuration.IncludeEnvironmentFlags || context.EnvironmentFlags.IsEmpty)
&& (configuration.IncludeBlastRadius || context.BlastRadius is null))
{
return context;
}
var environmentFlags = configuration.IncludeEnvironmentFlags
? context.EnvironmentFlags
: ImmutableDictionary<string, string>.Empty;
var blastRadius = configuration.IncludeBlastRadius
? context.BlastRadius
: null;
return SbomContextResult.Create(
context.ArtifactId,
context.Purl,
context.VersionTimeline,
context.DependencyPaths,
environmentFlags,
blastRadius,
context.Metadata);
}
private static string ComputeCacheKey(
AdvisoryTaskRequest request,
AdvisoryRetrievalResult structured,
@@ -242,8 +273,8 @@ internal sealed class AdvisoryPipelineOrchestrator : IAdvisoryPipelineOrchestrat
if (sbom is not null)
{
builder.Append("|sbom:timeline=").Append(sbom.VersionTimeline.Count);
builder.Append("|sbom:paths=").Append(sbom.DependencyPaths.Count);
builder.Append("|sbom:timeline=").Append(sbom.VersionTimeline.Length);
builder.Append("|sbom:paths=").Append(sbom.DependencyPaths.Length);
foreach (var entry in sbom.VersionTimeline
.OrderBy(e => e.Version, StringComparer.Ordinal)
.ThenBy(e => e.FirstObserved.ToUnixTimeMilliseconds())

View File

@@ -72,8 +72,8 @@ public sealed class AdvisoryPipelinePlanResponse
{
sbomSummary = new PipelineSbomSummary(
plan.SbomContext.ArtifactId,
plan.SbomContext.VersionTimeline.Count,
plan.SbomContext.DependencyPaths.Count,
plan.SbomContext.VersionTimeline.Length,
plan.SbomContext.DependencyPaths.Length,
plan.DependencyAnalysis?.Nodes.Length ?? 0);
}

View File

@@ -1,6 +1,5 @@
using System.Collections.Immutable;
using StellaOps.AdvisoryAI.Abstractions;
using StellaOps.AdvisoryAI.Documents;
using StellaOps.AdvisoryAI.Context;
using StellaOps.AdvisoryAI.Documents;
using StellaOps.AdvisoryAI.Tools;