fix(logging): fail-closed bearer redaction; hoist per-request logger creation
This commit is contained in:
@@ -24,6 +24,67 @@ public sealed class GatewayLogRedactorTests
|
||||
Assert.DoesNotContain("super_secret_value", redacted);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a bearer credential the gateway does not issue is redacted too. A client that
|
||||
/// pastes a JWT (or any other token) into the authorization header must not have it logged.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RedactClientIdentity_RedactsNonGatewayBearerCredential()
|
||||
{
|
||||
const string token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.c2lnbmF0dXJl";
|
||||
|
||||
string? redacted = GatewayLogRedactor.RedactClientIdentity($"Bearer {token}");
|
||||
|
||||
Assert.Equal("Bearer [redacted]", redacted);
|
||||
Assert.DoesNotContain(token, redacted, StringComparison.Ordinal);
|
||||
Assert.DoesNotContain("eyJ", redacted, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a gateway API key keeps its key-id shape so an operator can still tell keys apart.</summary>
|
||||
[Fact]
|
||||
public void RedactClientIdentity_PreservesGatewayKeyIdShape()
|
||||
{
|
||||
string? redacted = GatewayLogRedactor.RedactClientIdentity("Bearer mxgw_operator01_super-secret");
|
||||
|
||||
Assert.Equal("Bearer mxgw_operator01_[redacted]", redacted);
|
||||
Assert.DoesNotContain("super-secret", redacted, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that anything not recognized as a gateway API key fails closed: the scheme word
|
||||
/// survives only when it looks like an auth scheme, and the credential never does.
|
||||
/// </summary>
|
||||
/// <param name="clientIdentity">The raw client identity value.</param>
|
||||
/// <param name="expected">The expected redacted value.</param>
|
||||
[Theory]
|
||||
[InlineData("Bearer", "[redacted]")]
|
||||
[InlineData("Bearer ", "[redacted]")]
|
||||
[InlineData("Basic dXNlcjpwYXNzd29yZA==", "Basic [redacted]")]
|
||||
[InlineData("Negotiate YIIJvwYGKwYBBQUCoIIJ", "Negotiate [redacted]")]
|
||||
[InlineData("mxgw_operator01_super-secret", "[redacted]")]
|
||||
[InlineData("mxgw_operator01_super-secret trailing", "[redacted]")]
|
||||
[InlineData("Bearer mxgw_operator01", "Bearer mxgw_[redacted]")]
|
||||
[InlineData("Bearer mxgw_", "Bearer mxgw_[redacted]")]
|
||||
[InlineData("Bearer mxgw__super-secret", "Bearer mxgw_[redacted]")]
|
||||
[InlineData("bearer mxgw_operator01_super-secret", "bearer mxgw_operator01_[redacted]")]
|
||||
[InlineData("anonymous", "[redacted]")]
|
||||
[InlineData("some random junk", "[redacted]")]
|
||||
public void RedactClientIdentity_FailsClosedForUnrecognizedCredentials(string clientIdentity, string expected)
|
||||
{
|
||||
Assert.Equal(expected, GatewayLogRedactor.RedactClientIdentity(clientIdentity));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a blank client identity is passed through — there is nothing to redact.</summary>
|
||||
/// <param name="clientIdentity">The raw client identity value.</param>
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void RedactClientIdentity_PassesThroughBlankValues(string? clientIdentity)
|
||||
{
|
||||
Assert.Equal(clientIdentity, GatewayLogRedactor.RedactClientIdentity(clientIdentity));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that IsCredentialBearingCommand identifies credential-bearing MXAccess commands.</summary>
|
||||
/// <param name="commandMethod">Name of the MXAccess command method.</param>
|
||||
[Theory]
|
||||
|
||||
Reference in New Issue
Block a user