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\ntesting 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'
'\ntesting 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