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
Expand Up @@ -1329,6 +1329,63 @@ codeunit 133963 "Agent Message Test"
Assert.AreEqual('last-file.txt', LastAttachment."File Name", 'Last attachment file name should match');
end;

[Test]
Comment thread
nhsejth marked this conversation as resolved.
Resolved
procedure SanitizeMessagePreservesBasicFormatting()
var
AgentTaskMsgBuilderImpl: Codeunit "Agent Task Msg. Builder Impl.";
Body: Text;
SanitizedBody: Text;
begin
// [SCENARIO] Agent message sanitization preserves readable formatting and encoded text without active content.

// [GIVEN] An HTML message body containing formatting, styling, and an event handler.
Body := '<p style="color:red" onclick="alert()">Hello <b>world</b><br>4 &lt; 5</p>';

// [WHEN] The agent message body is sanitized.
SanitizedBody := AgentTaskMsgBuilderImpl.SanitizeMessage(Body);

// [THEN] Formatting and encoded text survive, while styling and the event handler are removed.
Assert.AreEqual('<p>Hello <b>world</b><br>4 &lt; 5</p>', SanitizedBody, 'Agent messages must retain basic formatting and encoded text without styling or event handlers.');
end;

[Test]
procedure SanitizeMessageRemovesMathmlIntegrationPoints()
var
AgentTaskMsgBuilderImpl: Codeunit "Agent Task Msg. Builder Impl.";
Body: Text;
SanitizedBody: Text;
begin
// [SCENARIO] Sanitized agent messages exclude foreign integration-point content using the HTML encoding.

// [GIVEN] A safe paragraph followed by a MathML HTML integration-point payload.
Body := '<p>Safe</p><math><annotation-xml encoding="text/html"><title><a encoding="</title><img src=x onerror=alert()>"></annotation-xml></math>';

// [WHEN] The agent message body is sanitized.
SanitizedBody := AgentTaskMsgBuilderImpl.SanitizeMessage(Body);

// [THEN] Only the safe paragraph remains.
Assert.AreEqual('<p>Safe</p>', SanitizedBody, 'Foreign HTML integration points and their contents must not survive agent message sanitization.');
end;

[Test]
procedure SanitizeMessageRemovesXhtmlMathmlIntegrationPoints()
var
AgentTaskMsgBuilderImpl: Codeunit "Agent Task Msg. Builder Impl.";
Body: Text;
SanitizedBody: Text;
begin
// [SCENARIO] Sanitized agent messages exclude foreign integration-point content using the XHTML encoding.

// [GIVEN] A safe paragraph followed by a MathML XHTML integration-point payload.
Body := '<p>Safe</p><math><annotation-xml encoding="application/xhtml+xml"><title><a encoding="</title><img src=x onerror=alert()>"></annotation-xml></math>';

// [WHEN] The XHTML-encoded payload is sanitized.
SanitizedBody := AgentTaskMsgBuilderImpl.SanitizeMessage(Body);

// [THEN] Only the safe paragraph remains.
Assert.AreEqual('<p>Safe</p>', SanitizedBody, 'The XHTML encoding must receive the same sanitization policy.');
end;

[Test]
procedure ContentTypeFromFilenameForKnownExtensions()
var
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,105 @@ codeunit 134689 "Email Message Unit Test"
Assert.IsTrue(Message.Attachments_Next() = 0, 'No attachments were expected');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreateMessageWithSanitizationPreservesBasicFormatting()
Comment thread
nhsejth marked this conversation as resolved.
Resolved
var
Message: Codeunit "Email Message";
begin
// [SCENARIO] Sanitized HTML email retains readable formatting and encoded text without active content.

// [GIVEN] An email editor and HTML containing formatting, styling, and an event handler.
PermissionsMock.Set('Email Edit');

// [WHEN] The HTML message is created with sanitization enabled.
Message.Create('recipient@test.com', 'Test subject', '<p style="color:red" onclick="alert()">Hello <b>world</b><br>4 &lt; 5</p>', true, true);

// [THEN] Formatting and encoded text survive, while styling and the event handler are removed.
Assert.AreEqual('<p>Hello <b>world</b><br>4 &lt; 5</p>', Message.GetBody(), 'Sanitization must preserve basic formatting and encoded text but remove styling and event handlers.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreateMessageWithSanitizationRemovesMathmlIntegrationPoints()
var
Message: Codeunit "Email Message";
Body: Text;
begin
// [SCENARIO] Sanitized email excludes foreign integration-point content using the HTML encoding.

// [GIVEN] An email editor and a safe paragraph followed by a MathML HTML integration-point payload.
PermissionsMock.Set('Email Edit');
Body := '<p>Safe</p><math><annotation-xml encoding="text/html"><title><a encoding="</title><img src=x onerror=alert()>"></annotation-xml></math>';

// [WHEN] The HTML message is created with sanitization enabled.
Message.Create('recipient@test.com', 'Test subject', Body, true, true);

// [THEN] Only the safe paragraph remains.
Assert.AreEqual('<p>Safe</p>', Message.GetBody(), 'Foreign HTML integration points and their contents must not survive email sanitization.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreateMessageWithSanitizationRemovesXhtmlMathmlIntegrationPoints()
var
Message: Codeunit "Email Message";
Body: Text;
begin
// [SCENARIO] Sanitized email excludes foreign integration-point content using the XHTML encoding.

// [GIVEN] An email editor and a safe paragraph followed by a MathML XHTML integration-point payload.
PermissionsMock.Set('Email Edit');
Body := '<p>Safe</p><math><annotation-xml encoding="application/xhtml+xml"><title><a encoding="</title><img src=x onerror=alert()>"></annotation-xml></math>';

// [WHEN] The XHTML-encoded payload is sanitized during message creation.
Message.Create('recipient@test.com', 'Test subject', Body, true, true);

// [THEN] Only the safe paragraph remains.
Assert.AreEqual('<p>Safe</p>', Message.GetBody(), 'The XHTML encoding must receive the same sanitization policy.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreatePlainTextMessageDoesNotApplyHtmlSanitization()
var
Message: Codeunit "Email Message";
Body: Text;
begin
// [SCENARIO] Plain-text email treats markup as literal content even when sanitization is requested.

// [GIVEN] An email editor and literal text containing markup and an ampersand.
PermissionsMock.Set('Email Edit');
Body := '<p>Literal text</p> & <b>more text</b>';

// [WHEN] A plain-text message is created with the sanitization option enabled.
Message.Create('recipient@test.com', 'Test subject', Body, false, true);

// [THEN] The body is unchanged and the message remains plain text.
Assert.AreEqual(Body, Message.GetBody(), 'The sanitization option must not alter a plain-text message.');
Assert.IsFalse(Message.IsBodyHTMLFormatted(), 'The message must remain plain text.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreateHtmlMessageCanKeepSanitizationDisabled()
var
Message: Codeunit "Email Message";
Body: Text;
begin
// [SCENARIO] Email callers can retain HTML styling by explicitly disabling sanitization.

// [GIVEN] An email editor and an HTML body with styling.
PermissionsMock.Set('Email Edit');
Body := '<p style="color:red">Formatted text</p>';

// [WHEN] The HTML message is created with sanitization disabled.
Message.Create('recipient@test.com', 'Test subject', Body, true, false);

// [THEN] The HTML body, including its styling, is preserved.
Assert.AreEqual(Body, Message.GetBody(), 'The dependency upgrade must not implicitly enable the optional sanitizer.');
end;

[Test]
[TransactionModel(TransactionModel::AutoRollback)]
procedure CreateMessageWithCCAndBCCTest()
Expand Down
Loading