namespace ScadaLink.NotificationService; /// /// NS-009: Scrubs SMTP credential secrets out of free text (typically exception /// messages echoed back by an SMTP server) before that text is written to a log. /// MailKit authentication exceptions can contain server responses that quote the /// supplied credentials; this prevents a password, client secret, or OAuth2 token /// from leaking into the operational logs. /// /// Public so the central Notification Outbox's EmailNotificationDeliveryAdapter /// can share this exact redaction logic rather than carry a divergent copy. /// /// public static class CredentialRedactor { private const string Mask = "***REDACTED***"; /// /// Returns with every secret component of the supplied /// colon-delimited credential string masked. /// /// The text to scrub (e.g. an exception message). /// /// The credential string in use — Basic Auth user:pass or OAuth2 /// tenantId:clientId:clientSecret. May be null. /// public static string Scrub(string? text, string? credentials) { if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(credentials)) { return text ?? string.Empty; } var result = text; // Mask each individual colon-delimited component (covers user, password, // tenant, clientId, clientSecret) and the whole packed string. Order longest // first so a component that is a substring of another is still fully masked. var parts = credentials.Split(':') .Where(p => p.Length >= 4) .Append(credentials) .Distinct() .OrderByDescending(p => p.Length); foreach (var part in parts) { result = result.Replace(part, Mask, StringComparison.Ordinal); } return result; } }