Skip to content

Commit

Permalink
MailKit tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
NachoEchevarria committed Feb 10, 2025
1 parent 16c3d5a commit ecdd6cd
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// <copyright file="AmazonSimpleEmailAspect.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>
#nullable enable

using System;
using Datadog.Trace.AppSec;
using Datadog.Trace.Iast.Dataflow;

namespace Datadog.Trace.Iast.Aspects;

/// <summary> Email html injection class aspect </summary>
[AspectClass("AWSSDK.SimpleEmail", AspectType.Sink, VulnerabilityType.EmailHtmlInjection)]
[global::System.ComponentModel.Browsable(false)]
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
public class AmazonSimpleEmailAspect
{
/// <summary>
/// Launches a email html injection vulnerability if the email body is tainted, it's not escaped and the email is html compatible.
/// No need to instrument methods Send(string from, string recipients, string subject, string body) and similar
/// since those methods would send the email as plain text.
/// </summary>
/// <param name="message">the email message that is going to be sent</param>
/// <returns>the MailMessage</returns>
[AspectMethodInsertBefore("Amazon.SimpleEmail.AmazonSimpleEmailServiceClient::SendEmailAsync(Amazon.SimpleEmail.Model.SendEmailRequest,System.Threading.CancellationToken", 1)]
public static object? Send(object? message)
{
try
{
IastModule.OnEmailHtmlInjection(message, EmailInjectionType.AmazonSimpleEmail);
return message;
}
catch (Exception ex) when (ex is not BlockException)
{
IastModule.LogAspectException(ex, $"{nameof(MailkitAspect)}.{nameof(Send)}");
return message;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <copyright file="ISendEmailRequest.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using Datadog.Trace.DuckTyping;

namespace Datadog.Trace.Iast.Aspects;

#nullable enable

internal interface ISendEmailRequest
{
IMessage Message { get; }
}

internal interface IMessage
{
IBody Body { get; }
}

internal interface IBody
{
IHtml Html { get; }
}

internal interface IHtml
{
IContent Content { get; }
}

internal interface IContent
{
string Data { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

using Datadog.Trace.DuckTyping;

namespace Datadog.Trace.Iast.Aspects;

#nullable enable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class SmtpClientAspect
{
try
{
IastModule.OnEmailHtmlInjection(message);
IastModule.OnEmailHtmlInjection(message, EmailInjectionType.SystemNetMail);
return message;
}
catch (Exception ex) when (ex is not BlockException)
Expand Down
15 changes: 15 additions & 0 deletions tracer/src/Datadog.Trace/Iast/EmailInjectionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// <copyright file="EmailInjectionType.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>

#nullable enable

namespace Datadog.Trace.Iast;

internal enum EmailInjectionType
{
AmazonSimpleEmail,
MailKit,
SystemNetMail
}
48 changes: 44 additions & 4 deletions tracer/src/Datadog.Trace/Iast/IastModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,25 @@ internal static IastModuleResponse OnXpathInjection(string xpath)
}
}

internal static void OnEmailHtmlInjection(object? message)
internal static void OnEmailHtmlInjection(string text)
{
if (!Iast.Instance.Settings.Enabled)
{
return;
}

OnExecutedSinkTelemetry(IastInstrumentedSinks.EmailHtmlInjection);

if (string.IsNullOrEmpty(text))
{
return;
}

// We use the same secure marks as XSS, but excluding db sources
GetScope(text, IntegrationId.EmailHtmlInjection, VulnerabilityTypeName.EmailHtmlInjection, OperationNameEmailHtmlInjection, taintValidator: Always, safeSources: _dbSources, exclusionSecureMarks: SecureMarks.Xss);
}

internal static void OnEmailHtmlInjection(object? message, EmailInjectionType type)
{
if (!Iast.Instance.Settings.Enabled)
{
Expand All @@ -855,15 +873,37 @@ internal static void OnEmailHtmlInjection(object? message)
return;
}

var messageDuck = message.DuckCast<IMailMessage>();
ExtractProperties(message, type, out var body, out var isHtml);

if (messageDuck?.IsBodyHtml is not true || string.IsNullOrEmpty(messageDuck.Body))
if (isHtml is not true || string.IsNullOrEmpty(body))
{
return;
}

// We use the same secure marks as XSS, but excluding db sources
GetScope(messageDuck.Body, IntegrationId.EmailHtmlInjection, VulnerabilityTypeName.EmailHtmlInjection, OperationNameEmailHtmlInjection, taintValidator: Always, safeSources: _dbSources, exclusionSecureMarks: SecureMarks.Xss);
GetScope(body!, IntegrationId.EmailHtmlInjection, VulnerabilityTypeName.EmailHtmlInjection, OperationNameEmailHtmlInjection, taintValidator: Always, safeSources: _dbSources, exclusionSecureMarks: SecureMarks.Xss);
}

private static void ExtractProperties(object mail, EmailInjectionType type, out string? body, out bool? isHtml)
{
switch (type)
{
case EmailInjectionType.SystemNetMail:
var mailMessage = mail.DuckCast<IMailMessage>();
body = mailMessage?.Body;
isHtml = mailMessage?.IsBodyHtml;
break;
case EmailInjectionType.AmazonSimpleEmail:
var sendEmailRequest = mail.DuckCast<ISendEmailRequest>();
body = sendEmailRequest?.Message?.Body?.Html?.Content?.Data;
isHtml = !string.IsNullOrEmpty(body);
break;
default:
Log.Error("Error while checking for email injection type.");
body = string.Empty;
isHtml = false;
break;
}
}

public static void LogAspectException(Exception ex, string aspectInfo)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ int GeneratedDefinitions::InitCallSites(UINT32 enabledCategories, UINT32 platfor
{
std::vector<WCHAR*> callSites =
{
(WCHAR*)WStr("[AspectClass(\"AWSSDK.SimpleEmail\",[None],Sink,[EmailHtmlInjection])] Datadog.Trace.Iast.Aspects.AmazonSimpleEmailAspect 4"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"Amazon.SimpleEmail.AmazonSimpleEmailServiceClient::SendEmailAsync(Amazon.SimpleEmail.Model.SendEmailRequest,System.Threading.CancellationToken\",\"\",[1],[False],[None],Default,[])] Send(System.Object) 15"),
(WCHAR*)WStr("[AspectClass(\"EntityFramework\",[None],Sink,[SqlInjection])] Datadog.Trace.Iast.Aspects.EntityCommandAspect 12"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"System.Data.Entity.Core.EntityClient.EntityCommand::ExecuteReader(System.Data.CommandBehavior)\",\"\",[1],[False],[None],Default,[])] ReviewSqlCommand(System.Object) 15"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"System.Data.Entity.Core.EntityClient.EntityCommand::ExecuteReaderAsync(System.Data.CommandBehavior)\",\"\",[1],[False],[None],Default,[])] ReviewSqlCommand(System.Object) 15"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"System.Data.Entity.Core.EntityClient.EntityCommand::ExecuteReaderAsync(System.Data.CommandBehavior,System.Threading.CancellationToken)\",\"\",[2],[False],[None],Default,[])] ReviewSqlCommand(System.Object) 15"),
(WCHAR*)WStr("[AspectClass(\"Mailkit\",[None],Sink,[EmailHtmlInjection])] Datadog.Trace.Iast.Aspects.MailkitAspect 4"),
(WCHAR*)WStr(" [AspectMethodReplace(\"MimeKit.TextPart::SetText(System.String,System.String)\",\"\",[0],[False],[None],Default,[])] SetText(System.Object,System.String,System.String) 15"),
(WCHAR*)WStr(" [AspectMethodReplace(\"MimeKit.TextPart::SetText(System.Text.Encoding,System.String)\",\"\",[0],[False],[None],Default,[])] SetTextSystemTextEncoding(System.Object,System.Text.Encoding,System.String) 15"),
(WCHAR*)WStr(" [AspectMethodReplace(\"MimeKit.TextPart::set_Text(System.String)\",\"\",[0],[False],[None],Default,[])] SetTextProperty(System.Object,System.String) 15"),
(WCHAR*)WStr("[AspectClass(\"Microsoft.AspNetCore.Http\",[None],Sink,[UnvalidatedRedirect])] Datadog.Trace.Iast.Aspects.AspNetCore.Http.HttpResponseAspect 4"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"Microsoft.AspNetCore.Http.HttpResponse::Redirect(System.String)\",\"\",[0],[False],[None],Default,[])] Redirect(System.String) 15"),
(WCHAR*)WStr(" [AspectMethodInsertBefore(\"Microsoft.AspNetCore.Http.HttpResponse::Redirect(System.String,System.Boolean)\",\"\",[1],[False],[None],Default,[])] Redirect(System.String) 15"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using System.Web;
using System;

namespace Samples.InstrumentedTests.Iast.Vulnerabilities;

Expand All @@ -22,6 +23,13 @@ public class EmailHtmlInjectionAWSSDKSimpleEmailTests : EmailInjectionBaseTests
[Fact]
public void GivenAnEmail_WhenSendAsyncHtmlMailMessageAsyncTaintedVaulesHtml_ThenIsVulnerable()
{
string ppp = taintedLastName + "ww";
Console.WriteLine(ppp);
new AmazonSimpleEmailServiceClient().SendEmailAsync(BuildMailMessage(true, taintedName, taintedLastName), default);

ppp = taintedLastName + "wwedddddd";
Console.WriteLine(ppp);

TestMailCall(() => new AmazonSimpleEmailServiceClient().SendEmailAsync(BuildMailMessage(true, taintedName, taintedLastName), default), true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,7 @@ public class EmailHtmlInjectionMailKitTests : EmailInjectionBaseTests
[Fact]
public void GivenAnEmail_WhenSendHtmlMailMessageTaintedValuesHtmlSetTextEncoding_ThenIsVulnerable()
{
var message = BuildMailMessage(true, taintedName, taintedLastName, SetTextMode.SetTextEncoding);
try
{
new SmtpClient().Send(message, default, null);
}
catch
{
}
AssertVulnerable();
TestMailCall(() => new SmtpClient().Send(BuildMailMessage(true, taintedName, taintedLastName, SetTextMode.SetTextEncoding), default, null), true);
}

[Fact]
Expand All @@ -59,7 +51,6 @@ public void GivenAnEmail_WhenSendHtmlMailMessageTaintedSanitizedValuesHtml_ThenI
TestMailCall(() => new SmtpClient().Send(BuildMailMessage(true, HttpUtility.HtmlEncode(taintedName), HttpUtility.HtmlEncode(taintedLastName)), default, null), false);
}


[Fact]
public void GivenAnEmail_WhenSendTextMailMessageTaintedValuesText_ThenIsNotVulnerable()
{
Expand Down

0 comments on commit ecdd6cd

Please sign in to comment.