FortiSOAR Knowledge Base
FortiSOAR: Security Orchestration and Response software provides innovative case management, automation, and orchestration. It pulls together all of an organization's tools, helps unify operations, and reduce alert fatigue, context switching, and the mean time to respond to incidents.
Andy_G
Staff
Staff
Article Id 195870
Description

Sometimes when working with Intelligence searches and enrichment the values returned in a field (as an array) contain multiple values. For instance when we look into HybridAnalysis.com for information regarding a hash, it will sometimes return multiple malware families. Its ugly to post just the results for this field into an Alert or other record type. So utilizing a FOR loop you can iterate through the results and put them into one field, if it is a rich text field you can also put it as a bulleted list. 


The example below will look within the classification_tags result from HybridAnalysis.com and then iterate through each one and put a comma after it. This is useful when posting to a text field, where its just one line. Be careful though text fields max at 255 characters, so know the data being returned. 

 

{% for tags in vars.loop_resource.classification_tags %} 
{{ tags }},  
{% endfor %}

 

For making a bulleted list:

  

{% for tags in vars.loop_resource.classification_tags %} 
<li> {{ tags }} </li>
{% endfor %}

For some types of results where you have multiple events or results in an array, where you want to pluck a single field from each result into a comma separated list, you can use the join filter in a one liner, instead of writing out a for loop.

{{vars.loop_resource|map(attribute='risk_score')|join(', ')}}


As mentioned before, watch out for that 255 character limit for all fields except Rich Text. This can be fixed using the truncate filter:

{{vars.loop_resource|map(attribute='risk_score')|join(', ')|truncate(255)}}

This will iterate through each result and pull the risk score then comma separate the values with a comma-space (, ).


This comes in handy when you'll want to potentially pull related events for an alert, but don't necessarily want to store all of the events in CyberSponse. You could pull 5K events from the SIEM related to an alert, then summarize several different key values such as SourceIP, DestinationIP, WebDomain, etc. To count a total of unique values and list out those unique values you can use the following for loop:


{{vars.loop_resource|groupby('sourceip')|list|length}}

{% for uniqvalues, list in vars.loop_resource|groupby('sourceip')|sort %}

{{uniqvalues}}

{% endfor %}


Another method to produce a nice comma separated list, while ignoring blank values and providing a unique list is to utilize map and rejectattr/selectattr. Like in the first example utilizing map, we can pick out values from an array of dict objects. One of the annoying properties of using map is if a user populates a field, then deletes it contents it becomes '' or "", and is no longer none. This creates ugly concatenated lists. Furthermore the for loop in the second example will not product nice comma separated lists as it will leave a trailing comma after the loop is over. Below is a method to exclude specific values or in this particular case none and blank fields:

 

{{vars.request.data.records|rejectattr('sourceip','none')|rejectattr('sourceip','eq','')|map(attribute="sourceip")|list|unique|join(', ')}}

 

Breaking this down we have the first rejectattr which looks for dict object containing destinationip that is none, none is a test condition to indicate none/null values in your dict object. The next rejectattr will filter out any dict object where destinationip == ''. You can change this to specific values and can also do ne, gt, lt and several others instead of eq, and can even do an "in" test for a "contains" type of match. Furthermore you can combine all kinds of conditions such as sourceip ne 10.10.4.1 then reject any dict object with destinationip eq 10.5.4.3. You can utilize selectattr to do the opposite and only grab dict objects with a value matching a test. This is extremely helpful when dealing with several input records in a playbook or dealing with multiple alerts/events/records being ingested into CyOps. Once you've done all of your filtering, you'll want to pull out just the specific fields you want. It might be helpful to set a variable to your filtered set and then in the next step you can easily access the filtered list variables you want without having to filter for every variable.


So we bring this all together with map. This allows us to grab all of the values from multiple dict objects and then adding |list|unique|join(', ') to the end will force it to be an array, rid duplicates and comma separate your result list for a nice looking result you can throw into a field or description. You can also utilize any other separator you'd like if you want a different separator for your data. If you leave it as a list (drop the join) you can use a for loop to loop through and add multiple bullets in a Rich Text field for every unique value in your array, like the original example.


Contributors