import json import demistomock as demisto import pytest def util_open_file(path): with open(path) as f: return f.read() def util_load_json(path): with open(path) as f: return json.loads(f.read()) def test_set_email_reply(mocker): """Unit test Given - Email message details When - Function is called with all arguments provided Then - Validate the function returns HTML that will properly render the email message """ from DisplayEmailHtmlThread import set_email_reply expected_html = util_open_file("test_data/single_html_doc.txt") input_html = "

Test email body.

" test_message = { "email_from": "soc_sender@company.com", "email_to": "end_user@company.com", "email_cc": "cc_user@company.com", "email_subject": "Test Email #1", "html_body": input_html, "email_time": "2022-04-06T17:53:46UTC", "attachment_names": "File1.txt, File2.txt", } result = set_email_reply(**test_message) assert result == expected_html def test_html_cleanup(mocker): """Unit test Given - Input HTML content When - Input html contains multiple separate HTML documents Then - Validate that the function returns a single HTML document """ from DisplayEmailHtmlThread import html_cleanup input_html = util_open_file("test_data/multiple_html_docs.txt") expected_html = util_open_file("test_data/cleaned_html.txt") result = html_cleanup(input_html) assert result == expected_html no_entries_message = """

This Incident does not contain any email threads yet.

""" @pytest.mark.parametrize( "emailselectedthread, email_threads, expected_result_type", [ (1, {}, "no_threads"), (1, {"EmailThreads": util_load_json("test_data/email_threads.json")}, "good_result"), (5, {"EmailThreads": util_load_json("test_data/email_threads.json")}, "error_result"), ], ) def test_main(emailselectedthread, email_threads, expected_result_type, mocker): """ Unit test Scenario - No email threads present Given - Script is called to render an HTML thread When - The incident where the script is being run contains no email threads Then - Validate that the script returns message that no threads are present Unit test Scenario - Threads present and thread selection valid Given - Script is called to render an HTML thread When - The incident where the script is being run contains email threads - The 'emailselectedthread' field is set to a value corresponding to an email thread that is present Then - Validate that the script returns properly rendered HTML for the email thread Unit test Scenario - Threads present but thread selection not valid Given - Script is called to render an HTML thread When - The incident where the script is being run contains email threads - The 'emailselectedthread' field is set to a value which does not correspond to any of the present threads Then - Validate that the script returns an appropriate error """ import DisplayEmailHtmlThread from DisplayEmailHtmlThread import main mock_incident = {"CustomFields": {"emailselectedthread": emailselectedthread}} mocker.patch.object(demisto, "incident", return_value=mock_incident) mocker.patch.object(demisto, "context", return_value=email_threads) return_results_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_results", return_value=True) return_error_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_error", return_value=True) main() results_call_args = return_results_mocker.call_args error_call_args = return_error_mocker.call_args if expected_result_type == "no_threads": assert results_call_args.args[0]["Contents"] == no_entries_message elif expected_result_type == "good_result": expected_result = util_open_file("test_data/good_result.txt") assert results_call_args.args[0]["Contents"] == expected_result elif expected_result_type == "error_result": expected_result = "An email thread of 5 was not found. Please make sure this thread number is correct." assert error_call_args.args[0] == expected_result def test_remove_color_from_html_text(): from DisplayEmailHtmlThread import remove_color_from_html_text html_message = ( '\r\n\r\n\r\n' '\r\n\r\n" '\r\n\r\n\r\n\r\n
\r\n' '
\r\nreply to a thread from outlook
\r\n
\r\n
From: Administrator
\r\nSent: Tuesday, ' "January 9, 2024 3:01:34 PM
\r\nTo: Administrator
\r\nSubject: <04352911> " 'test 9.1 15:00
\r\n
 
\r\n
\r\n
\r\n' '\r\n
testing again from xsoar
\r\n
' "\r\n\r\n\r\n" ) expected_html_message = ( '\n\n\n\n\n\n\n\n' '\n
\n
\r\nreply to a thread from outlook
\n
\n
From: Administrator
\nSent:' " Tuesday, January 9, 2024 3:01:34 PM
\nTo: Administrator
\nSubject: " '<04352911> test 9.1 15:00
\n
\xa0
\n
\n
\n' '\n
testing again from xsoar
\n
' "
\n\n\n" ) result = remove_color_from_html_text(html_message) assert result == expected_html_message def test_main_styled_html(mocker): """ Given - Script is called to render an HTML thread. The html contains styling attributes such as color. When - The incident where the script is being run contains email threads Then - Validate that the script returns an appropriate html, after the removal of the styling. """ import DisplayEmailHtmlThread from DisplayEmailHtmlThread import main email_threads = {"EmailThreads": util_load_json("test_data/email_thread_with_html_styling.json")} mock_incident = {"CustomFields": {"emailselectedthread": 0}} mocker.patch.object(demisto, "incident", return_value=mock_incident) mocker.patch.object(demisto, "context", return_value=email_threads) return_results_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_results", return_value=True) main() results_call_args = return_results_mocker.call_args assert " color" not in results_call_args.args[0]["Contents"] def test_html_cleanup_with_account_name(mocker): """Unit test Given - Input HTML content with image tags and an account name When - html_cleanup is called with an account_name Then - Validate that the function rewrites image src paths to include the account name """ from DisplayEmailHtmlThread import html_cleanup input_html = '

Hello

' result = html_cleanup(input_html, account_name="myaccount") assert 'src="xsoar/myaccount/entry/download/abc123"' in result assert result.startswith("") def test_main_with_single_dict_email_thread(mocker): """ Given - Script is called to render an HTML thread When - The incident context contains email threads as a single dict (not a list) Then - Validate that the script handles the dict-to-list conversion and renders properly """ import DisplayEmailHtmlThread from DisplayEmailHtmlThread import main single_thread = { "EmailBCC": "", "EmailBody": "Test message", "EmailCC": "", "EmailCommsThreadId": "12345", "EmailCommsThreadNumber": "1", "EmailFrom": "sender@test.com", "EmailHTML": "

Test message

", "EmailTo": "recipient@test.com", "EmailSubject": "Test Subject", "EmailAttachments": "None", "MessageTime": "2022-01-01T00:00:00UTC", } email_threads = {"EmailThreads": single_thread} mock_incident = {"CustomFields": {"emailselectedthread": 1}} mocker.patch.object(demisto, "incident", return_value=mock_incident) mocker.patch.object(demisto, "context", return_value=email_threads) mocker.patch.object(demisto, "args", return_value={}) return_results_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_results", return_value=True) main() results_call_args = return_results_mocker.call_args assert "Test message" in results_call_args.args[0]["Contents"] def test_main_with_account_name(mocker): """ Given - Script is called with an account_name argument When - The incident contains email threads with image tags Then - Validate that the account_name is used to rewrite image src paths """ import DisplayEmailHtmlThread from DisplayEmailHtmlThread import main email_thread = { "EmailBCC": "", "EmailBody": "Test", "EmailCC": "", "EmailCommsThreadId": "12345", "EmailCommsThreadNumber": "1", "EmailFrom": "sender@test.com", "EmailHTML": '

Hello

', "EmailTo": "recipient@test.com", "EmailSubject": "Test", "EmailAttachments": "None", "MessageTime": "2022-01-01T00:00:00UTC", } email_threads = {"EmailThreads": [email_thread]} mock_incident = {"CustomFields": {"emailselectedthread": 1}} mocker.patch.object(demisto, "incident", return_value=mock_incident) mocker.patch.object(demisto, "context", return_value=email_threads) mocker.patch.object(demisto, "args", return_value={"account_name": "testaccount"}) return_results_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_results", return_value=True) main() results_call_args = return_results_mocker.call_args assert 'src="xsoar/testaccount/entry/download/abc123"' in results_call_args.args[0]["Contents"] def test_main_with_no_email_html(mocker): """ Given - Script is called to render an HTML thread When - A thread entry has no EmailHTML field Then - Validate that the script handles the missing EmailHTML gracefully """ import DisplayEmailHtmlThread from DisplayEmailHtmlThread import main email_thread = { "EmailBCC": "", "EmailBody": "Test", "EmailCC": "", "EmailCommsThreadId": "12345", "EmailCommsThreadNumber": "1", "EmailFrom": "sender@test.com", "EmailTo": "recipient@test.com", "EmailSubject": "Test", "EmailAttachments": "None", "MessageTime": "2022-01-01T00:00:00UTC", } email_threads = {"EmailThreads": [email_thread]} mock_incident = {"CustomFields": {"emailselectedthread": 1}} mocker.patch.object(demisto, "incident", return_value=mock_incident) mocker.patch.object(demisto, "context", return_value=email_threads) mocker.patch.object(demisto, "args", return_value={}) return_results_mocker = mocker.patch.object(DisplayEmailHtmlThread, "return_results", return_value=True) main() results_call_args = return_results_mocker.call_args assert results_call_args.args[0]["Contents"] is not None assert "sender@test.com" in results_call_args.args[0]["Contents"] def test_rewrites_single_img_src(): """ Given - HTML contains image tag with src values in the form: src="xsoar/entry/download/" When - The rewrite_img_src function is executed with a valid account name Then - Validate that the function returns appropriate HTML with the account name correctly inserted into the image src path. """ from DisplayEmailHtmlThread import rewrite_img_src html = '' result = rewrite_img_src(html, "myaccount") assert result == '' def test_rewrites_single_img_src_without_account(): """ Given - HTML contains image tag with src values in the form: src="xsoar/entry/download/" When - The rewrite_img_src function is executed with no account name Then - Validate that the function returns the HTML input with no changes """ from DisplayEmailHtmlThread import rewrite_img_src html = '' result = rewrite_img_src(html) assert result == html class TestSetEmailReplyXSSPrevention: """Tests for XSS prevention in set_email_reply header fields.""" def test_xss_in_email_from(self): from DisplayEmailHtmlThread import set_email_reply result = set_email_reply( "", "to@test.com", "cc@test.com", "Subject", "

body

", "2024-01-01T00:00:00Z", "file.txt", ) assert "<script>" in result assert "" not in result def test_xss_in_email_to(self): from DisplayEmailHtmlThread import set_email_reply result = set_email_reply( "from@test.com", "", "cc@test.com", "Subject", "

body

", "2024-01-01T00:00:00Z", "file.txt", ) assert "<img src=x" in result assert "", "Subject", "

body

", "2024-01-01T00:00:00Z", "file.txt" ) assert "<iframe" in result def test_xss_in_email_subject(self): from DisplayEmailHtmlThread import set_email_reply result = set_email_reply( "from@test.com", "to@test.com", "cc@test.com", "", "

body

", "2024-01-01T00:00:00Z", "file.txt", ) assert "<img src=x onerror=alert(1)>" in result assert "body

", "2024-01-01T00:00:00Z", "", ) assert "<script>" in result assert "

World

") assert "" result = sanitize_html_body(malicious_html) assert result == malicious_html