DisplayEmailHtml

Displays the original email in HTML format.

python · Email Communication

Source

import html as html_module
import re

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

ALLOWED_EMAIL_TAGS = {
    "p",
    "br",
    "div",
    "span",
    "b",
    "i",
    "u",
    "a",
    "img",
    "table",
    "tr",
    "td",
    "th",
    "thead",
    "tbody",
    "ul",
    "ol",
    "li",
    "h1",
    "h2",
    "h3",
    "h4",
    "h5",
    "h6",
    "pre",
    "code",
    "blockquote",
    "strong",
    "em",
    "hr",
    "font",
    "center",
    "small",
    "big",
    "sub",
    "sup",
    "dl",
    "dt",
    "dd",
    "caption",
}

ALLOWED_EMAIL_ATTRIBUTES: dict[str, set[str]] = {
    "*": {"style", "class", "id", "dir", "align", "width", "height", "bgcolor", "valign"},
    "a": {"href", "target"},
    "img": {"src", "alt", "title"},
    "td": {"colspan", "rowspan"},
    "th": {"colspan", "rowspan", "scope"},
    "font": {"color", "face", "size"},
    "table": {"border", "cellpadding", "cellspacing"},
}


def sanitize_html_body(html_body: str) -> str:
    """Sanitize email body HTML using an allowlist of tags and attributes.

    When nh3 is available, strips disallowed tags and attributes while preserving safe ones.
    When nh3 is not available, returns the HTML as-is since full escaping
    would break legitimate formatting in an HTML rendering context.
    """
    try:
        import nh3

        return nh3.clean(html_body, tags=ALLOWED_EMAIL_TAGS, attributes=ALLOWED_EMAIL_ATTRIBUTES)  # pylint: disable=no-member
    except ImportError:
        demisto.debug("nh3 is not available; HTML sanitization skipped")
        return html_body


def create_email_html(email_html="", entry_id_list=None):
    if not entry_id_list:
        return email_html

    account_name = get_tenant_account_name()

    for entry_id in entry_id_list:
        # Handling inline attachments from Gmail mailboxes
        saas_xsoar_xsiam_prefix = "xsoar/" if is_xsiam_or_xsoar_saas() else ""
        escaped_name = re.escape(entry_id[0])
        if re.search(f'src="[^>]+"(?=[^>]+alt="{escaped_name}")', email_html):
            email_html = re.sub(
                f'src="[^>]+"(?=[^>]+alt="{escaped_name}")',
                f"src={account_name}/{saas_xsoar_xsiam_prefix}entry/download/{entry_id[1]}",
                email_html,
            )
        # Handling inline attachments from Outlook mailboxes
        # Note: when tested, entry id list and inline attachments were in the same order, so there was no need in
        # special validation that the right src was being replaced.
        else:
            email_html = re.sub(
                '(src="cid(.*?"))',
                f"src={account_name}/{saas_xsoar_xsiam_prefix}entry/download/{entry_id[1]}",
                email_html,
                count=1,
            )
    return email_html


def get_entry_id_list(attachments, files):
    """Get the email attachments and create entry id list.
    Args:
        attachments (list): The attachments of the email.
        files (list): The uploaded files in the context.
    Returns:
        list. Attachments entries ids list.
    """
    if not (attachments and files):
        return []

    entry_id_list = []
    files = [files] if not isinstance(files, list) else files
    for attachment in attachments:
        attachment_name = attachment.get("name", "")
        for file in files:
            if attachment_name == file.get("Name"):
                entry_id_list.append((attachment_name, file.get("EntryID")))
    demisto.info(f"\n\n idlist \n\n{entry_id_list}")
    return entry_id_list


def set_email_reply(email_from, email_to, email_cc, email_subject, html_body, attachments):
    """Set the email reply from the given details.
    Args:
        email_from: The email author mail.
        email_to: The email recipients.
        email_cc: The email cc.
        html_body: The email HTML body.
    Returns:
        str. Email reply.
    """
    single_reply = f"""
    From: {html_module.escape(email_from or '')}
    To: {html_module.escape(email_to or '')}
    CC: {html_module.escape(email_cc or '')}
    Subject: {html_module.escape(email_subject or '')}
    """
    if attachments:
        attachment_names = [html_module.escape(attachment.get("name") or "") for attachment in attachments]
        single_reply += f"Attachments: {attachment_names}\n"

    single_reply += f"\n{sanitize_html_body(html_body or '')}\n"

    return single_reply


def main(args):
    incident = demisto.incident()
    custom_fields = incident.get("CustomFields", {})
    email_from = custom_fields.get("emailfrom")
    email_cc = custom_fields.get("emailcc")
    email_to = custom_fields.get("emailto")
    email_subject = custom_fields.get("emailsubject")
    email_html = custom_fields.get("emailhtml", "")
    email_html_image = custom_fields.get("emailhtmlimage")
    attachments = incident.get("attachment", {})
    files = demisto.context().get("File", [])

    if not email_html_image or 'src="cid' in email_html_image:
        if 'src="cid' in email_html:
            entry_id_list = get_entry_id_list(attachments, files)
            html_body = create_email_html(email_html, entry_id_list)
            email_reply = set_email_reply(email_from, email_to, email_cc, email_subject, html_body, attachments)
            demisto.executeCommand("setIncident", {"customFields": {"emailhtmlimage": email_reply}})
            return_results(
                {
                    "ContentsFormat": formats["html"],
                    "Type": entryTypes["note"],
                    "Contents": email_reply,
                }
            )

        else:
            email_reply = set_email_reply(email_from, email_to, email_cc, email_subject, email_html, attachments)
            return_results({"ContentsFormat": formats["html"], "Type": entryTypes["note"], "Contents": email_reply})

    else:
        return_results(
            {
                "ContentsFormat": formats["html"],
                "Type": entryTypes["note"],
                "Contents": sanitize_html_body(email_html_image or ""),
            }
        )


if __name__ in ("__builtin__", "builtins", "__main__"):
    main(demisto.args())

README

Displays the original email in HTML format.

Script Data


Name Description
Script Type python3
Tags dynamic-section

Inputs


There are no inputs for this script.

Outputs


There are no outputs for this script.