// Copyright 2012-2025 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using Shouldly; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.Internal; /// /// Tests for server logging trace sanitization (RemovePassFromTrace, RemoveAuthTokenFromTrace). /// Mirrors server/log_test.go — TestNoPasswordsFromConnectTrace, TestRemovePassFromTrace, /// TestRemoveAuthTokenFromTrace. /// public class ServerLoggerTests { // --------------------------------------------------------------------------- // T:2020 — TestNoPasswordsFromConnectTrace // --------------------------------------------------------------------------- /// /// Mirrors TestNoPasswordsFromConnectTrace. /// Verifies that a CONNECT trace with a password or auth_token does not /// expose the secret value after sanitization. /// [Fact] // T:2020 public void NoPasswordsFromConnectTrace_ShouldSucceed() { const string connectWithPass = """CONNECT {"verbose":false,"pedantic":false,"user":"derek","pass":"s3cr3t","tls_required":false}"""; const string connectWithToken = """CONNECT {"verbose":false,"auth_token":"secret-token","tls_required":false}"""; ServerLogging.RemovePassFromTrace(connectWithPass).ShouldNotContain("s3cr3t"); ServerLogging.RemoveAuthTokenFromTrace(connectWithToken).ShouldNotContain("secret-token"); } // --------------------------------------------------------------------------- // T:2021 — TestRemovePassFromTrace // --------------------------------------------------------------------------- /// /// Mirrors TestRemovePassFromTrace — covers all test vectors from log_test.go. /// Each case verifies that RemovePassFromTrace redacts the first pass/password value /// with [REDACTED] while leaving other fields intact. /// [Theory] // T:2021 [InlineData( "user and pass", "CONNECT {\"user\":\"derek\",\"pass\":\"s3cr3t\"}\r\n", "CONNECT {\"user\":\"derek\",\"pass\":\"[REDACTED]\"}\r\n")] [InlineData( "user and pass extra space", "CONNECT {\"user\":\"derek\",\"pass\": \"s3cr3t\"}\r\n", "CONNECT {\"user\":\"derek\",\"pass\": \"[REDACTED]\"}\r\n")] [InlineData( "user and pass is empty", "CONNECT {\"user\":\"derek\",\"pass\":\"\"}\r\n", "CONNECT {\"user\":\"derek\",\"pass\":\"[REDACTED]\"}\r\n")] [InlineData( "user and pass is empty whitespace", "CONNECT {\"user\":\"derek\",\"pass\":\" \"}\r\n", "CONNECT {\"user\":\"derek\",\"pass\":\"[REDACTED]\"}\r\n")] [InlineData( "only pass", "CONNECT {\"pass\":\"s3cr3t\",}\r\n", "CONNECT {\"pass\":\"[REDACTED]\",}\r\n")] [InlineData( "complete connect", "CONNECT {\"echo\":true,\"verbose\":false,\"pedantic\":false,\"user\":\"foo\",\"pass\":\"s3cr3t\",\"tls_required\":false,\"name\":\"APM7JU94z77YzP6WTBEiuw\"}\r\n", "CONNECT {\"echo\":true,\"verbose\":false,\"pedantic\":false,\"user\":\"foo\",\"pass\":\"[REDACTED]\",\"tls_required\":false,\"name\":\"APM7JU94z77YzP6WTBEiuw\"}\r\n")] [InlineData( "user and pass are filtered", "CONNECT {\"user\":\"s3cr3t\",\"pass\":\"s3cr3t\"}\r\n", "CONNECT {\"user\":\"s3cr3t\",\"pass\":\"[REDACTED]\"}\r\n")] [InlineData( "single long password", "CONNECT {\"pass\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}\r\n", "CONNECT {\"pass\":\"[REDACTED]\"}\r\n")] public void RemovePassFromTrace_ShouldSucceed(string name, string input, string expected) { _ = name; // used for test display only ServerLogging.RemovePassFromTrace(input).ShouldBe(expected); } // --------------------------------------------------------------------------- // T:2022 — TestRemoveAuthTokenFromTrace // --------------------------------------------------------------------------- /// /// Mirrors TestRemoveAuthTokenFromTrace — covers representative test vectors /// from log_test.go. Each case verifies that RemoveAuthTokenFromTrace redacts /// the first auth_token value with [REDACTED]. /// [Theory] // T:2022 [InlineData( "user and auth_token", "CONNECT {\"user\":\"derek\",\"auth_token\":\"s3cr3t\"}\r\n", "CONNECT {\"user\":\"derek\",\"auth_token\":\"[REDACTED]\"}\r\n")] [InlineData( "user and auth_token extra space", "CONNECT {\"user\":\"derek\",\"auth_token\": \"s3cr3t\"}\r\n", "CONNECT {\"user\":\"derek\",\"auth_token\": \"[REDACTED]\"}\r\n")] [InlineData( "user and auth_token is empty", "CONNECT {\"user\":\"derek\",\"auth_token\":\"\"}\r\n", "CONNECT {\"user\":\"derek\",\"auth_token\":\"[REDACTED]\"}\r\n")] [InlineData( "only auth_token", "CONNECT {\"auth_token\":\"s3cr3t\",}\r\n", "CONNECT {\"auth_token\":\"[REDACTED]\",}\r\n")] [InlineData( "complete connect", "CONNECT {\"echo\":true,\"verbose\":false,\"pedantic\":false,\"auth_token\":\"s3cr3t\",\"tls_required\":false,\"name\":\"APM7JU94z77YzP6WTBEiuw\"}\r\n", "CONNECT {\"echo\":true,\"verbose\":false,\"pedantic\":false,\"auth_token\":\"[REDACTED]\",\"tls_required\":false,\"name\":\"APM7JU94z77YzP6WTBEiuw\"}\r\n")] [InlineData( "user and token are filtered", "CONNECT {\"user\":\"s3cr3t\",\"auth_token\":\"s3cr3t\"}\r\n", "CONNECT {\"user\":\"s3cr3t\",\"auth_token\":\"[REDACTED]\"}\r\n")] [InlineData( "single long token", "CONNECT {\"auth_token\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"}\r\n", "CONNECT {\"auth_token\":\"[REDACTED]\"}\r\n")] public void RemoveAuthTokenFromTrace_ShouldSucceed(string name, string input, string expected) { _ = name; // used for test display only ServerLogging.RemoveAuthTokenFromTrace(input).ShouldBe(expected); } }