Add a formatting script to an indicator type using OOTB examples or by specifying your own the script input and output.
A formatting script has the following main functions:
Validating the input (for example, checking that the Top Level Domain (TLD) is valid).
Modifying how the indicator appears in Cortex such as the War Room, Reports, etc.
After indicators are extracted according to the defined regex, the defined regex finds the indicator value and the formatting script modifies the string value, so it can be used in Cortex XSIAM. For example the IP indicator uses the UnEscapeIPs
formatting script, which removes any defanged characters from an IP address, such as 127[.]0[.]0[.]1
to 127.0.0.1
. In the Alert War Room, you can click on the IP address to view the extracted IP address. This extracted indicator using the formatting script is added to the Threat Intel database.
To apply a formatting script to an indicator type: .
Go to
→ → → → .Select the indicator type, and click Edit.
Select the desired formatting script.
Formatting scripts must have the
indicator-format
tag applied to appear in the list.
Note
Formatting scripts for out-of-the-box indicator types are system level. This means that the formatting scripts for these indicator types are not configurable. To create a formatting script for an out-of-the-box indicator type, you need to disable the existing indicator type and create a new (custom) indicator type. If you configured a formatting script before this change and updated your content, this configuration will revert to content settings (empty).
Out-of-the-box Formatting Script Examples
In the Scripts page, there several out-of-the-box formatting scripts, including:
UnEscapeIPs
ExtractDomainAndFQDNFromUrlAndEmail
This script is used by the Domain indicator, extracts domains and FQDN from URLs and emails. It removes prefixes such as proofpoint or safelinks, removes escaped URLs, and extracts the FQDN, etc.
ExtractEmailV2
CLI Execution Examples
!UnEscapeIPs input=127.0.0[.]1
!UnEscapeIPs input=127.0.0[.]1,8.8.8[.]8
!UnEscapeIPs input=${contextdata.indicators}
(where the keycontextdata.indicators
in the context object, is an array)
Formatting Script Input
The formatting script requires a single input argument named input
that accepts a single indicator value or an array of indicator values. The input argument should be an array to accept multiple inputs and return an entry-result per input.
Argument | Description |
---|---|
| Accepts a string or array of strings representing the indicator value(s) to be formatted. Will be accessed within the script using In the script settings, the Is Array checkbox must be checked (see screenshot below).The script code must be able to handle a single indicator value (as string), multiple indicator values in CSV format (as string) and an array of single indicator values (array). |
Formatting Script Outputs
The indicators appear as a human readable format in Cortex XSIAM. The output should be an array of formatted indicators or an array of entry results (an entry result per indicator to be created). The entry result per input can be a JSON array to create multiple indicators. If the entry result is an empty string, it is ignored and no indicator is created.
Use the return_results
function to generate the script output. For more information, see https://xsoar.pan.dev/docs/integrations/code-conventions#return_results.
Output Code Examples
Single-value result:
results = CommandResults( outputs_prefix='VirusTotal.IP', outputs_key_field='Address', outputs={ 'Address': '8.8.8.8', 'ASN': 12345 } ) return_results(results)
Multiple-value results:
results = [ CommandResults( outputs_prefix='VirusTotal.IP', outputs_key_field='Address', outputs={ 'Address': '8.8.8.8', 'ASN': 12345 } ), CommandResults( outputs_prefix='VirusTotal.IP', outputs_key_field='Address', outputs={ 'Address': '1.1.1.1', 'ASN': 67890 } )] return_results(results)
Complete Formatting Script Example
In the following example, the RemoveEmpty script removes empty items, entries, or nodes from an array.
// pack version: 1.2.30 const EMPTY_TOKENS = argToList(args.empty_values); function toBoolean(value) { if (typeof(value) === 'string') { if (['yes', 'true'].indexOf(value.toLowerCase()) != -1) { return true; } else if (['no', 'false'].indexOf(value.toLowerCase()) != -1) { return false; } throw 'Argument does not contain a valid boolean-like value'; } return value ? true : false; } function isObject(o) { return o instanceof Object && !(o instanceof Array); } function isEmpty(v) { return (v === undefined) || (v === null) || (typeof(v) == 'string' && (!v || EMPTY_TOKENS.indexOf(v) !== -1)) || (Array.isArray(v) && v.filter(x => !isEmpty(x)).length === 0) || (isObject(v) && Object.keys(v).length === 0); } function removeEmptyProperties(obj) { Object.keys(obj).forEach(k => { var ov = obj[k]; if (isObject(ov)) { removeEmptyProperties(ov); } else if (Array.isArray(ov)) { ov.forEach(av => isObject(av) && removeEmptyProperties(av)); obj[k] = ov.filter(x => !isEmpty(x)); } if (isEmpty(ov)) { delete obj[k]; } }); } var vals = Array.isArray(args.value) ? args.value : [args.value]; if (toBoolean(args.remove_keys)) { vals.forEach(v => isObject(v) && removeEmptyProperties(v)); } return vals.filter(x => !isEmpty(x));