Implement remediation-aware health checks across all Doctor plugin modules (Agent, Attestor, Auth, BinaryAnalysis, Compliance, Crypto, Environment, EvidenceLocker, Notify, Observability, Operations, Policy, Postgres, Release, Scanner, Storage, Vex) and their backing library counterparts (AI, Attestation, Authority, Core, Cryptography, Database, Docker, Integration, Notify, Observability, Security, ServiceGraph, Sources, Verification). Each check now emits structured remediation metadata (severity, category, runbook links, and fix suggestions) consumed by the Doctor dashboard remediation panel. Also adds: - docs/doctor/articles/ knowledge base for check explanations - Advisory AI search seed and allowlist updates for doctor content - Sprint plan for doctor checks documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
163 lines
5.6 KiB
C#
163 lines
5.6 KiB
C#
|
|
using Microsoft.Extensions.Configuration;
|
|
using StellaOps.Doctor.Models;
|
|
using StellaOps.Doctor.Plugins;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace StellaOps.Doctor.Plugins.Cryptography.Checks;
|
|
|
|
/// <summary>
|
|
/// Validates SM2/SM3/SM4 (Chinese) cryptography provider availability.
|
|
/// </summary>
|
|
public sealed class SmProviderCheck : IDoctorCheck
|
|
{
|
|
/// <inheritdoc />
|
|
public string CheckId => "check.crypto.sm";
|
|
|
|
/// <inheritdoc />
|
|
public string Name => "SM Cryptography";
|
|
|
|
/// <inheritdoc />
|
|
public string Description => "Validates SM2/SM3/SM4 (GB/T) cryptography provider";
|
|
|
|
/// <inheritdoc />
|
|
public DoctorSeverity DefaultSeverity => DoctorSeverity.Info;
|
|
|
|
/// <inheritdoc />
|
|
public IReadOnlyList<string> Tags => ["cryptography", "sm2", "sm3", "sm4", "regional", "china"];
|
|
|
|
/// <inheritdoc />
|
|
public TimeSpan EstimatedDuration => TimeSpan.FromMilliseconds(100);
|
|
|
|
/// <inheritdoc />
|
|
public bool CanRun(DoctorPluginContext context)
|
|
{
|
|
// Only run if SM crypto is configured or enabled
|
|
var smEnabled = context.Configuration.GetValue<bool?>("Cryptography:Sm:Enabled")
|
|
?? context.Configuration.GetValue<bool?>("Cryptography:EnableSm");
|
|
|
|
return smEnabled == true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<DoctorCheckResult> RunAsync(DoctorPluginContext context, CancellationToken ct)
|
|
{
|
|
var result = context.CreateResult(CheckId, "stellaops.doctor.cryptography", DoctorCategory.Cryptography.ToString());
|
|
|
|
var smProvider = context.Configuration.GetValue<string>("Cryptography:Sm:Provider")
|
|
?? "smsoft";
|
|
|
|
var smEndpoint = context.Configuration.GetValue<string>("Cryptography:Sm:Endpoint")
|
|
?? context.Configuration.GetValue<string>("SmRemote:Endpoint");
|
|
|
|
var issues = new List<string>();
|
|
var providerInfo = new Dictionary<string, string>();
|
|
|
|
// Check environment gate for SM providers
|
|
var smSoftAllowed = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");
|
|
providerInfo["SM_SOFT_ALLOWED"] = smSoftAllowed ?? "(not set)";
|
|
|
|
switch (smProvider.ToLowerInvariant())
|
|
{
|
|
case "smsoft":
|
|
CheckSmSoft(issues, providerInfo);
|
|
break;
|
|
|
|
case "smremote":
|
|
case "remote":
|
|
CheckSmRemote(issues, providerInfo, smEndpoint);
|
|
break;
|
|
|
|
case "bouncycastle":
|
|
CheckBouncyCastleSm(issues, providerInfo);
|
|
break;
|
|
|
|
default:
|
|
issues.Add($"Unknown SM provider: {smProvider}");
|
|
break;
|
|
}
|
|
|
|
providerInfo["ConfiguredProvider"] = smProvider;
|
|
|
|
if (issues.Count > 0)
|
|
{
|
|
return Task.FromResult(result
|
|
.Warn($"{issues.Count} SM provider issue(s)")
|
|
.WithEvidence("SM cryptography", e =>
|
|
{
|
|
foreach (var kvp in providerInfo)
|
|
{
|
|
e.Add(kvp.Key, kvp.Value);
|
|
}
|
|
})
|
|
.WithCauses(issues.ToArray())
|
|
.WithRemediation(r => r
|
|
.AddManualStep(1, "Set environment gate", "Set SM_SOFT_ALLOWED=1 to enable SM software providers")
|
|
.AddManualStep(2, "Configure SmRemote", "Configure SmRemote:Endpoint for remote SM crypto service")
|
|
.WithRunbookUrl("docs/doctor/articles/crypto/crypto-sm.md"))
|
|
.WithVerification("stella doctor --check check.crypto.sm")
|
|
.Build());
|
|
}
|
|
|
|
return Task.FromResult(result
|
|
.Pass("SM cryptography provider is configured")
|
|
.WithEvidence("SM cryptography", e =>
|
|
{
|
|
foreach (var kvp in providerInfo)
|
|
{
|
|
e.Add(kvp.Key, kvp.Value);
|
|
}
|
|
})
|
|
.Build());
|
|
}
|
|
|
|
private static void CheckSmSoft(List<string> issues, Dictionary<string, string> providerInfo)
|
|
{
|
|
providerInfo["Provider"] = "SmSoft (Software Implementation)";
|
|
|
|
var smSoftAllowed = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");
|
|
if (smSoftAllowed != "1" && smSoftAllowed?.ToLowerInvariant() != "true")
|
|
{
|
|
issues.Add("SM_SOFT_ALLOWED environment variable not set - SmSoft provider may not be available");
|
|
}
|
|
|
|
// Check if BouncyCastle is available for SM algorithms
|
|
providerInfo["Implementation"] = "BouncyCastle (managed)";
|
|
}
|
|
|
|
private static void CheckSmRemote(List<string> issues, Dictionary<string, string> providerInfo, string? endpoint)
|
|
{
|
|
providerInfo["Provider"] = "SmRemote (Remote Service)";
|
|
|
|
if (string.IsNullOrWhiteSpace(endpoint))
|
|
{
|
|
issues.Add("SmRemote endpoint not configured");
|
|
providerInfo["Endpoint"] = "(not set)";
|
|
}
|
|
else
|
|
{
|
|
providerInfo["Endpoint"] = endpoint;
|
|
|
|
// Check if endpoint looks valid
|
|
if (!Uri.TryCreate(endpoint, UriKind.Absolute, out var uri))
|
|
{
|
|
issues.Add($"SmRemote endpoint is not a valid URI: {endpoint}");
|
|
}
|
|
else
|
|
{
|
|
providerInfo["Host"] = uri.Host;
|
|
providerInfo["Port"] = uri.Port.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void CheckBouncyCastleSm(List<string> issues, Dictionary<string, string> providerInfo)
|
|
{
|
|
providerInfo["Provider"] = "BouncyCastle";
|
|
providerInfo["Implementation"] = "Managed .NET implementation";
|
|
|
|
// BouncyCastle should be available if the package is referenced
|
|
providerInfo["Algorithms"] = "SM2, SM3, SM4";
|
|
}
|
|
}
|