f40409a688
Tracking IDs are review bookkeeping that rots in shipped source; the rationale they tagged is kept. Comment-only change (CommentChecker 458 -> 16 false positives).
103 lines
4.3 KiB
C#
103 lines
4.3 KiB
C#
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
|
|
|
namespace ZB.MOM.WW.MxGateway.Client.Tests;
|
|
|
|
/// <summary>
|
|
/// Unit tests for <see cref="MxGatewaySecretRedaction"/> — the exact-substring scrub applied to
|
|
/// diagnostic text and rebuilt exceptions before they leave the client on a failure path.
|
|
/// </summary>
|
|
public sealed class MxGatewaySecretRedactionTests
|
|
{
|
|
/// <summary>Test: Redact replaces every occurrence of secret.</summary>
|
|
[Fact]
|
|
public void Redact_ReplacesEveryOccurrenceOfSecret()
|
|
{
|
|
string result = MxGatewaySecretRedaction.Redact(
|
|
"pw=hunter2 retry pw=hunter2 again hunter2",
|
|
"hunter2");
|
|
|
|
Assert.DoesNotContain("hunter2", result, StringComparison.Ordinal);
|
|
Assert.Equal("pw=<redacted> retry pw=<redacted> again <redacted>", result);
|
|
}
|
|
|
|
/// <summary>Test: Redact scrubs both secrets when one is substring of the other.</summary>
|
|
[Fact]
|
|
public void Redact_ScrubsBothSecretsWhenOneIsSubstringOfTheOther()
|
|
{
|
|
// "secret" is a substring of "secretPassword"; both must be fully scrubbed regardless of
|
|
// supplied order — no residual leak of either verbatim value.
|
|
string result = MxGatewaySecretRedaction.Redact(
|
|
"a=secretPassword b=secret",
|
|
"secret",
|
|
"secretPassword");
|
|
|
|
Assert.DoesNotContain("secretPassword", result, StringComparison.Ordinal);
|
|
Assert.DoesNotContain("secret", result, StringComparison.Ordinal);
|
|
}
|
|
|
|
/// <summary>Test: Redact with null secrets array returns message unchanged.</summary>
|
|
[Fact]
|
|
public void Redact_WithNullSecretsArray_ReturnsMessageUnchanged()
|
|
{
|
|
const string message = "nothing to scrub here";
|
|
|
|
string result = MxGatewaySecretRedaction.Redact(message, null!);
|
|
|
|
Assert.Equal(message, result);
|
|
}
|
|
|
|
/// <summary>Test: Redact with empty secrets array returns message unchanged.</summary>
|
|
[Fact]
|
|
public void Redact_WithEmptySecretsArray_ReturnsMessageUnchanged()
|
|
{
|
|
const string message = "nothing to scrub here";
|
|
|
|
string result = MxGatewaySecretRedaction.Redact(message);
|
|
|
|
Assert.Equal(message, result);
|
|
}
|
|
|
|
/// <summary>Test: Redact ignores whitespace only secret.</summary>
|
|
[Fact]
|
|
public void Redact_IgnoresWhitespaceOnlySecret()
|
|
{
|
|
// A whitespace-only secret must not over-redact the internal spaces of the message.
|
|
const string message = "user operator logged in";
|
|
|
|
string result = MxGatewaySecretRedaction.Redact(message, " ");
|
|
|
|
Assert.Equal(message, result);
|
|
}
|
|
|
|
/// <summary>Test: Redacted preserves concrete subtype and does not chain secret bearing original.</summary>
|
|
[Fact]
|
|
public void Redacted_PreservesConcreteSubtypeAndDoesNotChainSecretBearingOriginal()
|
|
{
|
|
const string secret = "hunter2";
|
|
Exception transportCause = new InvalidOperationException("transport reset");
|
|
MxGatewaySessionException original = new(
|
|
$"session rejected credential '{secret}'",
|
|
"session-1",
|
|
"correlation-1",
|
|
new ProtocolStatus { Code = ProtocolStatusCode.SessionNotReady, Message = $"echoed '{secret}'" },
|
|
hResult: -1,
|
|
statuses: [new MxStatusProxy { DiagnosticText = $"denied '{secret}'" }],
|
|
innerException: transportCause);
|
|
|
|
MxGatewayException redacted = MxGatewaySecretRedaction.Redacted(original, secret);
|
|
|
|
// Concrete runtime type is preserved.
|
|
Assert.IsType<MxGatewaySessionException>(redacted);
|
|
// The secret is gone from the message and every structured accessor.
|
|
Assert.DoesNotContain(secret, redacted.Message, StringComparison.Ordinal);
|
|
Assert.DoesNotContain(secret, redacted.ToString(), StringComparison.Ordinal);
|
|
Assert.DoesNotContain(secret, redacted.ProtocolStatus!.Message, StringComparison.Ordinal);
|
|
Assert.All(redacted.Statuses, status =>
|
|
Assert.DoesNotContain(secret, status.DiagnosticText, StringComparison.Ordinal));
|
|
Assert.Contains("<redacted>", redacted.Message, StringComparison.Ordinal);
|
|
// The secret-bearing original is NOT chained; the original's transport cause is carried.
|
|
Assert.NotSame(original, redacted.InnerException);
|
|
Assert.Same(transportCause, redacted.InnerException);
|
|
}
|
|
}
|