CreateEmailHtmlBody

This script allows sending an HTML email, using a template stored as a list item under Lists: - Cortex XSIAM: Settings -> Configurations -> Lists. - Cortex XSOAR: Settings -> Lists. Placeholders are marked in DT format (i.e. ${incident.id} for incident ID). Available placeholders for example: - ${incident.labels.Email/from} - ${incident.name} - ${object.value} See incident Context Data menu for available placeholders Note: Sending emails require an active Mail Sender integration instance.

javascript · Common Scripts

Source

/**
 * Creates an HTML email body by replacing placeholders with actual values
 * Supports different Cortex marketplaces: XSOAR, XSIAM, and unified platform
 */

// Constants
const MARKETPLACES = {
    XSOAR: 'xsoar',
    XSIAM: 'x2',
    PLATFORM: 'unified_platform'
};

const ENTITY_TYPES = {
    INCIDENT: 'incident',
    ALERT: 'alert',
    ISSUE: 'issue',
    OBJECT: 'object'
};

/**
 * Safe string replacement utility
 * @param {string} text - Original text
 * @param {string} search - Text to search for
 * @param {string} replacement - Replacement text
 * @returns {string} Text with all occurrences replaced
 */
function replaceAll(text, search, replacement) {
    if (!text || typeof text !== 'string') return text;
    return text.split(search).join(replacement || '');
}

/**
 * Get a specific label from incident/alert/issue labels
 * @param {Object} entity - The incident, alert, or issue object
 * @param {string} path - The label path (e.g., 'incident.labels.labelName')
 * @returns {string|null} Label value or null if not found
 */
function getLabel(entity, path) {
    try {
        const pathParts = path.split('.');
        const labelName = pathParts[2];

        for (let i = 0; i < entity.labels.length; i++) {
            if (entity.labels[i] && entity.labels[i].type === labelName) {
                return entity.labels[i].value || null;
            }
        }
    } catch (error) {
        logDebug("Error getting label: " + error.message);
    }
    return null;
}

/**
 * Get the appropriate entity type based on marketplace
 * @param {string} marketplace - Current marketplace
 * @returns {string} Entity type name
 */
function getEntityType(marketplace) {
    switch (marketplace) {
        case MARKETPLACES.XSIAM:
            return ENTITY_TYPES.ALERT;
        case MARKETPLACES.PLATFORM:
            return ENTITY_TYPES.ISSUE;
        default:
            return ENTITY_TYPES.INCIDENT;
    }
}

/**
 * Process field path and extract value
 * @param {string} path - Field path
 * @param {string} marketplace - Current marketplace
 * @param {Object} entity - Current entity (incident/alert/issue)
 * @param {Object} objectArg - Optional object argument
 * @returns {*} Field value or null
 */
function processFieldPath(path, marketplace, entity, objectArg) {
    try {
        const entityType = getEntityType(marketplace);
        
        // Handle labels
        if (path.indexOf(entityType + '.labels.') === 0) {
            logDebug("Field " + path + " is handled as label.");
            return getLabel(entity, path);
        }
        
        // Handle entity fields
        if (path.indexOf(entityType + '.') === 0) {
            const entityWrapper = {};
            entityWrapper[entityType] = entity;
            let value = dq(entityWrapper, path);
            
            // Try custom fields if direct field lookup fails
            if (value === null) {
                logDebug("Field " + path + " not found directly. Trying custom fields.");
                const customFieldPath = path.replace(entityType + '.', entityType + '.CustomFields.');
                value = dq(entityWrapper, customFieldPath);
            }
            return value;
        }
        
        // Handle object fields
        if (path.indexOf('object.') === 0) {
            logDebug("Field " + path + " is part of object.");
            if (!objectArg) {
                logDebug("No object provided for object field");
                return null;
            }
            
            const obj = (typeof objectArg === 'string') ? JSON.parse(objectArg) : objectArg;
            return dq({'object': obj}, path);
        }
        
        // Handle investigation context
        return dq(invContext, path);
        
    } catch (error) {
        logDebug("Error processing field path " + path + ": " + error.message);
        return null;
    }
}

// Main execution
try {    
    // Get template content
    const res = executeCommand("getList", {"listName": args.listTemplate});
    
    if (!res || !res[0]) {
        return {
            Type: entryTypes.error,
            Contents: "Failed to retrieve template list"
        };
    }
    
    if (res[0].Type === entryTypes.error) {
        return res;
    }
    
    // Get marketplace information
    const cortexMarketplacesDetail = getDemistoVersion().platform;
    logDebug("Marketplace: " + cortexMarketplacesDetail);
    
    // Find and process placeholders
    let html = res[0].Contents || '';
    const placeholderRegex = /\${(.+?)}/g;
    let fieldMap = {};
    let match;
    
    // Reset regex lastIndex to ensure we catch all matches
    placeholderRegex.lastIndex = 0;
    
    while ((match = placeholderRegex.exec(html)) !== null) {
        const path = match[1];
        if (!fieldMap.hasOwnProperty(path)) { // Avoid processing the same path multiple times
            fieldMap[path] = processFieldPath(path, cortexMarketplacesDetail, incidents[0], args.object);
            logDebug("Field value fetched for " + path + ": " + fieldMap[path]);
        }
    }
    
    // Replace placeholders with actual values
    for (const path in fieldMap) {
        const value = fieldMap[path];
        const placeholder = '${' + path + '}';
        
        if (value !== null && value !== undefined) {
            logDebug("Replacing placeholder " + placeholder + " with: " + value);
            html = replaceAll(html, placeholder, String(value));
        } else if (args.removeNotFound === 'yes') {
            logDebug("Removing unfound placeholder: " + placeholder);
            html = replaceAll(html, placeholder, '');
        } else {
            logDebug("Leaving placeholder unchanged: " + placeholder);
        }
    }
    
    // Set context and return result
    setContext(args.key, html);
    
    return {
        ContentsFormat: formats.json,
        Type: entryTypes.note,
        Contents: {htmlBody: html},
        HumanReadable: 'HTML body set to context key "' + args.key + '"'
    };
    
} catch (error) {
    logDebug("Script execution error: " + error.message);
    return {
        Type: entryTypes.error,
        Contents: "Script execution failed: " + error.message
    };
}

README

Sends a HTML email, using a template stored as a list item under Lists (Settings -> Advanced -> Lists).
Placeholders are marked in DT format. For example, ${incident.id} for the incident ID.

Examples of available placeholders:

  • ${incident.labels.Email/from}
  • ${incident.name}
  • ${object.value}
    See incident Context Data menu for available placeholders

Note: Sending emails requires an active Mail Sender integration instance.

Script Data


Name Description
Script Type javascript
Tags email, communication

Inputs


Argument Name Description
listTemplate The list where the template is stored.
key The context key to store the HTML body.
object The values object provided as stringified JSON.
removeNotFound Whether to replace a path not found, if true, with an empty string, otherwise to leave it as is.

Outputs


Path Description Type
htmlBody The HTML body. Unknown