refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)

Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
Joseph Doherty
2026-05-28 09:37:45 -04:00
parent 6d87ee3c3b
commit 7b0b9c7365
1531 changed files with 11180 additions and 11054 deletions
@@ -0,0 +1,81 @@
namespace ZB.MOM.WW.ScadaBridge.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.
/// <para>
/// Public so the central Notification Outbox's <c>EmailNotificationDeliveryAdapter</c>
/// can share this exact redaction logic rather than carry a divergent copy.
/// </para>
/// </summary>
public 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>
/// <summary>
/// NS-025: minimum length for a colon-separated SECRET component to be
/// considered worth masking. Twelve characters is the standard heuristic
/// for "long enough to be a password / client secret"; shorter components
/// (e.g. a 4-char user name like <c>root</c>, or a 7-char "from" alias)
/// would mask too much unrelated diagnostic text if treated as secrets.
/// </summary>
private const int MinSecretLength = 12;
public static string Scrub(string? text, string? credentials)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(credentials))
{
return text ?? string.Empty;
}
var result = text;
// NS-025: redact only the obviously-secret slots — the LAST
// colon-separated component (the password in Basic, the client
// secret in OAuth2) and the whole packed string — not the user
// name / tenant id / client id. A short user name like "root" or
// a sender alias like "smtp" no longer becomes a global redaction
// token that eats unrelated path / error text.
var secretsToRedact = new List<string>();
// The full packed credential is always the most-sensitive shape.
secretsToRedact.Add(credentials);
// The trailing colon-component is the password / clientSecret slot.
// Only redact it if it's plausibly secret-shaped (>= MinSecretLength).
var parts = credentials.Split(':');
if (parts.Length >= 2)
{
var lastComponent = parts[^1];
if (lastComponent.Length >= MinSecretLength)
{
secretsToRedact.Add(lastComponent);
}
}
// Order longest first so a secret that is a substring of the packed
// string is still fully masked.
var ordered = secretsToRedact
.Distinct()
.OrderByDescending(s => s.Length);
foreach (var part in ordered)
{
result = result.Replace(part, Mask, StringComparison.Ordinal);
}
return result;
}
}