IdentifyAttachedEmail

Identify whether the incident includes an email message attached as an eml or msg file and return the answer to playbook. Also saves the identified entry ID to context for use for later. Commonly used in automated playbooks that handle phishing reports sent to a special phishing mailbox set up by the security team.

python · Common Scripts

Details

IDIdentifyAttachedEmail
Languagepython
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10404775
Tagsphishing email Condition

README

Identify whether the incident includes an email message attached as an eml or msg file and return the answer to playbook.
Also saves the identified entry ID to context for use for later.
Commonly used in automated playbooks that handle phishing reports sent to a special phishing mailbox set up by the security team.

Script Data


Name Description
Script Type python2
Tags phishing, email, Condition
Cortex XSOAR Version 5.0.0

Used In


This script is used in the following playbooks and scripts.

  • Process Email - Core
  • Process Email - Core v2
  • Process Email - Generic
  • Process Email - Generic v2

Inputs


Argument Name Description
entryid Specific entryid to check if it is an email attachment. If not specified will check all entries of the incident.

Outputs


Path Description Type
yes If incident contains an email attachment. Unknown
no If incident does not contain an email attachment Unknown
reportedemailentryid The entry IDs of the email attachments found. String
import IdentifyAttachedEmail
import pytest
from IdentifyAttachedEmail import *


def execute_command(command, args):
    if command == "getEntry":
        if args["id"] == "23@2":
            return [{"Type": entryTypes["note"], "FileMetadata": {"info": "koko"}, "ID": "23@2"}]
        elif args["id"] == "24@2":
            return [
                {
                    "Type": entryTypes["file"],
                    "FileMetadata": {"info": "news or mail text, ASCII text", "type": "eml"},
                    "ID": "24@2",
                }
            ]
    if command == "getEntries":
        return {}
    return None


def test_is_email():
    # valid - type and info are present and valid
    assert is_email({"type": "eml}", "info": "SMTP mail, UTF-8 Unicode text"}, "test.txt")
    assert is_email({"type": "eml", "info": "SMTP mail, UTF-8 Unicode text"}, "test.txt")
    assert is_email({"type": "message/rfc822", "info": "SMTP mail, UTF-8 Unicode text"}, "test.txt")
    assert is_email({"info": "CDFV2 Microsoft Outlook Message", "type": "eml"}, "msg.test")
    assert is_email(
        {"info": "RFC 822 mail text, ISO-8859 text, with very long lines, with CRLF line terminator", "type": "eml"}, "test.bin"
    )
    assert is_email({"info": "CDFV2 Microsoft Outlook Message", "type": "eml"}, "test.bin")
    assert is_email(
        {
            "info": 'multipart/signed; protocol="application/pkcs7-signature";, ASCII text, with CRLF line terminators',
            "type": "eml",
        },
        "test.bin",
    )
    assert is_email({"info": "CDFV2 Microsoft Outlook Message", "type": "msg"}, "test.msg")

    # invalid - info is missing with wrong type
    assert not is_email({"type": "invalid type"}, "test.txt")

    # valid - info is missing with valid type
    assert is_email({"type": "eml"}, "test.txt")
    assert is_email({"type": "eml}"}, "test.eml")
    assert is_email({"type": "message/rfc822"}, "test.msg")

    # invalid - info is missing with invalid type
    assert not is_email({"type": "invalid type"}, "test.txt")

    # valid - type is missing with valid info
    assert is_email({"info": "SMTP mail, UTF-8 Unicode text"}, "test.eml")

    # invalid - type is missing with invalid info
    assert not is_email({"info": "invalid info"}, "test.eml")

    # invalid - wrong type
    assert not is_email({"type": "invalid type", "info": "SMTP mail, UTF-8 Unicode text"}, "test.txt")
    assert not is_email({"type": "message/rfc822", "info": "vCalendar calendar file"}, "Workshop.eml")
    assert not is_email({"type": "eml", "info": "vCalendar calendar file"}, "Workshop.eml")

    # invalid - wrong info
    assert not is_email({"type": "eml", "info": "invalid info"}, "test.txt")

    # valid - .eml file with "text" or "data"  in the info
    assert is_email({"info": "ASCII text, with CRLF line terminators"}, "msg.eml")
    assert is_email({"info": "data"}, "test.eml")
    assert is_email({"info": "UTF-8 Unicode text, with very long lines, with CRLF line terminators"}, "test.eml")

    # invalid - file is not .msg or .eml
    assert not is_email({"info": "data"}, "test.bin")
    assert not is_email({"info": "composite document file v2 document"}, "cv.doc")


def test_get_email_entry_id(mocker):
    mocker.patch.object(IdentifyAttachedEmail, "is_email", return_value=True)
    assert not get_email_entry_id("")


def test_identify_attached_mail(mocker):
    entry_ids = '["23@2","24@2"]'
    from CommonServerPython import demisto

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)
    args = {"entryid": entry_ids}
    results = identify_attached_mail(args)
    assert results == ("yes", {"reportedemailentryid": ["24@2"]})


def test_identify_attached_mail_no_email_attached(mocker):
    entry_ids = """[\"23@2\"]"""
    from CommonServerPython import demisto

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)

    args = {"entryid": entry_ids}
    results = identify_attached_mail(args)
    assert results == ("no", None)


def test_identify_attached_mail_in_xsoar_saas_list_of_entries_passed(mocker):
    """
    Given
    - two entries with ids 23@2 24@2
    - the platform is xsoar saas

    When
    - running the script to get the entries

    Then
    - expect the getEntriesByIDs to be called

    """
    entry_ids = """[\"23@2\",\"24@2\"]"""
    import CommonServerPython

    mocker.patch.object(CommonServerPython, "get_demisto_version", return_value={"version": "8.2.0", "buildNumber": "12345"})

    def execute_command(command, args):
        if command == "getEntriesByIDs" and args.get("entryIDs") == "23@2,24@2":
            return [
                {"File": "msg.eml", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "23@2"},
                {"File": "foo.txt", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "24@2"},
            ]
        else:
            pytest.fail()

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)

    args = {"entryid": entry_ids}
    results = identify_attached_mail(args)
    assert results == ("yes", {"reportedemailentryid": ["23@2"]})


def test_identify_attached_mail_no_entries_passed(mocker):
    """
    Given
    - no entries passed
    - the platform is xsoar saas

    When
    - running the script to get the entries

    Then
    - expect the getEntries to be called with filters

    """
    import CommonServerPython

    mocker.patch.object(CommonServerPython, "get_demisto_version", return_value={"version": "8.2.0", "buildNumber": "12345"})

    def execute_command(command, args):
        if command == "getEntries" and args == {"filter": {"categories": ["attachments"]}}:
            return [
                {"File": "msg.eml", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "23@2"},
                {"File": "foo.txt", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "24@2"},
            ]
        else:
            pytest.fail()

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)

    results = identify_attached_mail({})
    assert results == ("yes", {"reportedemailentryid": ["23@2"]})


def test_identify_attached_mail_no_email_found(mocker):
    """
    Given
    - no email entries in the warroom
    - the platform is xsoar saas

    When
    - running the script to get the entries

    Then
    - no entries to be found

    """
    import CommonServerPython

    mocker.patch.object(CommonServerPython, "get_demisto_version", return_value={"version": "8.2.0", "buildNumber": "12345"})

    def execute_command(command, args):
        if command == "getEntries" and args == {"filter": {"categories": ["attachments"]}}:
            return
        else:
            pytest.fail()

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)

    results = identify_attached_mail({})
    assert results == ("no", None)


def test_list_of_entries_passed_in_xsoar_saas_but_no_file_entries(mocker):
    """
    Given
    - two entries with ids 23@2 24@2 which are not file entries
    - the platform is xsoar saas

    When
    - running the script to get the entries

    Then
    - expect the getEntriesByIDs to be called
    - expect no email entries to be found

    """
    entry_ids = """[\"23@2\",\"24@2\"]"""
    import CommonServerPython

    mocker.patch.object(CommonServerPython, "get_demisto_version", return_value={"version": "8.2.0", "buildNumber": "12345"})

    def execute_command(command, args):
        if command == "getEntriesByIDs" and args.get("entryIDs") == "23@2,24@2":
            return [
                {"File": "msg.txt", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "23@2"},
                {"File": "foo.txt", "FileMetadata": {"info": "ASCII text, with CRLF line terminators"}, "ID": "24@2"},
            ]
        else:
            pytest.fail()

    mocker.patch.object(demisto, "executeCommand", side_effect=execute_command)

    args = {"entryid": entry_ids}
    results = identify_attached_mail(args)
    assert results == ("no", None)