Mail Sender (New)
Send emails implemented in Python with embedded image support.
Email · Mail Sender (New)
Details
| ID | Mail Sender (New) |
|---|---|
| Provider | Open Source |
| Category | |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10404775 |
| Supported Modules | Agentix Cloud Runtime Security Cloud Posture Security XSIAM EDR Cortex Cloud |
README
This is a Python script for sending emails, which includes various attachment implementations.
Supported Authentication
This integration does not support NTLM authentication. If you are using Exchange as the SMTP server for sending mail and receive the error message: No suitable authentication method found. make sure that you enable Basic Authentication for SMTP authentication in your Exchange environment.
This example shows Basic Authentication on Exchange 2010:
Configure Mail Sender on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Mail Sender (New).
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance
- Mail server hostname or IP address
- SMTP Port
- Credentials
- Password
- Sender address: if the sender address is different than the log-in credentials and password, the sender address must have permission to send emails.
- FQDN: Fully Qualified Domain Name, used in EHLO
- Use TLS for connection
- Do not validate server certificate (not secure)
- Click Test to validate the URLs, token, and connection.
Commands
1. Send email
Sends an email.
Basic Command
send-mail
Input
| Argument Name | Description |
| to | Recipient email address |
| cc | Additional recipient email address |
| bcc | Additional recipient email address |
| subject | Subject of the email |
| body | Content of the email in plain text |
| htmlBody | Content of the email in html format |
| replyTo |
If recipient chooses to reply, reply will automatically be sent to this email address |
| attachIDs | Comma-separated list of IDs of war room entries that contain the files you want to attach to the email |
| attachNames |
A comma-separated list of new names, to rename attachments, in correspondence with the order that they were attached to the email. Examples:
|
| attachCIDs |
A comma-separated list of CIDs to embed attachments inside the email itself |
| transientFile |
Textual name for attached file. Multiple files are supported. |
| transientFileContent |
Content for attached file. Multiple files are supported. Example: transientFile.1="t1.txt" transientFileContent.1="test 1" transientFile.2="t3.txt" transientFileContent.2="test 3" |
| transientFileCID |
CID for attached file if you want it inline. Multiple files are supported. Example: transientFile.1="t1.txt" transientFileContent.1="test 1" transientFileCID.1="t1.txt@xxx.yyy" transientFile.2="t3.txt" transientFileContent.2="test 3" |
| templateParams |
Replace {varname} variables with values from this parameter. Expected values are in the form of a JSON document Example: {
"varname":{
"value":"some value",
"key":"context key"
}
}
Each var name can either be provided with a value or a context key to retrieve the value from. Note that only context data is accessible for this argument, while incident fields are not. |
| additionalHeader |
Custom header to add to an email. |
| sender | The sender address to use for the email. If provided, it overrides the default sender address configured in the integration instance. |
Command Example
!send-mail to="name@example.com" cc="test@example.com" bcc="admin@example.com" subject="Topic of the day" replyTo="replymail@example.com" attachIDs="111@02a9cf84-c76f-4b2e-8840-c6b2a85c53cf,129@02a9cf84-c86f-4b2e-8840-c6c2a89c53cf" attachNames="notcookie.png,cookie.jpg" attachCIDs="notcookie,mycookie" transientFile="friendly_note.txt" transientFileContent="this is some text" htmlBody="<html>
<body>
<p>HELLO</p>
<img src=\"cid:mycookie\"/>
</body>
</html>"
About CIDs
CIDs work by attaching the image to the email you are sending, and then using standard HTML image tags that reference that image to embed it in the email when the user opens it.
Example:
<html>
<body>
<img src=\"cid:radomimagecid\"/>
</body>
</html>
CID Limitations
CID is not supported on all email applications. It is supported mainly on Outlook.
On web applications, such as Gmail, adding CID duplicates the attachment: as an actual attachment and embedded in the text body.
Configuration parameters
host— Mail server hostname or IP address (required)port— SMTP Port (required)credentials— Credentialsfrom— Sender address (required)fqdn— Fully Qualified Domain Name (FQDN) - used in EHLOtls— Connection Securityinsecure— Trust any certificate (not secure)
Commands (1)
-
send-mailSend an email.
import MailSenderNew import demistomock as demisto import pytest import hmac import hashlib RETURN_ERROR_TARGET = "MailSenderNew.return_error" @pytest.mark.parametrize( "subject,subj_include,headers", [ ("test-before\ntest-after", "test-after", "foo=baz"), ("test-before\ntest-after", "test-after", "foo=baz"), ("\xd7\xa2\xd7\x91\xd7\xa8\xd7\x99\xd7\xaa", "=?utf-8?", "foo=baz"), # non-ascii char utf-8 encoded ("עברית", "=?utf-8?", "foo=baz"), ], ) # noqa: E124 def test_create_msg(mocker, subject, subj_include, headers): mocker.patch.object( demisto, "args", return_value={ "to": "test@test.com,test1@test.com", # disable-secrets-detection "from": "test@test.com", "bcc": "bcc@test.com", # disable-secrets-detection "cc": "cc@test.com", # disable-secrets-detection "subject": subject, "body": "this is the body", "additionalHeader": headers, }, ) mocker.patch.object( demisto, "params", return_value={ "from": "test@test.com", }, ) (body, html_body, msg, to, cc, bcc) = MailSenderNew.create_msg() assert to == ["test@test.com", "test1@test.com"] # disable-secrets-detection assert cc == ["cc@test.com"] # disable-secrets-detection assert bcc == ["bcc@test.com"] # disable-secrets-detection assert body == "this is the body" # disable-secrets-detection assert html_body == "" # disable-secrets-detection lines = msg.splitlines() subj = [x for x in lines if "Subject" in x][0] assert subj_include in subj assert "foo" in msg def test_debug_smtp(mocker): """ Test that when we do test-module and fail we collect the server debug log """ mocker.patch.object(demisto, "params", return_value={"from": "test@test.com", "host": "localhost", "port": "2025"}) mocker.patch.object(demisto, "command", return_value="test-module") demisto_error = mocker.patch.object(demisto, "error") return_error_mock = mocker.patch(RETURN_ERROR_TARGET) MailSenderNew.main() assert return_error_mock.call_count == 1 assert demisto_error.call_count == 1 # LOG should at least contain: "connect: " with port assert MailSenderNew.LOG.messages assert "2025" in MailSenderNew.LOG.messages[0] def test_hmac(mocker): """ Test that hmac is able to handle unicode user/pass """ mocker.patch.object(demisto, "params", return_value={"credentials": {"identifier": "user", "password": "pass"}}) user, password = MailSenderNew.get_user_pass() res = user + hmac.HMAC(str.encode(password), b"test", hashlib.sha256).hexdigest() assert len(res) > 0 @pytest.mark.parametrize( "template_params_arg", ['{"name": {"value": "hello3"}}', ({"name": {"value": "hello3"}}), ({"name": {"key": "key_from_context"}})], ) def test_template_params(mocker, template_params_arg): """ Given: A templateParams argument Case A: As a string type. Case B: As a dict type. When: Calling template_params(). Then: - Make sure providing both JSON and JSON string objects for the argument is supported. - Make sure that for "key" field the method takes the value from the context. - Make sure that for "value" field the method takes the provided value. """ mocker.patch.object(demisto, "getArg", return_value=template_params_arg) mocker.patch.object(demisto, "context", return_value={"key_from_context": "value_from_context"}) mocker.patch.object(demisto, "dt", side_effect=lambda context, k: context.get(k)) actual_params = MailSenderNew.parse_template_params() if "key" in str(template_params_arg): assert actual_params == {"name": "value_from_context"} else: assert actual_params == {"name": "hello3"} def test_attachments(mocker): mocker.patch.object( demisto, "args", return_value={ "attachIDs": "123456", "attachNames": "attach.txt", "body": "this is a test by UT", "subject": "special test via UT", "to": "admin@test.com", "transientFile": "test1.txt,test2.txt", "transientFileContent": "content1,content2", }, ) mocker.patch.object(demisto, "params", return_value={"from": "test@test.com"}) mocker.patch.object(demisto, "getFilePath", return_value={"path": "test_data/attachment.txt", "name": "attachment.txt"}) (_, _, msg, _, _, _) = MailSenderNew.create_msg() decode_files_content = ["Y29udGVudA==", "Y29udGVudDE=", "Y29udGVudDI="] assert all(file_name in msg for file_name in ["attach.txt", "test1.txt", "test2.txt"]) assert all(decode_file_content in msg for decode_file_content in decode_files_content) @pytest.mark.parametrize( "args, params, expected_from", [ pytest.param( {"to": "test@test.com", "subject": "test", "body": "test", "sender": "override@test.com"}, {"from": "default@test.com"}, "override@test.com", id="use_explicit_sender_override", ), pytest.param( {"to": "test@test.com", "subject": "test", "body": "test"}, {"from": "default@test.com"}, "default@test.com", id="fallback_to_default_sender", ), ], ) def test_create_msg_sender_override(mocker, args, params, expected_from): """ Given: - Case A: 'sender' argument is provided in demisto.args(). - Case B: 'sender' argument is NOT provided in demisto.args(). When: - Calling create_msg(). Then: - In Case A, the 'From' header should be the 'sender' argument. - In Case B, the 'From' header should be the 'from' parameter from the integration configuration. """ mocker.patch.object(demisto, "args", return_value=args) mocker.patch.object(demisto, "params", return_value=params) (_, _, msg, _, _, _) = MailSenderNew.create_msg() assert f"From: {expected_from}" in msg
