fix(notification-service): resolve NotificationService-005..009 — explicit TLS modes, per-credential token cache, timeout/throttle, address validation, credential redaction
This commit is contained in:
48
src/ScadaLink.NotificationService/CredentialRedactor.cs
Normal file
48
src/ScadaLink.NotificationService/CredentialRedactor.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace ScadaLink.NotificationService;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
internal static class CredentialRedactor
|
||||
{
|
||||
private const string Mask = "***REDACTED***";
|
||||
|
||||
/// <summary>
|
||||
/// Returns <paramref name="text"/> with every secret component of the supplied
|
||||
/// colon-delimited credential string masked.
|
||||
/// </summary>
|
||||
/// <param name="text">The text to scrub (e.g. an exception message).</param>
|
||||
/// <param name="credentials">
|
||||
/// The credential string in use — Basic Auth <c>user:pass</c> or OAuth2
|
||||
/// <c>tenantId:clientId:clientSecret</c>. May be null.
|
||||
/// </param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user