fix(notifications): EWS sender review follow-ups — https guard, transient-path logging, test hardening

This commit is contained in:
Joseph Doherty
2026-08-10 06:29:37 -04:00
parent 08957cc907
commit 8657fae14f
2 changed files with 63 additions and 8 deletions
@@ -68,6 +68,19 @@ public sealed class EwsSoapMailSender : IEwsMailSender
{
ArgumentNullException.ThrowIfNull(request);
// Defense in depth: a Basic header is a replayable cleartext credential, so it never
// leaves this process over anything but https — not even if a misconfigured
// SmtpConfiguration row supplies an http:// URL. Config defects do not improve on
// retry, so this is permanent. Only the scheme and host appear in the message.
if (!string.Equals(request.Endpoint.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
{
var schemeError =
$"EWS endpoint '{request.Endpoint.Scheme}://{request.Endpoint.Host}' is not https; " +
"Basic credentials are only ever sent over https.";
_logger.LogError("Permanent EWS failure: {Detail}", schemeError);
throw new EwsPermanentException(schemeError);
}
var envelope = EwsSoapEnvelope.BuildCreateItem(
request.FromAddress, request.BccRecipients, request.Subject, request.Body);
@@ -114,7 +127,14 @@ public sealed class EwsSoapMailSender : IEwsMailSender
}
catch (Exception ex) when (ex is HttpRequestException or OperationCanceledException)
{
// Transport failure or our own timeout — availability-shaped, so retry.
// Transport failure or our own timeout — availability-shaped, so retry. Logged with
// the host and exception type only: the exception message is untrusted text and the
// recipient addresses are notification content.
_logger.LogWarning(
"Transient EWS failure contacting {EwsHost} ({ExceptionType}).",
request.Endpoint.Host,
ex.GetType().Name);
throw new EwsTransientException(
Scrub(
$"EWS request to {request.Endpoint.Host} failed: {ex.Message}",