fix: redact bot secrets in startup logs
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Npgsql;
|
||||
|
||||
namespace GmRelay.Bot.Infrastructure.Logging;
|
||||
|
||||
public static partial class SecretRedactor
|
||||
{
|
||||
public static string RedactConnectionString(string? connectionString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var builder = new NpgsqlConnectionStringBuilder(connectionString);
|
||||
if (!string.IsNullOrWhiteSpace(builder.Password))
|
||||
{
|
||||
builder.Password = "***";
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
return RedactText(connectionString);
|
||||
}
|
||||
}
|
||||
|
||||
public static string RedactText(string? text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return SecretKeyValueRegex().Replace(
|
||||
text,
|
||||
static match => $"{match.Groups["key"].Value}={GetRedactedValue()}");
|
||||
}
|
||||
|
||||
private static string GetRedactedValue() => "***";
|
||||
|
||||
[GeneratedRegex(@"(?<key>password|pwd|passwd|token|secret|api[-_]?key)\s*=\s*(?<value>[^;\s,]+)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex SecretKeyValueRegex();
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using GmRelay.Bot.Features.Reminders.SendJoinLink;
|
||||
using GmRelay.Bot.Features.Sessions.CreateSession;
|
||||
using GmRelay.Bot.Features.Sessions.RescheduleSession;
|
||||
using GmRelay.Bot.Infrastructure.Database;
|
||||
using GmRelay.Bot.Infrastructure.Logging;
|
||||
using GmRelay.Bot.Infrastructure.Scheduling;
|
||||
using GmRelay.Bot.Infrastructure.Telegram;
|
||||
using Npgsql;
|
||||
@@ -20,11 +21,16 @@ builder.AddServiceDefaults();
|
||||
builder.Services.AddSingleton<NpgsqlDataSource>(sp =>
|
||||
{
|
||||
var config = sp.GetRequiredService<IConfiguration>();
|
||||
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
|
||||
var connectionString = config.GetConnectionString("gmrelaydb")
|
||||
?? throw new InvalidOperationException(
|
||||
"ConnectionStrings:gmrelaydb is required. Set via environment variable ConnectionStrings__gmrelaydb.");
|
||||
|
||||
Console.WriteLine($"[DBG] Master ConnectionString => {connectionString}");
|
||||
var logger = loggerFactory.CreateLogger("GmRelay.Bot.Startup");
|
||||
logger.LogInformation(
|
||||
"Configured PostgreSQL data source with connection string {ConnectionString}",
|
||||
SecretRedactor.RedactConnectionString(connectionString));
|
||||
|
||||
return NpgsqlDataSource.Create(connectionString);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using GmRelay.Bot.Infrastructure.Logging;
|
||||
|
||||
namespace GmRelay.Bot.Tests.Infrastructure.Logging;
|
||||
|
||||
public sealed class SecretRedactorTests
|
||||
{
|
||||
[Fact]
|
||||
public void RedactConnectionString_ShouldMaskDatabasePassword()
|
||||
{
|
||||
var result = SecretRedactor.RedactConnectionString(
|
||||
"Host=localhost;Port=5432;Database=gmrelay;Username=gmrelay;Password=super-secret");
|
||||
|
||||
Assert.Contains("Password=***", result);
|
||||
Assert.DoesNotContain("super-secret", result);
|
||||
Assert.Contains("Host=localhost", result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RedactText_ShouldMaskKnownSecretKeys()
|
||||
{
|
||||
var result = SecretRedactor.RedactText(
|
||||
"Password=super-secret Token=telegram-token apiKey=service-key");
|
||||
|
||||
Assert.DoesNotContain("super-secret", result);
|
||||
Assert.DoesNotContain("telegram-token", result);
|
||||
Assert.DoesNotContain("service-key", result);
|
||||
Assert.Contains("Password=***", result);
|
||||
Assert.Contains("Token=***", result);
|
||||
Assert.Contains("apiKey=***", result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user