Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;

namespace Shesha.Email.Dtos
{
public class EmailWithAttachmentsDto: SendTestEmailInput
{
public string From { get; set; }
public List<IFormFile> Attachments { get; set; } = [];
}
Comment on lines +6 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add nullable annotation to From property.

The project has <Nullable>enable</Nullable>. The From property should be marked as string? if it can be null/empty, or initialized with a default value if required.

 public class EmailWithAttachmentsDto: SendTestEmailInput
 {
-    public string From { get; set; }
+    public string? From { get; set; }
     public List<IFormFile> Attachments { get; set; } = [];
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public class EmailWithAttachmentsDto: SendTestEmailInput
{
public string From { get; set; }
public List<IFormFile> Attachments { get; set; } = [];
}
public class EmailWithAttachmentsDto: SendTestEmailInput
{
public string? From { get; set; }
public List<IFormFile> Attachments { get; set; } = [];
}
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Email/Dtos/EmailWithAttachmentsDto.cs
around lines 6 to 10, the From property lacks a nullable annotation under
Nullable enable; either change its type to string? if it can be null/empty, or
assign a non-null default (for example = string.Empty) to keep it non-nullable;
update the declaration accordingly and ensure project nullable context remains
consistent.

}
240 changes: 240 additions & 0 deletions shesha-core/src/Shesha.Application/Email/MailKitEmailHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using MimeKit.Utils;
using NUglify;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Remove unused import.

NUglify is imported but not used anywhere in this file.

-using NUglify;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
using NUglify;
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Email/MailKitEmailHelper.cs around line 5,
the using directive for NUglify is unused; remove the line "using NUglify;" from
the top of the file and save, then rebuild or run static analysis to ensure no
other references remain.

using Shesha.Configuration;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SmtpClient = MailKit.Net.Smtp.SmtpClient;

namespace Shesha.Email
{
internal static class MailKitEmailHelper
{
public static MimeMessage ConvertToMimeMessage(MailMessage mail)
{
if (mail == null)
throw new ArgumentNullException(nameof(mail));

var message = new MimeMessage();

if (mail.From != null)
message.From.Add(CreateMailboxAddress(mail.From));

foreach (var address in mail.To.Cast<MailAddress>())
{
message.To.Add(CreateMailboxAddress(address));
}

foreach (var address in mail.CC.Cast<MailAddress>())
{
message.Cc.Add(CreateMailboxAddress(address));
}

foreach (var address in mail.Bcc.Cast<MailAddress>())
{
message.Bcc.Add(CreateMailboxAddress(address));
}

message.Subject = mail.Subject ?? string.Empty;

foreach (var headerKey in mail.Headers.AllKeys)
{
var value = mail.Headers[headerKey];
if (!string.IsNullOrEmpty(headerKey) && !string.IsNullOrEmpty(value))
{
message.Headers.Replace(headerKey, value);
}
}

var builder = new BodyBuilder();

var htmlView = mail.AlternateViews
.Cast<AlternateView>()
.FirstOrDefault(v => string.Equals(v.ContentType.MediaType.Split('/')[0], "text", StringComparison.OrdinalIgnoreCase)
&& string.Equals(v.ContentType.MediaType.Split('/')[1], "html", StringComparison.OrdinalIgnoreCase));

if (htmlView != null)
{
builder.HtmlBody = ReadAlternateView(htmlView);
AddLinkedResources(builder, htmlView);
}
else if (mail.IsBodyHtml)
{
builder.HtmlBody = mail.Body;
}
else
{
builder.TextBody = mail.Body;
}

foreach (var alternateView in mail.AlternateViews.Cast<AlternateView>())
{
if (alternateView == htmlView)
continue;

var alternateViewMediaType = alternateView.ContentType.MediaType;
var mediaTypeMain = alternateViewMediaType.Split('/')[0];
var mediaTypeSub = alternateViewMediaType.Split('/')[1];

if (string.Equals(mediaTypeMain, "text", StringComparison.OrdinalIgnoreCase)
&& string.Equals(mediaTypeSub, "plain", StringComparison.OrdinalIgnoreCase))
{
builder.TextBody = ReadAlternateView(alternateView);
}
}

foreach (var attachment in mail.Attachments.Cast<Attachment>())
{
builder.Attachments.Add(CreateAttachmentPart(attachment));
}

message.Body = builder.ToMessageBody();

return message;
}

public static async Task SendAsync(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default)
{
if (settings == null)
throw new ArgumentNullException(nameof(settings));

using var smtpClient = new SmtpClient();
await smtpClient.ConnectAsync(settings.Host, settings.Port, GetSecureSocketOption(settings), cancellationToken).ConfigureAwait(false);

var credential = CreateCredential(settings);
if (credential != null)
{
await smtpClient.AuthenticateAsync(credential, cancellationToken).ConfigureAwait(false);
}

await smtpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
await smtpClient.DisconnectAsync(true, cancellationToken).ConfigureAwait(false);
}

public static void Send(MimeMessage message, SmtpSettings settings, CancellationToken cancellationToken = default)
{
if (settings == null)
throw new ArgumentNullException(nameof(settings));

using var smtpClient = new SmtpClient();
smtpClient.Connect(settings.Host, settings.Port, GetSecureSocketOption(settings), cancellationToken);

var credential = CreateCredential(settings);
if (credential != null)
{
smtpClient.Authenticate(credential, cancellationToken);
}

smtpClient.Send(message, cancellationToken);
smtpClient.Disconnect(true, cancellationToken);
}

private static void AddLinkedResources(BodyBuilder builder, AlternateView view)
{
foreach (var resource in view.LinkedResources.Cast<LinkedResource>())
{
builder.LinkedResources.Add(CreateLinkedResource(resource));
}
}

private static MimePart CreateAttachmentPart(Attachment attachment)
{
var contentType = attachment.ContentType.MediaType;
var mediaType = contentType.Split('/')[0] ?? "application";
var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";

var mimePart = new MimePart(mediaType, mediaSubType)
{
Content = new MimeContent(attachment.ContentStream),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = attachment.Name
};

// The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
return mimePart;
}

private static MimeEntity CreateLinkedResource(LinkedResource resource)
{
var contentType = resource.ContentType.MediaType;
var mediaType = contentType.Split('/')[0] ?? "application";
var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";
using MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
var mimePart = new MimePart(mediaType, mediaSubType)
{
Content = new MimeContent(memoryStream),
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
ContentTransferEncoding = ContentEncoding.Base64,
ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId,
FileName = resource.ContentId
};

// The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
return mimePart;
}
Comment on lines +166 to +183
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: using disposes MemoryStream before MimeContent can use it.

The using keyword on line 171 causes the MemoryStream to be disposed when the method returns, but MimeContent requires the stream to remain open for later serialization. This will cause runtime failures when sending emails with linked resources.

 private static MimeEntity CreateLinkedResource(LinkedResource resource)
 {
     var contentType = resource.ContentType.MediaType;
     var mediaType = contentType.Split('/')[0] ?? "application";
     var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";
-    using MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
+    MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
     var mimePart = new MimePart(mediaType, mediaSubType)
     {
         Content = new MimeContent(memoryStream),
         ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
         ContentTransferEncoding = ContentEncoding.Base64,
         ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId,
         FileName = resource.ContentId
     };

     // The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
     return mimePart;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static MimeEntity CreateLinkedResource(LinkedResource resource)
{
var contentType = resource.ContentType.MediaType;
var mediaType = contentType.Split('/')[0] ?? "application";
var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";
using MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
var mimePart = new MimePart(mediaType, mediaSubType)
{
Content = new MimeContent(memoryStream),
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
ContentTransferEncoding = ContentEncoding.Base64,
ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId,
FileName = resource.ContentId
};
// The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
return mimePart;
}
private static MimeEntity CreateLinkedResource(LinkedResource resource)
{
var contentType = resource.ContentType.MediaType;
var mediaType = contentType.Split('/')[0] ?? "application";
var mediaSubType = contentType.Split('/')[1] ?? "octet-stream";
MemoryStream memoryStream = CopyToMemoryStream(resource.ContentStream);
var mimePart = new MimePart(mediaType, mediaSubType)
{
Content = new MimeContent(memoryStream),
ContentDisposition = new ContentDisposition(ContentDisposition.Inline),
ContentTransferEncoding = ContentEncoding.Base64,
ContentId = string.IsNullOrWhiteSpace(resource.ContentId) ? MimeUtils.GenerateMessageId() : resource.ContentId,
FileName = resource.ContentId
};
// The created MemoryStream is now owned by MimeContent, which will dispose it when disposed.
return mimePart;
}
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Email/MailKitEmailHelper.cs around lines
166 to 183, the MemoryStream is wrapped in a using which disposes it before
MimeContent can use it; remove the using so the MemoryStream remains alive and
is owned/disposed by MimeContent (i.e., create the MemoryStream, pass it into
new MimeContent without disposing here), and ensure any callers rely on
MimeContent to dispose the stream.


private static MemoryStream CopyToMemoryStream(Stream stream)
{
var memoryStream = new MemoryStream();
if (stream.CanSeek)
stream.Position = 0;

stream.CopyTo(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}

private static MailboxAddress CreateMailboxAddress(MailAddress address)
{
return string.IsNullOrEmpty(address.DisplayName)
? MailboxAddress.Parse(address.Address)
: new MailboxAddress(address.DisplayName, address.Address);
}

private static string ReadAlternateView(AlternateView view)
{
var encoding = !string.IsNullOrWhiteSpace(view.ContentType.CharSet)
? Encoding.GetEncoding(view.ContentType.CharSet)
: Encoding.UTF8;

if (view.ContentStream.CanSeek)
view.ContentStream.Position = 0;

using var reader = new StreamReader(view.ContentStream, encoding, detectEncodingFromByteOrderMarks: true, leaveOpen: true);
var content = reader.ReadToEnd();
if (view.ContentStream.CanSeek)
view.ContentStream.Position = 0;

return content;
}

private static NetworkCredential? CreateCredential(SmtpSettings settings)
{
if (string.IsNullOrWhiteSpace(settings.UserName))
return null;

return string.IsNullOrWhiteSpace(settings.Domain)
? new NetworkCredential(settings.UserName, settings.Password)
: new NetworkCredential(settings.UserName, settings.Password, settings.Domain);
}

private static SecureSocketOptions GetSecureSocketOption(SmtpSettings settings)
{
if (!settings.EnableSsl)
return SecureSocketOptions.None;

return settings.Port == 465
? SecureSocketOptions.SslOnConnect
: SecureSocketOptions.StartTls;
}
Comment on lines +230 to +238
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding explicit SSL mode configuration for edge cases.

The port-based heuristic (465 = implicit SSL, others = StartTls) is reasonable for common configurations, but some SMTP servers may have non-standard setups. Consider adding an explicit SslMode option in SmtpSettings for full configurability.

🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Email/MailKitEmailHelper.cs around lines
230 to 238, the method currently infers SSL behavior solely from EnableSsl and
port which fails for non‑standard servers; add an explicit SslMode property
(enum: None, SslOnConnect, StartTls, Auto/Default) to SmtpSettings and update
GetSecureSocketOption to prefer SmtpSettings.SslMode when set, mapping the enum
to the corresponding SecureSocketOptions values and only falling back to the
existing port-based heuristic when SslMode is unspecified or set to
Auto/Default.

}
}
41 changes: 10 additions & 31 deletions shesha-core/src/Shesha.Application/Email/SheshaEmailSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;

namespace Shesha.Email
{
Expand Down Expand Up @@ -78,10 +77,8 @@ protected override async Task SendEmailAsync(MailMessage mail)
if (!PrepareAndCheckMail(mail, smtpSettings))
return;

using (var smtpClient = GetSmtpClient(smtpSettings))
{
await smtpClient.SendMailAsync(mail);
}
using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings);
}

protected override void SendEmail(MailMessage mail)
Expand All @@ -95,10 +92,8 @@ protected override void SendEmail(MailMessage mail)
if (!PrepareAndCheckMail(mail, smtpSettings))
return;

using (var smtpClient = GetSmtpClient(smtpSettings))
{
smtpClient.Send(mail);
}
using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
MailKitEmailHelper.Send(mimeMessage, smtpSettings);
}

#region private methods
Expand Down Expand Up @@ -197,22 +192,6 @@ private void NormalizeMailSender(MailMessage mail, SmtpSettings smtpSettings)
}
}

/// <summary>
/// Returns SmtpClient configured according to the current application settings
/// </summary>
private SmtpClient GetSmtpClient(SmtpSettings smtpSettings)
{
var client = new SmtpClient(smtpSettings.Host, smtpSettings.Port)
{
EnableSsl = smtpSettings.EnableSsl,
Credentials = string.IsNullOrWhiteSpace(smtpSettings.Domain)
? new NetworkCredential(smtpSettings.UserName, smtpSettings.Password)
: new NetworkCredential(smtpSettings.UserName, smtpSettings.Password, smtpSettings.Domain)
};

return client;
}

#endregion
}
}
#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
using Shesha.Configuration;
using Shesha.Configuration.Email;
using Shesha.Domain;
using Shesha.Email;
using Shesha.Email.Dtos;
using Shesha.Notifications.Dto;
using Shesha.Notifications.MessageParticipants;
using Shesha.Utilities;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;

Expand Down Expand Up @@ -86,10 +86,9 @@ private async Task SendEmailAsync(MailMessage mail)
{
try
{
using (var smtpClient = await GetSmtpClientAsync())
{
smtpClient.Send(mail);
}
using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync();
await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings);
Comment on lines +89 to +91
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Reorder operations to fail fast before expensive conversion.

The MimeMessage conversion (line 89) happens before fetching SmtpSettings (line 90). If settings retrieval fails, the conversion work is wasted. Consider fetching settings first.

 private async Task SendEmailAsync(MailMessage mail)
 {
     try
     {
-        using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
         var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync();
+        using var mimeMessage = MailKitEmailHelper.ConvertToMimeMessage(mail);
         await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings);
     }
🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Notifications/Emails/EmailChannelSender.cs
around lines 89-91, the code converts the Mail object to a MimeMessage before
retrieving SMTP settings, causing wasted work if settings retrieval fails;
reorder the operations to first await
_emailSettings.SmtpSettings.GetValueAsync(), validate or fail early on the
settings, then call MailKitEmailHelper.ConvertToMimeMessage(mail) and finally
await MailKitEmailHelper.SendAsync(mimeMessage, smtpSettings).

}
catch (Exception ex)
{
Expand All @@ -98,28 +97,6 @@ private async Task SendEmailAsync(MailMessage mail)
}
}

private async Task<SmtpClient> GetSmtpClientAsync()
{
var smtpSettings = await _emailSettings.SmtpSettings.GetValueAsync();
return GetSmtpClient(smtpSettings);
}

/// <summary>
/// Returns SmtpClient configured according to the current application settings
/// </summary>
private SmtpClient GetSmtpClient(SmtpSettings smtpSettings)
{
var client = new SmtpClient(smtpSettings.Host, smtpSettings.Port)
{
EnableSsl = smtpSettings.EnableSsl,
Credentials = string.IsNullOrWhiteSpace(smtpSettings.Domain)
? new NetworkCredential(smtpSettings.UserName, smtpSettings.Password)
: new NetworkCredential(smtpSettings.UserName, smtpSettings.Password, smtpSettings.Domain)
};

return client;
}

/// <summary>
///
/// </summary>
Expand Down
13 changes: 7 additions & 6 deletions shesha-core/src/Shesha.Application/Shesha.Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="DocumentFormat.OpenXml" Version="3.0.0" />
<PackageReference Include="Hangfire.Core" Version="1.8.6" />
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MediaTypeMap.Core" Version="2.3.3" />
<PackageReference Include="Hangfire.Core" Version="1.8.6" />
<PackageReference Include="IDisposableAnalyzers" Version="4.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MailKit" Version="4.7.0" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

MailKit 4.7.0 latest version NuGet

💡 Result:

No — MailKit 4.7.0 is not the latest. The current NuGet latest version is 4.14.1 (install with: dotnet add package MailKit --version 4.14.1). [1]

Sources:
[1] NuGet: MailKit package (4.14.1).


🌐 Web query:

MailKit security vulnerabilities CVE 2024 2025

💡 Result:

Summary — notable 2024–2025 issues affecting MailKit (or its dependencies)

  1. Denial‑of‑Service via vulnerable System.Formats.Asn1 (affects MimeKit used by MailKit). Patched in MimeKit 4.7.1 (upgrade MailKit/MimeKit). Sources: GitHub advisory / OSS databases. [1][2]

  2. CVE‑2024‑38095 — .NET System.Formats.Asn1 / ASN.1 parsing DoS (excessive CPU). This is a .NET runtime/component vulnerability that can impact libraries (including MimeKit/MailKit S/MIME operations) until patched in .NET; apply Microsoft/.NET updates. [3]

  3. MailKit repository security page: no MailKit‑specific published security advisories (report via private advisory); keep MailKit/MimeKit up to latest. [4]

If you want, I can:

  • list exact vulnerable version ranges and recommended fixed versions, or
  • produce upgrade/mitigation steps for your project (NuGet package commands and minimal code/workarounds).

Sources:
[1] GitLab/GHSA advisory for MimeKit (GHSA‑gmc6‑fwg3‑75m5) — fixed in MimeKit 4.7.1. (advisories.gitlab.com)
[2] OSV / GitHub advisory summary (GHSA‑gmc6‑fwg3‑75m5). (osv.dev)
[3] NVD entry for CVE‑2024‑38095 (.NET ASN.1 DoS). (nvd.nist.gov)
[4] MailKit GitHub security policy / page (no published advisories). (github.com)

Citations:


Upgrade MailKit from 4.7.0 to 4.14.1 to address security vulnerabilities.

MailKit 4.7.0 is outdated; the current NuGet latest is 4.14.1. More critically, MailKit 4.7.0 depends on a vulnerable version of MimeKit that is affected by a Denial-of-Service vulnerability (GHSA-gmc6-fwg3-75m5) caused by System.Formats.Asn1 ASN.1 parsing, which was patched in MimeKit 4.7.1. Additionally, CVE-2024-38095 impacts ASN.1 operations used in S/MIME processing. Upgrade to MailKit 4.14.1 immediately.

🤖 Prompt for AI Agents
In shesha-core/src/Shesha.Application/Shesha.Application.csproj around line 169,
the project references MailKit 4.7.0 which depends on a vulnerable MimeKit;
update the PackageReference Version attribute to 4.14.1 to pull in the patched
MimeKit and mitigate ASN.1/S-MIME DoS vulnerabilities; after updating, restore
NuGet packages and run the test suite/CI to ensure no breaking changes.

<PackageReference Include="MediaTypeMap.Core" Version="2.3.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.13.2">
<PrivateAssets>all</PrivateAssets>
Expand Down
Loading