diff --git a/src/System Application/Test/Agent/src/SDK/AgentMessageTest.Codeunit.al b/src/System Application/Test/Agent/src/SDK/AgentMessageTest.Codeunit.al index 01c20f6c474..6cda705cf98 100644 --- a/src/System Application/Test/Agent/src/SDK/AgentMessageTest.Codeunit.al +++ b/src/System Application/Test/Agent/src/SDK/AgentMessageTest.Codeunit.al @@ -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] + 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 := '
Hello world
4 < 5
Hello world
4 < 5
Safe
'; + + // [WHEN] The agent message body is sanitized. + SanitizedBody := AgentTaskMsgBuilderImpl.SanitizeMessage(Body); + + // [THEN] Only the safe paragraph remains. + Assert.AreEqual('Safe
', 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 := 'Safe
'; + + // [WHEN] The XHTML-encoded payload is sanitized. + SanitizedBody := AgentTaskMsgBuilderImpl.SanitizeMessage(Body); + + // [THEN] Only the safe paragraph remains. + Assert.AreEqual('Safe
', SanitizedBody, 'The XHTML encoding must receive the same sanitization policy.'); + end; + [Test] procedure ContentTypeFromFilenameForKnownExtensions() var diff --git a/src/System Application/Test/Email/src/EmailMessageUnitTest.Codeunit.al b/src/System Application/Test/Email/src/EmailMessageUnitTest.Codeunit.al index 0c6020f4874..8c15d2d3d63 100644 --- a/src/System Application/Test/Email/src/EmailMessageUnitTest.Codeunit.al +++ b/src/System Application/Test/Email/src/EmailMessageUnitTest.Codeunit.al @@ -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() + 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', 'Hello world
4 < 5
Hello world
4 < 5
Safe
'; + + // [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('Safe
', 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 := 'Safe
'; + + // [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('Safe
', 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 := 'Literal text
& more text'; + + // [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 := 'Formatted text
'; + + // [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()