d67634cc4f
The driver logged and surfaced the raw AgentUri (which may carry user:pass@) as HostName (the /hosts key), in ~20 log lines and in exception messages that reach DriverHealth.LastError, and the client's request-URI messages (timeouts, stream end, not-a-stream, framing) embedded it too - while the Test-Connect probe deliberately redacted it. New MTConnectUriRedaction (user-info, and for the configured string its query, removed; a clean URI is unchanged so host keys do not move); the driver computes its display form once per options document. Refs #743 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
61 lines
2.8 KiB
C#
61 lines
2.8 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.MTConnect;
|
|
|
|
/// <summary>
|
|
/// Review 2026-10-21 R06:T-8 — the display form of an Agent URI for every log line, exception message,
|
|
/// <c>DriverHealth.LastError</c> and <c>HostName</c> this driver produces. An <c>AgentUri</c> may carry
|
|
/// <c>user:pass@</c>; the Test-Connect probe already redacted it (<c>MTConnectDriverProbe.RedactedDisplay</c>)
|
|
/// while the driver itself logged and surfaced the raw string — and every request URI built from it.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// A URI with no user-info (and, for the configured string, no query or fragment) is returned <b>unchanged</b>,
|
|
/// so the common case — and every host key derived from it — is byte-identical to what it was. One that does
|
|
/// carry user-info loses it (and, for the configured string, its query / fragment, which a reverse proxy may use
|
|
/// for a token). A configured string that is not an absolute URI but contains an <c>@</c> is replaced wholesale:
|
|
/// there is no reliable way to find the credential in it.
|
|
/// </remarks>
|
|
internal static class MTConnectUriRedaction
|
|
{
|
|
/// <summary>Placeholder for a configured value that cannot be parsed and might carry a credential.</summary>
|
|
internal const string Unparseable = "(agentUri withheld: not an absolute URI)";
|
|
|
|
/// <summary>The display form of a configured <c>AgentUri</c>.</summary>
|
|
/// <param name="agentUri">The configured value.</param>
|
|
/// <returns>The value with any user-info (and query / fragment) removed.</returns>
|
|
public static string Redact(string? agentUri)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(agentUri))
|
|
{
|
|
return agentUri ?? string.Empty;
|
|
}
|
|
|
|
if (!Uri.TryCreate(agentUri.Trim(), UriKind.Absolute, out var uri))
|
|
{
|
|
return agentUri.Contains('@', StringComparison.Ordinal) ? Unparseable : agentUri;
|
|
}
|
|
|
|
if (uri.UserInfo.Length == 0 && uri.Query.Length == 0 && uri.Fragment.Length == 0)
|
|
{
|
|
return agentUri;
|
|
}
|
|
|
|
var port = uri.IsDefaultPort ? string.Empty : $":{uri.Port}";
|
|
var path = uri.AbsolutePath == "/" ? string.Empty : uri.AbsolutePath;
|
|
|
|
return $"{uri.Scheme}://{uri.Host}{port}{path}";
|
|
}
|
|
|
|
/// <summary>The display form of a request URI built from the <c>AgentUri</c> (its query is the request's own).</summary>
|
|
/// <param name="uri">The request URI.</param>
|
|
/// <returns>The URI as text, without user-info.</returns>
|
|
public static string Redact(Uri uri)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(uri);
|
|
if (!uri.IsAbsoluteUri || uri.UserInfo.Length == 0)
|
|
{
|
|
return uri.ToString();
|
|
}
|
|
|
|
return new UriBuilder(uri) { UserName = string.Empty, Password = string.Empty }.Uri.ToString();
|
|
}
|
|
}
|