2858e42e80
Claude-Session: https://claude.ai/code/session_016J6isfcgqy4SB2RK3ZvFvT
107 lines
3.7 KiB
C#
107 lines
3.7 KiB
C#
using ZB.MOM.WW.MxGateway.Server.Diagnostics;
|
|
|
|
namespace ZB.MOM.WW.MxGateway.Tests.Diagnostics;
|
|
|
|
public sealed class GatewayLogRedactorSeamTests
|
|
{
|
|
/// <summary>Redacting a log property containing a bearer API key masks the secret while preserving the key id prefix.</summary>
|
|
[Fact]
|
|
public void Redact_MasksApiKeyInClientIdentity()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new() { ["ClientIdentity"] = "Bearer mxgw_operator01_super-secret" };
|
|
redactor.Redact(props);
|
|
Assert.Equal("Bearer mxgw_operator01_[redacted]", props["ClientIdentity"]);
|
|
}
|
|
|
|
/// <summary>A command value carried by a credential-bearing command never reaches a sink in the clear.</summary>
|
|
[Fact]
|
|
public void Redact_MasksCommandValueForCredentialBearingCommand()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new()
|
|
{
|
|
["CommandMethod"] = "AuthenticateUser",
|
|
["CommandValue"] = "operator01:hunter2"
|
|
};
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Value logging is off by default, so an ordinary command value is masked too — the seam
|
|
/// exposes no opt-in, matching <see cref="GatewayLogRedactor.RedactCommandValue"/>'s default.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Redact_MasksCommandValueForOrdinaryCommand()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new()
|
|
{
|
|
["CommandMethod"] = "Read",
|
|
["CommandValue"] = "plaintext-tag-value"
|
|
};
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
|
|
}
|
|
|
|
/// <summary>A command value with no accompanying method is still masked — an unknown method cannot be cleared.</summary>
|
|
[Fact]
|
|
public void Redact_MasksCommandValueWhenCommandMethodAbsent()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new() { ["CommandValue"] = "plaintext-tag-value" };
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.Equal(GatewayLogRedactor.RedactedValue, props["CommandValue"]);
|
|
}
|
|
|
|
/// <summary>A null command value stays null rather than becoming the redaction placeholder.</summary>
|
|
[Fact]
|
|
public void Redact_LeavesNullCommandValueNull()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new() { ["CommandValue"] = null };
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.Null(props["CommandValue"]);
|
|
}
|
|
|
|
/// <summary>An event carrying no command value is untouched — the seam must not invent the property.</summary>
|
|
[Fact]
|
|
public void Redact_DoesNotAddCommandValueWhenAbsent()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new() { ["ClientIdentity"] = "anonymous" };
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.False(props.ContainsKey("CommandValue"));
|
|
}
|
|
|
|
/// <summary>Any property whose name contains "password" is masked regardless of casing.</summary>
|
|
[Fact]
|
|
public void Redact_PasswordNamedProperty_IsRedacted()
|
|
{
|
|
GatewayLogRedactorSeam redactor = new();
|
|
Dictionary<string, object?> props = new()
|
|
{
|
|
["OperatorPassword"] = "pw-secret",
|
|
["verify_user_PASSWORD"] = "pw-secret",
|
|
["OperatorUser"] = "alice",
|
|
};
|
|
|
|
redactor.Redact(props);
|
|
|
|
Assert.Equal("[redacted]", props["OperatorPassword"]);
|
|
Assert.Equal("[redacted]", props["verify_user_PASSWORD"]);
|
|
Assert.Equal("alice", props["OperatorUser"]);
|
|
}
|
|
}
|