fix(server): resolve Medium code-review finding (Server-013)

Replace silent Enum.TryParse fallback to None with a ParseSecurityProfile
helper that emits a startup Log.Warning naming the unsupported value and
listing recognised profiles; operators now see the misconfiguration
before any client connects rather than getting an unexplained None posture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 11:03:35 -04:00
parent a00f0338b5
commit 2dd0bd4198
2 changed files with 28 additions and 4 deletions

View File

@@ -97,12 +97,15 @@ var opcUaOptions = new OpcUaServerOptions
PkiStoreRoot = opcUaSection.GetValue<string>("PkiStoreRoot")
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "OtOpcUa", "pki"),
AutoAcceptUntrustedClientCertificates = opcUaSection.GetValue<bool?>("AutoAcceptUntrustedClientCertificates") ?? false, // Server-010: secure by default
SecurityProfile = Enum.TryParse<OpcUaSecurityProfile>(opcUaSection.GetValue<string>("SecurityProfile"), true, out var p)
? p : OpcUaSecurityProfile.None,
SecurityProfile = ParseSecurityProfile(opcUaSection.GetValue<string>("SecurityProfile"), out var profileParseWarning),
Ldap = ldapOptions,
AnonymousRoles = opcUaSection.GetSection("AnonymousRoles").Get<string[]>() ?? [],
};
// Server-013: warn at startup when the configured SecurityProfile string does not map to
// any supported profile — Enum.TryParse previously silently fell back to None.
if (profileParseWarning is not null) Log.Warning("{Warning}", profileParseWarning);
builder.Services.AddSingleton(options);
builder.Services.AddSingleton(opcUaOptions);
builder.Services.AddSingleton(ldapOptions);
@@ -271,6 +274,27 @@ builder.Services.AddSingleton<Phase7Composer>();
var host = builder.Build();
await host.RunAsync();
// Server-013: parse the SecurityProfile config value and produce a warning string when the
// configured value is unrecognised, so operators get a startup diagnostic instead of silent
// fallback to None.
static OpcUaSecurityProfile ParseSecurityProfile(string? raw, out string? warning)
{
if (string.IsNullOrWhiteSpace(raw))
{
warning = null;
return OpcUaSecurityProfile.None;
}
if (Enum.TryParse<OpcUaSecurityProfile>(raw, ignoreCase: true, out var parsed))
{
warning = null;
return parsed;
}
warning = $"OpcUaServer:SecurityProfile value '{raw}' is not a recognised profile " +
$"(supported: {string.Join(", ", Enum.GetNames<OpcUaSecurityProfile>())}). " +
"Falling back to SecurityProfile.None — no encryption or signed transport will be applied.";
return OpcUaSecurityProfile.None;
}
// Server-007: lightweight cached config-DB health probe for /healthz.
// Runs CanConnectAsync once and caches the result for 10 s so the /healthz
// endpoint never blocks an HTTP handler thread on a DB round-trip while still