DisplayHTMLWithImages
Display HTML with embedded images.
- Type
- python
- Pack
- CommonScripts
Source
import re
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401
IMG_FORMATS = ["jpeg", "gif", "bmp", "png", "jfif", "tiff", "eps", "indd", "jpg"]
# Define allowed tags for email body rendering (preserve formatting, strip dangerous tags)
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",
"col",
"colgroup",
}
def is_image(file_name: str):
return file_name and file_name.split(".")[-1] in IMG_FORMATS
def create_html_with_images(email_html="", entry_id_list=None):
if not entry_id_list:
return email_html
account_name = get_tenant_account_name()
xsoar_prefix = "/xsoar" if is_xsiam_or_xsoar_saas() else ""
for file_name, attach_content_id, file_entry_id in entry_id_list:
# Escape user-controlled values before interpolating into regex patterns
safe_content_id = re.escape(attach_content_id)
safe_file_name = re.escape(file_name)
# Handling inline attachments from Gmail mailboxes
if re.search(f'src="[^>]+{safe_content_id}"(?=[^>]+alt="{safe_file_name}")', email_html):
email_html = re.sub(
f'src="[^>]+{safe_content_id}"(?=[^>]+alt="{safe_file_name}")',
f"src={account_name}{xsoar_prefix}/entry/download/{file_entry_id}",
email_html,
)
# Handling inline attachments from Outlook mailboxes
# Note: the format of an image src are like this src="cid:THE CONTENT ID"
else:
email_html = re.sub(
f'(src="cid(.*?{safe_content_id}.*?"))',
f"src={account_name}{xsoar_prefix}/entry/download/{file_entry_id}",
email_html,
count=1,
)
return email_html
# for backwards compatible
def get_entry_id_list_by_incident_attachments(attachments, files):
"""Get the incident attachments and create entry id list.
Args:
attachments (list): The incident attachments.
files (list): The uploaded files in the context.
Returns:
list. List ou tuples, in the format (file name, attachment id, file EntryID).
"""
if not (attachments and files):
return []
img_data_list = []
entry_ids = set()
for attachment in attachments:
attachment_name = attachment.get("name", "")
if is_image(attachment_name):
for file in files:
if attachment_name == file.get("Name") and file.get("EntryID") not in entry_ids:
entry_ids.add(file.get("EntryID"))
img_data_list.append((attachment_name, "", file.get("EntryID")))
break
demisto.info(f"\n\n idlist by incident attachments \n\n{img_data_list}")
return img_data_list
def get_entry_id_list_by_parsed_email_attachments(attachments, files):
"""Get the email attachments and create entry id list.
Args:
attachments (list): The parsed email attachments.
files (list): The uploaded files in the context.
Returns:
list. List ou tuples, in the format (file name, attachment id, file EntryID).
"""
if not (attachments and files):
return []
img_data_list = []
entry_ids = set()
for attachment in attachments:
attach_name = attachment.get("Name", "")
attach_id = (attachment.get("Content-ID") or "").replace("<", "").replace(">", "")
if is_image(attach_name) and attach_id:
for file in files:
# we use the entry_ids set to avoid taking the wrong file in case there is two different images with same name
if attach_name == file.get("Name") and file.get("EntryID") not in entry_ids:
entry_ids.add(file.get("EntryID"))
img_data_list.append((attach_name, attach_id, file.get("EntryID")))
break
demisto.info(f"\n\n idlist by parsed email attachments\n\n{img_data_list}")
return img_data_list
def _sanitize_html(html_body: str) -> str:
"""Sanitize HTML body using an allowlist of safe tags.
When bleach is available, strips disallowed tags while preserving safe ones.
When bleach is not available, uses a basic tag-stripping fallback to remove
potentially dangerous tags while preserving content.
"""
try:
import bleach # type: ignore[import-untyped]
html_body = bleach.clean(
html_body,
tags=ALLOWED_EMAIL_TAGS,
strip=True,
attributes={
"a": ["href", "title"],
"img": ["src", "alt", "width", "height"],
"td": ["style", "colspan", "rowspan"],
"th": ["style", "colspan", "rowspan"],
"div": ["style"],
"span": ["style"],
"p": ["style"],
"font": ["color", "size", "face"],
"table": ["style", "border", "cellpadding", "cellspacing"],
},
)
except ImportError:
demisto.debug("bleach is not available; using basic tag-stripping fallback")
# Remove script/iframe/object/embed tags and their content as a basic fallback
# Use [^>]*> for end tags to handle whitespace/attributes like </script > or </script\t\nbar>
html_body = re.sub(r"<\s*script[^>]*>.*?<\s*/\s*script[^>]*>", "", html_body, flags=re.DOTALL | re.IGNORECASE)
html_body = re.sub(r"<\s*iframe[^>]*>.*?<\s*/\s*iframe[^>]*>", "", html_body, flags=re.DOTALL | re.IGNORECASE)
html_body = re.sub(r"<\s*object[^>]*>.*?<\s*/\s*object[^>]*>", "", html_body, flags=re.DOTALL | re.IGNORECASE)
html_body = re.sub(r"<\s*embed[^>]*>.*?<\s*/\s*embed[^>]*>", "", html_body, flags=re.DOTALL | re.IGNORECASE)
# Also remove self-closing variants
html_body = re.sub(r"<\s*(?:script|iframe|object|embed)[^>]*/\s*>", "", html_body, flags=re.IGNORECASE)
return html_body
def main():
incident = demisto.incident()
html_body = demisto.get(incident, "CustomFields.emailhtml") or demisto.get(incident, "CustomFields.emailbody") or ""
if 'src="cid' in html_body:
context = demisto.context()
files = argToList(context.get("File", []))
attachments = argToList(demisto.get(context, "Email.AttachmentsData", []))
entry_id_list = get_entry_id_list_by_parsed_email_attachments(attachments, files)
if not entry_id_list:
# for backwards compatible
attachments = incident.get("attachment", {})
entry_id_list = get_entry_id_list_by_incident_attachments(attachments, files)
html_body = create_html_with_images(html_body, entry_id_list)
# Sanitize after image replacement so cid: references are resolved first
html_body = _sanitize_html(html_body)
html_body = f'<div style="background-color: white; color:black;"> {html_body} </div>\n'
return_results(
{
"ContentsFormat": formats["html"],
"Type": entryTypes["note"],
"Contents": html_body,
}
)
if __name__ in ("__builtin__", "builtins", "__main__"):
main()
README
Display HTML with embedded images.
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.