When a function returns results to a workflow, there are several options on how to display and represent those results. Of course, it all depends on what specific results are returned such as mixed text, tabular data, links, etc. and how that data will be further acted upon. Below is summary of how to capture this data in an incident and the considerations when choosing one method over another.
The following code snippets would be used in a function's Post-Process Script within a workflow.
Function Results
Consider a payload returned from a SIEM for an IP address search. It may contain search results including summary information and additional data which may be used for further analysis. Depending on the data visibility needs and how the data will be later used, different data fields within an incident can be used. We'll focus on custom fields, notes, artifacts and data tables.
{ 'content': { 'success': 'True', 'Summary Score': 87, 'Console URL': 'https://siem.com?org=567', 'Report URL': 'https://siem.com/report/1234', 'Target IP': '12.34.56.78', 'Report Run': '2018-11-19 16:48:34', 'Related IPs': [ { 'IP': '12.34.56.79', 'Score': 56, 'Organization': 'able corp', 'Registration Date': '2018-05-01 16:04:00' }, { 'IP': '12.34.56.80', 'Score': 89, 'Organization': 'able corp', 'Registration Date': '2017-11-17 10:19:00' }, { 'IP': '12.34.56.81', 'Score': 95, 'Organization': 'alpha corp', 'Registration Date': '2018-11-01 00:04:00' } ] }}
Custom Fields
Custom fields are very useful for retaining links to external integration points. They can be placed anywhere within an incident's layout, including the summary section. When referencing a link, create a custom field of type Text Area in order to support Rich Text. In your function's post-process script, the python code should reference that field similar to this example. Note that custom fields are referenced via the incident.properties collection.
incident.properties.siem_link = helper.createRichText(u"<a href='{}' target='blank'>Link</a>".format(results['content']['Console URL']))
Notes
Notes are very useful for capturing unstructured data and larger blocks of data unsuitable for other data fields. Because they support rich text, additional data formatting is available. This example creates an incident note using the summary information from the function results above.
data = u"""Artifact IP address: {}<br>Score: {}<br>Report URL: <a href='{}'>Report</a>""".format(results['content']['Target IP'], results['content']['Summary Score'], results['content']['Report URL'])incident.addNote(helper.createRichText(data))
Artifacts
Artifacts have limited fields for data returned from functions. But it's possible to extend the description field with result data. This can be useful when specific function results should be retained with the original artifact.
summary = u"Score: {}\nReport date: {}".format(results['content']['Summary Score'], results['content']['Report Run'])if artifact.description: artifact.description = u"{}\n{}".format(artifact.description.content, summary)else: artifact.description = summaryIt's also possible to create additional artifacts. This example shows how to iterate over the function results.
for related in results['content']['Related IPs']: description = u"""Original IP address: {}Score: {}Organization: {}Registration Date: {}""".format(results['content']['Target IP'], related['Score'], related['Organization'], related['Registration Date']) incident.addArtifact('IP Address', related['IP'], description)
Data Tables
Finally, there will be times when function result data returned should be retained as a structured collection rather than separated or unstructured. Custom data tables become a useful option and can be displayed in the same tab as artifacts for quick reference. Data tables can also have rules associated with them for follow-on actions.
The following example adds data to a custom table 'IP Score' with the following columns:
| target_ip | date | ip_address | score | organization | reg_date |
from java.util import Datedt = str(Date())for related in results['content']['Related IPs']: row = incident.addRow('ip_score') row['target_ip'] = results['content']['Target IP'] row['date'] = dt row['ip_address'] = related['IP'] row['score'] = related['Score'] row['organization'] = related['Organization'] row['reg_date'] = related['Registration Date']Be aware that data tables are shared for all artifacts and rerunning a given function will duplicate data rows.
Summary
Here's a simple guide for using different fields for displaying function results.
| Object Type | Rich Text support | Actionable | Notes
-------------------------------------------------------------
| Custom Field | Yes | Yes | Good for links
| Notes | Yes | Yes | Good for summary, unstructured information
| Artifacts | Yes | Yes | Good for continued analysis
| Data Tables | Yes | Yes | Good for collections of structured data
------------------------------
Mark Scherfling
------------------------------